mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 11:52:28 +01:00
CLOUDSTACK-6998: GloboDNS, Integration with external DNS Provider
This is a feature to handle DNS entries by means of an external DNS Provider, such as Bind. These entries include DNS domains and reverse domains, VM records and reverse records. For a complete description, please refer to the design document available at https://cwiki.apache.org/confluence/display/CLOUDSTACK/Bind+and+PowerDNS+integration+by+Globo+DNSAPI For the discussion about this feature on the dev mailing list, please refer to http://markmail.org/thread/fvwf36hpxotiibka Summary: - new Network Service Provider called GloboDNS - new Network Element to manage network domains and VM records (entries) on an external API - new Network Resource to communicate with GloboDNS (open source) - new API command to add DNS server - new global option to determine if this provider should override VM entries on external DNS server - changes in UI to include GloboDNS in Providers list Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
7ada4ad50b
commit
233445ed68
@ -136,6 +136,8 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
|
||||
public static final Provider NuageVsp = new Provider("NuageVsp", false);
|
||||
public static final Provider NuageVspVpc = new Provider("NuageVspVpc", false);
|
||||
public static final Provider BrocadeVcs = new Provider("BrocadeVcs", false);
|
||||
// add GloboDns provider
|
||||
public static final Provider GloboDns = new Provider("GloboDns", true);
|
||||
|
||||
private final String name;
|
||||
private final boolean isExternal;
|
||||
|
||||
@ -47,6 +47,7 @@ public interface ExternalNetworkDeviceManager extends Manager {
|
||||
public static final NetworkDevice CiscoVnmc = new NetworkDevice("CiscoVnmc", Network.Provider.CiscoVnmc.getName());
|
||||
public static final NetworkDevice OpenDaylightController = new NetworkDevice("OpenDaylightController", Network.Provider.Opendaylight.getName());
|
||||
public static final NetworkDevice BrocadeVcs = new NetworkDevice("BrocadeVcs", Network.Provider.BrocadeVcs.getName());
|
||||
public static final NetworkDevice GloboDns = new NetworkDevice("GloboDns", Network.Provider.GloboDns.getName());
|
||||
|
||||
public NetworkDevice(String deviceName, String ntwkServiceprovider) {
|
||||
_name = deviceName;
|
||||
|
||||
@ -346,6 +346,11 @@
|
||||
<artifactId>cloud-plugin-api-solidfire-intg-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-plugin-network-globodns</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@ -763,3 +763,6 @@ createServiceInstance=1
|
||||
addOpenDaylightController=1
|
||||
deleteOpenDaylightController=1
|
||||
listOpenDaylightControllers=1
|
||||
|
||||
### GloboDNS commands
|
||||
addGloboDnsHost=1
|
||||
|
||||
37
plugins/network-elements/globodns/pom.xml
Normal file
37
plugins/network-elements/globodns/pom.xml
Normal file
@ -0,0 +1,37 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cloud-plugin-network-globodns</artifactId>
|
||||
<name>Apache CloudStack Plugin - GloboDNS</name>
|
||||
<parent>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloudstack-plugins</artifactId>
|
||||
<version>4.5.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.globo.globodns</groupId>
|
||||
<artifactId>globodns-client</artifactId>
|
||||
<version>0.0.15</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,18 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
name=globodns
|
||||
parent=network
|
||||
@ -0,0 +1,22 @@
|
||||
<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
license agreements. See the NOTICE file distributed with this work for additional
|
||||
information regarding copyright ownership. The ASF licenses this file to
|
||||
you under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of
|
||||
the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
|
||||
by applicable law or agreed to in writing, software distributed under the
|
||||
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
|
||||
OF ANY KIND, either express or implied. See the License for the specific
|
||||
language governing permissions and limitations under the License. -->
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
||||
|
||||
<context:component-scan base-package="com.globo.globodns.cloudstack" />
|
||||
|
||||
</beans>
|
||||
@ -0,0 +1,124 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.globo.globodns.cloudstack.api;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.PhysicalNetworkResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.globo.globodns.cloudstack.element.GloboDnsElementService;
|
||||
|
||||
@APICommand(name = "addGloboDnsHost", responseObject = SuccessResponse.class, description = "Adds the GloboDNS external host", since="4.5.0")
|
||||
public class AddGloboDnsHostCmd extends BaseAsyncCmd {
|
||||
|
||||
private static final String s_name = "addglobodnshostresponse";
|
||||
@Inject
|
||||
GloboDnsElementService _globoDnsElementService;
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
// ////////////// API parameters /////////////////////
|
||||
// ///////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, description = "the Physical Network ID")
|
||||
private Long physicalNetworkId;
|
||||
|
||||
@Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Username for GloboDNS")
|
||||
private String username;
|
||||
|
||||
@Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "Password for GloboDNS")
|
||||
private String password;
|
||||
|
||||
@Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "GloboDNS url")
|
||||
private String url;
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
// ///////////////// Accessors ///////////////////////
|
||||
// ///////////////////////////////////////////////////
|
||||
|
||||
public Long getPhysicalNetworkId() {
|
||||
return physicalNetworkId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
// ///////////// API Implementation///////////////////
|
||||
// ///////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException {
|
||||
try {
|
||||
Host host = _globoDnsElementService.addGloboDnsHost(physicalNetworkId, username, password, url);
|
||||
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
response.setSuccess((host == null ? false : true));
|
||||
this.setResponseObject(response);
|
||||
|
||||
} catch (InvalidParameterValueException invalidParamExcp) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage());
|
||||
} catch (CloudRuntimeException runtimeExcp) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return s_name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccountId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
//EventTypes.EVENT_NETWORK_CREATE
|
||||
return EventTypes.EVENT_NETWORK_CREATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Add GloboDNS provider";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.commands;
|
||||
|
||||
import com.cloud.agent.api.Command;
|
||||
|
||||
public class CreateOrUpdateDomainCommand extends Command {
|
||||
|
||||
private String domainName;
|
||||
|
||||
private Long templateId;
|
||||
|
||||
public CreateOrUpdateDomainCommand(String domainName, Long templateId) {
|
||||
this.domainName = domainName;
|
||||
this.templateId = templateId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getDomainName() {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
public Long getTemplateId() {
|
||||
return templateId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.commands;
|
||||
|
||||
import com.cloud.agent.api.Command;
|
||||
|
||||
public class CreateOrUpdateRecordAndReverseCommand extends Command {
|
||||
|
||||
private String recordName;
|
||||
|
||||
private String recordIp;
|
||||
|
||||
private String networkDomain;
|
||||
|
||||
private Long reverseTemplateId;
|
||||
|
||||
private boolean override;
|
||||
|
||||
public CreateOrUpdateRecordAndReverseCommand(String recordName, String recordIp, String networkDomain, Long reverseTemplateId, boolean override) {
|
||||
this.recordName = recordName;
|
||||
this.recordIp = recordIp;
|
||||
this.networkDomain = networkDomain;
|
||||
this.reverseTemplateId = reverseTemplateId;
|
||||
this.override = override;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getRecordName() {
|
||||
return this.recordName;
|
||||
}
|
||||
|
||||
public String getRecordIp() {
|
||||
return this.recordIp;
|
||||
}
|
||||
|
||||
public String getNetworkDomain() {
|
||||
return this.networkDomain;
|
||||
}
|
||||
|
||||
public Long getReverseTemplateId() {
|
||||
return reverseTemplateId;
|
||||
}
|
||||
|
||||
public boolean isOverride() {
|
||||
return override;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.commands;
|
||||
|
||||
import com.cloud.agent.api.Command;
|
||||
|
||||
public class RemoveDomainCommand extends Command {
|
||||
|
||||
private String networkDomain;
|
||||
|
||||
private boolean override;
|
||||
|
||||
public RemoveDomainCommand(String networkDomain, boolean override) {
|
||||
this.networkDomain = networkDomain;
|
||||
this.override = override;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getNetworkDomain() {
|
||||
return this.networkDomain;
|
||||
}
|
||||
|
||||
public boolean isOverride() {
|
||||
return override;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.commands;
|
||||
|
||||
import com.cloud.agent.api.Command;
|
||||
|
||||
public class RemoveRecordCommand extends Command {
|
||||
|
||||
private String recordName;
|
||||
|
||||
private String recordIp;
|
||||
|
||||
private String networkDomain;
|
||||
|
||||
private boolean override;
|
||||
|
||||
public RemoveRecordCommand(String recordName, String recordIp, String networkDomain, boolean override) {
|
||||
this.recordName = recordName;
|
||||
this.recordIp = recordIp;
|
||||
this.networkDomain = networkDomain;
|
||||
this.override = override;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getRecordName() {
|
||||
return recordName;
|
||||
}
|
||||
|
||||
public String getRecordIp() {
|
||||
return recordIp;
|
||||
}
|
||||
|
||||
public String getNetworkDomain() {
|
||||
return networkDomain;
|
||||
}
|
||||
|
||||
public boolean isOverride() {
|
||||
return override;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.commands;
|
||||
|
||||
import com.cloud.agent.api.Command;
|
||||
|
||||
public class SignInCommand extends Command {
|
||||
|
||||
private String email;
|
||||
|
||||
private String password;
|
||||
|
||||
public SignInCommand(String email, String password) {
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return this.email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,388 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com.globo.globodns.cloudstack.element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.StartupCommand;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.deploy.DeployDestination;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.Host.Type;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.Network.Capability;
|
||||
import com.cloud.network.Network.Provider;
|
||||
import com.cloud.network.Network.Service;
|
||||
import com.cloud.network.PhysicalNetwork;
|
||||
import com.cloud.network.PhysicalNetworkServiceProvider;
|
||||
import com.cloud.network.dao.PhysicalNetworkDao;
|
||||
import com.cloud.network.element.NetworkElement;
|
||||
import com.cloud.offering.NetworkOffering;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.resource.ResourceStateAdapter;
|
||||
import com.cloud.resource.ServerResource;
|
||||
import com.cloud.resource.UnableDeleteHostException;
|
||||
import com.cloud.utils.component.AdapterBase;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallbackWithException;
|
||||
import com.cloud.utils.db.TransactionStatus;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.NicProfile;
|
||||
import com.cloud.vm.ReservationContext;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachineProfile;
|
||||
import com.globo.globodns.cloudstack.api.AddGloboDnsHostCmd;
|
||||
import com.globo.globodns.cloudstack.commands.CreateOrUpdateDomainCommand;
|
||||
import com.globo.globodns.cloudstack.commands.CreateOrUpdateRecordAndReverseCommand;
|
||||
import com.globo.globodns.cloudstack.commands.RemoveDomainCommand;
|
||||
import com.globo.globodns.cloudstack.commands.RemoveRecordCommand;
|
||||
import com.globo.globodns.cloudstack.commands.SignInCommand;
|
||||
import com.globo.globodns.cloudstack.resource.GloboDnsResource;
|
||||
|
||||
@Component
|
||||
@Local(NetworkElement.class)
|
||||
public class GloboDnsElement extends AdapterBase implements ResourceStateAdapter, NetworkElement, GloboDnsElementService, Configurable {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(GloboDnsElement.class);
|
||||
|
||||
private static final Map<Service, Map<Capability, String>> capabilities = setCapabilities();
|
||||
|
||||
private static final ConfigKey<Long> GloboDNSTemplateId = new ConfigKey<Long>("Advanced", Long.class, "globodns.domain.templateid", "1",
|
||||
"Template id to be used when creating domains in GloboDNS", true, ConfigKey.Scope.Global);
|
||||
private static final ConfigKey<Boolean> GloboDNSOverride = new ConfigKey<Boolean>("Advanced", Boolean.class, "globodns.override.entries", "true",
|
||||
"Allow GloboDns to override entries that already exist", true, ConfigKey.Scope.Global);
|
||||
|
||||
// DAOs
|
||||
@Inject
|
||||
DataCenterDao _dcDao;
|
||||
@Inject
|
||||
HostDao _hostDao;
|
||||
@Inject
|
||||
PhysicalNetworkDao _physicalNetworkDao;
|
||||
|
||||
// Managers
|
||||
@Inject
|
||||
AgentManager _agentMgr;
|
||||
@Inject
|
||||
ResourceManager _resourceMgr;
|
||||
|
||||
protected boolean isTypeSupported(VirtualMachine.Type type) {
|
||||
return type == VirtualMachine.Type.User || type == VirtualMachine.Type.ConsoleProxy || type == VirtualMachine.Type.DomainRouter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
public boolean implement(final Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException,
|
||||
ResourceUnavailableException, InsufficientCapacityException {
|
||||
|
||||
Long zoneId = network.getDataCenterId();
|
||||
DataCenter zone = _dcDao.findById(zoneId);
|
||||
if (zone == null) {
|
||||
throw new CloudRuntimeException("Could not find zone associated to this network");
|
||||
}
|
||||
CreateOrUpdateDomainCommand cmd = new CreateOrUpdateDomainCommand(network.getNetworkDomain(), GloboDNSTemplateId.value());
|
||||
callCommand(cmd, zoneId);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected String hostNameOfVirtualMachine(VirtualMachineProfile vm) {
|
||||
return vm.getHostName().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
public boolean prepare(final Network network, final NicProfile nic, final VirtualMachineProfile vm, DeployDestination dest, ReservationContext context)
|
||||
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
|
||||
|
||||
if (!isTypeSupported(vm.getType())) {
|
||||
s_logger.info("GloboDNS only manages records for VMs of type User, ConsoleProxy and DomainRouter. VM " + vm + " is " + vm.getType());
|
||||
return false;
|
||||
}
|
||||
|
||||
Long zoneId = network.getDataCenterId();
|
||||
final DataCenter zone = _dcDao.findById(zoneId);
|
||||
if (zone == null) {
|
||||
throw new CloudRuntimeException("Could not find zone associated to this network");
|
||||
}
|
||||
|
||||
/* Create new A record in GloboDNS */
|
||||
// We allow only lower case names in DNS, so force lower case names for VMs
|
||||
String vmName = vm.getHostName();
|
||||
String vmHostname = hostNameOfVirtualMachine(vm);
|
||||
if (!vmName.equals(vmHostname) && vm.getType() == VirtualMachine.Type.User) {
|
||||
throw new InvalidParameterValueException("VM name should contain only lower case letters and digits: " + vmName + " - " + vm);
|
||||
}
|
||||
|
||||
CreateOrUpdateRecordAndReverseCommand cmd = new CreateOrUpdateRecordAndReverseCommand(vmHostname, nic.getIp4Address(), network.getNetworkDomain(),
|
||||
GloboDNSTemplateId.value(), GloboDNSOverride.value());
|
||||
callCommand(cmd, zoneId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
public boolean release(final Network network, NicProfile nic, final VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException,
|
||||
ResourceUnavailableException {
|
||||
|
||||
if (!isTypeSupported(vm.getType())) {
|
||||
s_logger.info("GloboDNS only manages records for VMs of type User, ConsoleProxy and DomainRouter. VM " + vm + " is " + vm.getType());
|
||||
return false;
|
||||
}
|
||||
|
||||
Long zoneId = network.getDataCenterId();
|
||||
final DataCenter zone = _dcDao.findById(zoneId);
|
||||
if (zone == null) {
|
||||
throw new CloudRuntimeException("Could not find zone associated to this network");
|
||||
}
|
||||
|
||||
RemoveRecordCommand cmd = new RemoveRecordCommand(hostNameOfVirtualMachine(vm), nic.getIp4Address(), network.getNetworkDomain(), GloboDNSOverride.value());
|
||||
callCommand(cmd, zoneId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
public boolean destroy(final Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
|
||||
Long zoneId = network.getDataCenterId();
|
||||
final DataCenter zone = _dcDao.findById(zoneId);
|
||||
if (zone == null) {
|
||||
throw new CloudRuntimeException("Could not find zone associated to this network");
|
||||
}
|
||||
|
||||
RemoveDomainCommand cmd = new RemoveDomainCommand(network.getNetworkDomain(), GloboDNSOverride.value());
|
||||
callCommand(cmd, zoneId);
|
||||
return true;
|
||||
}
|
||||
|
||||
///////// Provider control methods ////////////
|
||||
private Answer callCommand(Command cmd, Long zoneId) {
|
||||
|
||||
HostVO globoDnsHost = getGloboDnsHost(zoneId);
|
||||
if (globoDnsHost == null) {
|
||||
throw new CloudRuntimeException("Could not find the GloboDNS resource");
|
||||
}
|
||||
|
||||
Answer answer = _agentMgr.easySend(globoDnsHost.getId(), cmd);
|
||||
if (answer == null || !answer.getResult()) {
|
||||
String msg = "Error executing command " + cmd;
|
||||
msg = answer == null ? msg : answer.getDetails();
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Service, Map<Capability, String>> getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
private static Map<Service, Map<Capability, String>> setCapabilities() {
|
||||
Map<Service, Map<Capability, String>> caps = new HashMap<Service, Map<Capability, String>>();
|
||||
Map<Capability, String> dnsCapabilities = new HashMap<Capability, String>();
|
||||
// FIXME
|
||||
dnsCapabilities.put(Capability.AllowDnsSuffixModification, "true");
|
||||
caps.put(Service.Dns, dnsCapabilities);
|
||||
return caps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider getProvider() {
|
||||
return Provider.GloboDns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady(PhysicalNetworkServiceProvider provider) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
|
||||
PhysicalNetwork pNtwk = _physicalNetworkDao.findById(provider.getPhysicalNetworkId());
|
||||
Host host = getGloboDnsHost(pNtwk.getDataCenterId());
|
||||
if (host != null) {
|
||||
_resourceMgr.deleteHost(host.getId(), true, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnableIndividualServices() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyServicesCombination(Set<Service> services) {
|
||||
return true;
|
||||
}
|
||||
|
||||
////// Configurable methods /////////////
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
return GloboDnsElement.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[] {GloboDNSTemplateId, GloboDNSOverride};
|
||||
}
|
||||
|
||||
////////// Resource/Host methods ////////////
|
||||
@Override
|
||||
public List<Class<?>> getCommands() {
|
||||
List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
||||
cmdList.add(AddGloboDnsHostCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map<String, String> details, List<String> hostTags) {
|
||||
if (!(startup[0] instanceof StartupCommand && resource instanceof GloboDnsResource)) {
|
||||
return null;
|
||||
}
|
||||
host.setType(Host.Type.L2Networking);
|
||||
return host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException {
|
||||
if (!(host.getType() == Host.Type.L2Networking)) {
|
||||
return null;
|
||||
}
|
||||
return new DeleteHostAnswer(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||
super.configure(name, params);
|
||||
_resourceMgr.registerResourceStateAdapter(name, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
private HostVO getGloboDnsHost(Long zoneId) {
|
||||
return _hostDao.findByTypeNameAndZoneId(zoneId, Provider.GloboDns.getName(), Type.L2Networking);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
public Host addGloboDnsHost(Long physicalNetworkId, final String username, final String password, String url) {
|
||||
|
||||
if (username == null || username.trim().isEmpty()) {
|
||||
throw new InvalidParameterValueException("Invalid username: " + username);
|
||||
}
|
||||
|
||||
if (password == null || password.trim().isEmpty()) {
|
||||
throw new InvalidParameterValueException("Invalid password: " + password);
|
||||
}
|
||||
|
||||
if (url == null || url.trim().isEmpty()) {
|
||||
throw new InvalidParameterValueException("Invalid url: " + url);
|
||||
}
|
||||
|
||||
// validate physical network and zone
|
||||
// Check if physical network exists
|
||||
PhysicalNetwork pNtwk = null;
|
||||
if (physicalNetworkId != null) {
|
||||
pNtwk = _physicalNetworkDao.findById(physicalNetworkId);
|
||||
if (pNtwk == null) {
|
||||
throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id");
|
||||
}
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Invalid physicalNetworkId: " + physicalNetworkId);
|
||||
}
|
||||
|
||||
final Long zoneId = pNtwk.getDataCenterId();
|
||||
|
||||
final Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("guid", "globodns-" + String.valueOf(zoneId));
|
||||
params.put("zoneId", String.valueOf(zoneId));
|
||||
params.put("name", Provider.GloboDns.getName());
|
||||
|
||||
params.put("url", url);
|
||||
params.put("username", username);
|
||||
params.put("password", password);
|
||||
|
||||
final Map<String, Object> hostDetails = new HashMap<String, Object>();
|
||||
hostDetails.putAll(params);
|
||||
|
||||
Host host = Transaction.execute(new TransactionCallbackWithException<Host, CloudRuntimeException>() {
|
||||
|
||||
@Override
|
||||
public Host doInTransaction(TransactionStatus status) throws CloudRuntimeException {
|
||||
try {
|
||||
GloboDnsResource resource = new GloboDnsResource();
|
||||
resource.configure(Provider.GloboDns.getName(), hostDetails);
|
||||
|
||||
Host host = _resourceMgr.addHost(zoneId, resource, resource.getType(), params);
|
||||
|
||||
if (host == null) {
|
||||
throw new CloudRuntimeException("Failed to add GloboDNS host");
|
||||
}
|
||||
|
||||
// Validate username and password by logging in
|
||||
SignInCommand cmd = new SignInCommand(username, password);
|
||||
Answer answer = callCommand(cmd, zoneId);
|
||||
if (answer == null || !answer.getResult()) {
|
||||
// Could not sign in on GloboDNS
|
||||
throw new ConfigurationException("Could not sign in on GloboDNS. Please verify URL, username and password.");
|
||||
}
|
||||
|
||||
return host;
|
||||
} catch (ConfigurationException e) {
|
||||
throw new CloudRuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return host;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package com.globo.globodns.cloudstack.element;
|
||||
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
|
||||
public interface GloboDnsElementService extends PluggableService {
|
||||
|
||||
public Host addGloboDnsHost(Long pNtwkId, String username, String password, String url);
|
||||
}
|
||||
@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.resource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.IAgentControl;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.MaintainAnswer;
|
||||
import com.cloud.agent.api.MaintainCommand;
|
||||
import com.cloud.agent.api.PingCommand;
|
||||
import com.cloud.agent.api.ReadyAnswer;
|
||||
import com.cloud.agent.api.ReadyCommand;
|
||||
import com.cloud.agent.api.StartupCommand;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.Host.Type;
|
||||
import com.cloud.resource.ServerResource;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.globo.globodns.client.GloboDns;
|
||||
import com.globo.globodns.client.GloboDnsException;
|
||||
import com.globo.globodns.client.model.Authentication;
|
||||
import com.globo.globodns.client.model.Domain;
|
||||
import com.globo.globodns.client.model.Export;
|
||||
import com.globo.globodns.client.model.Record;
|
||||
import com.globo.globodns.cloudstack.commands.CreateOrUpdateDomainCommand;
|
||||
import com.globo.globodns.cloudstack.commands.CreateOrUpdateRecordAndReverseCommand;
|
||||
import com.globo.globodns.cloudstack.commands.RemoveDomainCommand;
|
||||
import com.globo.globodns.cloudstack.commands.RemoveRecordCommand;
|
||||
import com.globo.globodns.cloudstack.commands.SignInCommand;
|
||||
|
||||
public class GloboDnsResource extends ManagerBase implements ServerResource {
|
||||
private String _zoneId;
|
||||
|
||||
private String _guid;
|
||||
|
||||
private String _name;
|
||||
|
||||
private String _username;
|
||||
|
||||
private String _url;
|
||||
|
||||
private String _password;
|
||||
|
||||
protected GloboDns _globoDns;
|
||||
|
||||
private static final String IPV4_RECORD_TYPE = "A";
|
||||
private static final String REVERSE_RECORD_TYPE = "PTR";
|
||||
private static final String REVERSE_DOMAIN_SUFFIX = "in-addr.arpa";
|
||||
private static final String DEFAULT_AUTHORITY_TYPE = "M";
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(GloboDnsResource.class);
|
||||
|
||||
@Override
|
||||
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||
|
||||
_zoneId = (String)params.get("zoneId");
|
||||
if (_zoneId == null) {
|
||||
throw new ConfigurationException("Unable to find zone");
|
||||
}
|
||||
|
||||
_guid = (String)params.get("guid");
|
||||
if (_guid == null) {
|
||||
throw new ConfigurationException("Unable to find guid");
|
||||
}
|
||||
|
||||
_name = (String)params.get("name");
|
||||
if (_name == null) {
|
||||
throw new ConfigurationException("Unable to find name");
|
||||
}
|
||||
|
||||
_url = (String)params.get("url");
|
||||
if (_url == null) {
|
||||
throw new ConfigurationException("Unable to find url");
|
||||
}
|
||||
|
||||
_username = (String)params.get("username");
|
||||
if (_username == null) {
|
||||
throw new ConfigurationException("Unable to find username");
|
||||
}
|
||||
|
||||
_password = (String)params.get("password");
|
||||
if (_password == null) {
|
||||
throw new ConfigurationException("Unable to find password");
|
||||
}
|
||||
|
||||
_globoDns = GloboDns.buildHttpApi(_url, _username, _password);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stop() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Host.Type.L2Networking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StartupCommand[] initialize() {
|
||||
s_logger.trace("initialize called");
|
||||
StartupCommand cmd = new StartupCommand(getType());
|
||||
cmd.setName(_name);
|
||||
cmd.setGuid(_guid);
|
||||
cmd.setDataCenter(_zoneId);
|
||||
cmd.setPod("");
|
||||
cmd.setPrivateIpAddress("");
|
||||
cmd.setStorageIpAddress("");
|
||||
cmd.setVersion(GloboDnsResource.class.getPackage().getImplementationVersion());
|
||||
return new StartupCommand[] {cmd};
|
||||
}
|
||||
|
||||
@Override
|
||||
public PingCommand getCurrentStatus(long id) {
|
||||
return new PingCommand(getType(), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected() {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAgentControl getAgentControl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAgentControl(IAgentControl agentControl) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Answer executeRequest(Command cmd) {
|
||||
if (cmd instanceof ReadyCommand) {
|
||||
return new ReadyAnswer((ReadyCommand)cmd);
|
||||
} else if (cmd instanceof MaintainCommand) {
|
||||
return new MaintainAnswer((MaintainCommand)cmd);
|
||||
} else if (cmd instanceof SignInCommand) {
|
||||
return execute((SignInCommand)cmd);
|
||||
} else if (cmd instanceof RemoveDomainCommand) {
|
||||
return execute((RemoveDomainCommand)cmd);
|
||||
} else if (cmd instanceof RemoveRecordCommand) {
|
||||
return execute((RemoveRecordCommand)cmd);
|
||||
} else if (cmd instanceof CreateOrUpdateDomainCommand) {
|
||||
return execute((CreateOrUpdateDomainCommand)cmd);
|
||||
} else if (cmd instanceof CreateOrUpdateRecordAndReverseCommand) {
|
||||
return execute((CreateOrUpdateRecordAndReverseCommand)cmd);
|
||||
}
|
||||
return Answer.createUnsupportedCommandAnswer(cmd);
|
||||
}
|
||||
|
||||
public Answer execute(SignInCommand cmd) {
|
||||
try {
|
||||
Authentication auth = _globoDns.getAuthAPI().signIn(cmd.getEmail(), cmd.getPassword());
|
||||
if (auth != null) {
|
||||
return new Answer(cmd, true, "Signed in successfully");
|
||||
} else {
|
||||
return new Answer(cmd, false, "Unable to sign in on GloboDNS");
|
||||
}
|
||||
} catch (GloboDnsException e) {
|
||||
return new Answer(cmd, false, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public Answer execute(RemoveDomainCommand cmd) {
|
||||
try {
|
||||
Domain domain = searchDomain(cmd.getNetworkDomain(), false);
|
||||
if (domain != null) {
|
||||
if (!cmd.isOverride()) {
|
||||
for (Record record : _globoDns.getRecordAPI().listAll(domain.getId())) {
|
||||
if (record.getTypeNSRecordAttributes().getId() == null) {
|
||||
s_logger.warn("There are records in domain " + cmd.getNetworkDomain() + " and override is not enable. I will not delete this domain.");
|
||||
return new Answer(cmd, true, "Domain keeped");
|
||||
}
|
||||
}
|
||||
}
|
||||
_globoDns.getDomainAPI().removeDomain(domain.getId());
|
||||
scheduleExportChangesToBind();
|
||||
} else {
|
||||
s_logger.warn("Domain " + cmd.getNetworkDomain() + " already been deleted.");
|
||||
}
|
||||
|
||||
return new Answer(cmd, true, "Domain removed");
|
||||
} catch (GloboDnsException e) {
|
||||
return new Answer(cmd, false, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public Answer execute(RemoveRecordCommand cmd) {
|
||||
boolean needsExport = false;
|
||||
try {
|
||||
if (removeRecord(cmd.getRecordName(), cmd.getRecordIp(), cmd.getNetworkDomain(), false, cmd.isOverride())) {
|
||||
needsExport = true;
|
||||
}
|
||||
|
||||
// remove reverse
|
||||
String reverseGloboDnsName = generateReverseDomainNameFromNetworkIp(cmd.getRecordIp());
|
||||
String reverseRecordName = generateReverseRecordNameFromNetworkIp(cmd.getRecordIp());
|
||||
String reverseRecordContent = cmd.getRecordName() + '.' + cmd.getNetworkDomain();
|
||||
|
||||
if (removeRecord(reverseRecordName, reverseRecordContent, reverseGloboDnsName, true, cmd.isOverride())) {
|
||||
needsExport = true;
|
||||
}
|
||||
|
||||
return new Answer(cmd, true, "Record removed");
|
||||
} catch (GloboDnsException e) {
|
||||
return new Answer(cmd, false, e.getMessage());
|
||||
} finally {
|
||||
if (needsExport) {
|
||||
scheduleExportChangesToBind();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Answer execute(CreateOrUpdateRecordAndReverseCommand cmd) {
|
||||
boolean needsExport = false;
|
||||
try {
|
||||
Domain domain = searchDomain(cmd.getNetworkDomain(), false);
|
||||
if (domain == null) {
|
||||
domain = _globoDns.getDomainAPI().createDomain(cmd.getNetworkDomain(), cmd.getReverseTemplateId(), DEFAULT_AUTHORITY_TYPE);
|
||||
s_logger.warn("Domain " + cmd.getNetworkDomain() + " doesn't exist, maybe someone removed it. It was automatically created with template "
|
||||
+ cmd.getReverseTemplateId());
|
||||
}
|
||||
|
||||
boolean created = createOrUpdateRecord(domain.getId(), cmd.getRecordName(), cmd.getRecordIp(), IPV4_RECORD_TYPE, cmd.isOverride());
|
||||
if (!created) {
|
||||
String msg = "Unable to create record " + cmd.getRecordName() + " at " + cmd.getNetworkDomain();
|
||||
if (!cmd.isOverride()) {
|
||||
msg += ". Override record option is false, maybe record already exist.";
|
||||
}
|
||||
return new Answer(cmd, false, msg);
|
||||
} else {
|
||||
needsExport = true;
|
||||
}
|
||||
|
||||
String reverseRecordContent = cmd.getRecordName() + '.' + cmd.getNetworkDomain();
|
||||
if (createOrUpdateReverse(cmd.getRecordIp(), reverseRecordContent, cmd.getReverseTemplateId(), cmd.isOverride())) {
|
||||
needsExport = true;
|
||||
} else {
|
||||
if (!cmd.isOverride()) {
|
||||
String msg = "Unable to create reverse record " + cmd.getRecordName() + " for ip " + cmd.getRecordIp();
|
||||
msg += ". Override record option is false, maybe record already exist.";
|
||||
return new Answer(cmd, false, msg);
|
||||
}
|
||||
}
|
||||
|
||||
return new Answer(cmd);
|
||||
} catch (GloboDnsException e) {
|
||||
return new Answer(cmd, false, e.getMessage());
|
||||
} finally {
|
||||
if (needsExport) {
|
||||
scheduleExportChangesToBind();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean createOrUpdateReverse(String networkIp, String reverseRecordContent, Long templateId, boolean override) {
|
||||
String reverseDomainName = generateReverseDomainNameFromNetworkIp(networkIp);
|
||||
Domain reverseDomain = searchDomain(reverseDomainName, true);
|
||||
if (reverseDomain == null) {
|
||||
reverseDomain = _globoDns.getDomainAPI().createReverseDomain(reverseDomainName, templateId, DEFAULT_AUTHORITY_TYPE);
|
||||
s_logger.info("Created reverse domain " + reverseDomainName + " with template " + templateId);
|
||||
}
|
||||
|
||||
// create reverse
|
||||
String reverseRecordName = generateReverseRecordNameFromNetworkIp(networkIp);
|
||||
return createOrUpdateRecord(reverseDomain.getId(), reverseRecordName, reverseRecordContent, REVERSE_RECORD_TYPE, override);
|
||||
}
|
||||
|
||||
public Answer execute(CreateOrUpdateDomainCommand cmd) {
|
||||
|
||||
boolean needsExport = false;
|
||||
try {
|
||||
Domain domain = searchDomain(cmd.getDomainName(), false);
|
||||
if (domain == null) {
|
||||
// create
|
||||
domain = _globoDns.getDomainAPI().createDomain(cmd.getDomainName(), cmd.getTemplateId(), DEFAULT_AUTHORITY_TYPE);
|
||||
s_logger.info("Created domain " + cmd.getDomainName() + " with template " + cmd.getTemplateId());
|
||||
if (domain == null) {
|
||||
return new Answer(cmd, false, "Unable to create domain " + cmd.getDomainName());
|
||||
} else {
|
||||
needsExport = true;
|
||||
}
|
||||
} else {
|
||||
s_logger.warn("Domain " + cmd.getDomainName() + " already exist.");
|
||||
}
|
||||
return new Answer(cmd);
|
||||
} catch (GloboDnsException e) {
|
||||
return new Answer(cmd, false, e.getMessage());
|
||||
} finally {
|
||||
if (needsExport) {
|
||||
scheduleExportChangesToBind();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to remove a record from bindZoneName. If record was removed returns true.
|
||||
* @param recordName
|
||||
* @param bindZoneName
|
||||
* @return true if record exists and was removed.
|
||||
*/
|
||||
protected boolean removeRecord(String recordName, String recordValue, String bindZoneName, boolean reverse, boolean override) {
|
||||
Domain domain = searchDomain(bindZoneName, reverse);
|
||||
if (domain == null) {
|
||||
s_logger.warn("Domain " + bindZoneName + " doesn't exists in GloboDNS. Record " + recordName + " has already been removed.");
|
||||
return false;
|
||||
}
|
||||
Record record = searchRecord(recordName, domain.getId());
|
||||
if (record == null) {
|
||||
s_logger.warn("Record " + recordName + " in domain " + bindZoneName + " has already been removed.");
|
||||
return false;
|
||||
} else {
|
||||
if (!override && !record.getContent().equals(recordValue)) {
|
||||
s_logger.warn("Record " + recordName + " in domain " + bindZoneName + " have different value from " + recordValue
|
||||
+ " and override is not enable. I will not delete it.");
|
||||
return false;
|
||||
}
|
||||
_globoDns.getRecordAPI().removeRecord(record.getId());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record in Zone, or update it if record has been exists.
|
||||
* @param domainId
|
||||
* @param name
|
||||
* @param ip
|
||||
* @param type
|
||||
* @return if record was created or updated.
|
||||
*/
|
||||
private boolean createOrUpdateRecord(Long domainId, String name, String ip, String type, boolean override) {
|
||||
Record record = this.searchRecord(name, domainId);
|
||||
if (record == null) {
|
||||
// Create new record
|
||||
record = _globoDns.getRecordAPI().createRecord(domainId, name, ip, type);
|
||||
s_logger.info("Created record " + name + " in domain " + domainId);
|
||||
} else {
|
||||
if (!ip.equals(record.getContent())) {
|
||||
if (Boolean.TRUE.equals(override)) {
|
||||
// ip is incorrect. Fix.
|
||||
_globoDns.getRecordAPI().updateRecord(record.getId(), domainId, name, ip);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* GloboDns export all changes to Bind server.
|
||||
*/
|
||||
public void scheduleExportChangesToBind() {
|
||||
try {
|
||||
Export export = _globoDns.getExportAPI().scheduleExport();
|
||||
if (export != null) {
|
||||
s_logger.info("GloboDns Export: " + export.getResult());
|
||||
}
|
||||
} catch (GloboDnsException e) {
|
||||
s_logger.warn("Error on scheduling export. Although everything was persist, someone need to manually force export in GloboDns", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find bindZoneName in GloboDns.
|
||||
* @param name
|
||||
* @return Domain object or null if domain not exists.
|
||||
*/
|
||||
private Domain searchDomain(String name, boolean reverse) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
List<Domain> candidates;
|
||||
if (reverse) {
|
||||
candidates = _globoDns.getDomainAPI().listReverseByQuery(name);
|
||||
} else {
|
||||
candidates = _globoDns.getDomainAPI().listByQuery(name);
|
||||
}
|
||||
for (Domain candidate : candidates) {
|
||||
if (name.equals(candidate.getName())) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find recordName in domain.
|
||||
* @param recordName
|
||||
* @param domainId Id of BindZoneName. Maybe you need use searchDomain before to use BindZoneName.
|
||||
* @return Record or null if not exists.
|
||||
*/
|
||||
private Record searchRecord(String recordName, Long domainId) {
|
||||
if (recordName == null || domainId == null) {
|
||||
return null;
|
||||
}
|
||||
List<Record> candidates = _globoDns.getRecordAPI().listByQuery(domainId, recordName);
|
||||
// GloboDns search name in name and content. We need to iterate to check if recordName exists only in name
|
||||
for (Record candidate : candidates) {
|
||||
if (recordName.equalsIgnoreCase(candidate.getName())) {
|
||||
s_logger.debug("Record " + recordName + " in domain id " + domainId + " found in GloboDNS");
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
s_logger.debug("Record " + recordName + " in domain id " + domainId + " not found in GloboDNS");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate reverseBindZoneName of network. We ALWAYS use /24.
|
||||
* @param networkIp
|
||||
* @return Bind Zone Name reverse of network specified by networkIp
|
||||
*/
|
||||
private String generateReverseDomainNameFromNetworkIp(String networkIp) {
|
||||
String[] octets = networkIp.split("\\.");
|
||||
String reverseDomainName = octets[2] + '.' + octets[1] + '.' + octets[0] + '.' + REVERSE_DOMAIN_SUFFIX;
|
||||
return reverseDomainName;
|
||||
}
|
||||
|
||||
private String generateReverseRecordNameFromNetworkIp(String networkIp) {
|
||||
String[] octets = networkIp.split("\\.");
|
||||
String reverseRecordName = octets[3];
|
||||
return reverseRecordName;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.globo.globodns.client.model.Domain;
|
||||
|
||||
public class GloboDnsDomainListResponse extends Answer {
|
||||
|
||||
private List<Domain> domainList;
|
||||
|
||||
public GloboDnsDomainListResponse(Command command, List<Domain> domainList) {
|
||||
super(command, true, null);
|
||||
this.domainList = domainList;
|
||||
}
|
||||
|
||||
public List<Domain> getDomainList() {
|
||||
return domainList;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.globo.globodns.cloudstack.response;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.globo.globodns.client.model.Domain;
|
||||
|
||||
public class GloboDnsDomainResponse extends Answer {
|
||||
|
||||
private Domain domain;
|
||||
|
||||
public GloboDnsDomainResponse(Command command, Domain domain) {
|
||||
super(command, true, null);
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
public Domain getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.response;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.globo.globodns.client.model.Export;
|
||||
|
||||
public class GloboDnsExportResponse extends Answer {
|
||||
|
||||
private Export export;
|
||||
|
||||
public GloboDnsExportResponse(Command command, Export export) {
|
||||
super(command, true, null);
|
||||
this.export = export;
|
||||
}
|
||||
|
||||
public Export getExport() {
|
||||
return export;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.globo.globodns.client.model.Record;
|
||||
|
||||
public class GloboDnsRecordListResponse extends Answer {
|
||||
|
||||
private List<Record> recordList;
|
||||
|
||||
public GloboDnsRecordListResponse(Command command, List<Record> recordList) {
|
||||
super(command, true, null);
|
||||
this.recordList = recordList;
|
||||
}
|
||||
|
||||
public List<Record> getRecordList() {
|
||||
return recordList;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.globo.globodns.cloudstack.response;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.globo.globodns.client.model.Record;
|
||||
|
||||
public class GloboDnsRecordResponse extends Answer {
|
||||
|
||||
private Record record;
|
||||
|
||||
public GloboDnsRecordResponse(Command command, Record record) {
|
||||
super(command, true, null);
|
||||
this.record = record;
|
||||
}
|
||||
|
||||
public Record getRecord() {
|
||||
return record;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,250 @@
|
||||
package com.globo.globodns.cloudstack.element;
|
||||
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.isA;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.times;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.test.utils.SpringUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.deploy.DeployDestination;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.host.Host.Type;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.Network.Provider;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.PhysicalNetworkDao;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.vm.NicProfile;
|
||||
import com.cloud.vm.ReservationContext;
|
||||
import com.cloud.vm.ReservationContextImpl;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachineProfile;
|
||||
import com.globo.globodns.cloudstack.commands.CreateOrUpdateRecordAndReverseCommand;
|
||||
import com.globo.globodns.cloudstack.commands.RemoveRecordCommand;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
|
||||
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class GloboDnsElementTest {
|
||||
|
||||
private static long zoneId = 5L;
|
||||
private static long globoDnsHostId = 7L;
|
||||
private static long domainId = 10L;
|
||||
private AccountVO acct = null;
|
||||
private UserVO user = null;
|
||||
|
||||
@Inject
|
||||
DataCenterDao _datacenterDao;
|
||||
|
||||
@Inject
|
||||
GloboDnsElement _globodnsElement;
|
||||
|
||||
@Inject
|
||||
HostDao _hostDao;
|
||||
|
||||
@Inject
|
||||
AgentManager _agentMgr;
|
||||
|
||||
@Inject
|
||||
AccountManager _acctMgr;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ComponentContext.initComponentsLifeCycle();
|
||||
|
||||
acct = new AccountVO(200L);
|
||||
acct.setType(Account.ACCOUNT_TYPE_NORMAL);
|
||||
acct.setAccountName("user");
|
||||
acct.setDomainId(domainId);
|
||||
|
||||
user = new UserVO();
|
||||
user.setUsername("user");
|
||||
user.setAccountId(acct.getAccountId());
|
||||
|
||||
CallContext.register(user, acct);
|
||||
when(_acctMgr.getSystemAccount()).thenReturn(this.acct);
|
||||
when(_acctMgr.getSystemUser()).thenReturn(this.user);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
CallContext.unregister();
|
||||
acct = null;
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testUpperCaseCharactersAreNotAllowed() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
|
||||
Network network = mock(Network.class);
|
||||
when(network.getDataCenterId()).thenReturn(zoneId);
|
||||
when(network.getId()).thenReturn(1l);
|
||||
NicProfile nic = new NicProfile();
|
||||
VirtualMachineProfile vm = mock(VirtualMachineProfile.class);
|
||||
when(vm.getHostName()).thenReturn("UPPERCASENAME");
|
||||
when(vm.getType()).thenReturn(VirtualMachine.Type.User);
|
||||
when(_datacenterDao.findById(zoneId)).thenReturn(mock(DataCenterVO.class));
|
||||
DeployDestination dest = new DeployDestination();
|
||||
ReservationContext context = new ReservationContextImpl(null, null, user);
|
||||
_globodnsElement.prepare(network, nic, vm, dest, context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrepareMethodCallGloboDnsToRegisterHostName() throws Exception {
|
||||
Network network = mock(Network.class);
|
||||
when(network.getDataCenterId()).thenReturn(zoneId);
|
||||
when(network.getId()).thenReturn(1l);
|
||||
NicProfile nic = new NicProfile();
|
||||
nic.setIp4Address("10.11.12.13");
|
||||
VirtualMachineProfile vm = mock(VirtualMachineProfile.class);
|
||||
when(vm.getHostName()).thenReturn("vm-name");
|
||||
when(vm.getType()).thenReturn(VirtualMachine.Type.User);
|
||||
DataCenterVO dataCenterVO = mock(DataCenterVO.class);
|
||||
when(dataCenterVO.getId()).thenReturn(zoneId);
|
||||
when(_datacenterDao.findById(zoneId)).thenReturn(dataCenterVO);
|
||||
DeployDestination dest = new DeployDestination();
|
||||
ReservationContext context = new ReservationContextImpl(null, null, user);
|
||||
|
||||
HostVO hostVO = mock(HostVO.class);
|
||||
when(hostVO.getId()).thenReturn(globoDnsHostId);
|
||||
when(_hostDao.findByTypeNameAndZoneId(eq(zoneId), eq(Provider.GloboDns.getName()), eq(Type.L2Networking))).thenReturn(hostVO);
|
||||
|
||||
when(_agentMgr.easySend(eq(globoDnsHostId), isA(CreateOrUpdateRecordAndReverseCommand.class))).then(new org.mockito.stubbing.Answer<Answer>() {
|
||||
|
||||
@Override
|
||||
public Answer answer(InvocationOnMock invocation) throws Throwable {
|
||||
Command cmd = (Command)invocation.getArguments()[1];
|
||||
return new Answer(cmd);
|
||||
}
|
||||
});
|
||||
|
||||
_globodnsElement.prepare(network, nic, vm, dest, context);
|
||||
verify(_agentMgr, times(1)).easySend(eq(globoDnsHostId), isA(CreateOrUpdateRecordAndReverseCommand.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReleaseMethodCallResource() throws Exception {
|
||||
Network network = mock(Network.class);
|
||||
when(network.getDataCenterId()).thenReturn(zoneId);
|
||||
when(network.getId()).thenReturn(1l);
|
||||
NicProfile nic = new NicProfile();
|
||||
nic.setIp4Address("10.11.12.13");
|
||||
VirtualMachineProfile vm = mock(VirtualMachineProfile.class);
|
||||
when(vm.getHostName()).thenReturn("vm-name");
|
||||
when(vm.getType()).thenReturn(VirtualMachine.Type.User);
|
||||
DataCenterVO dataCenterVO = mock(DataCenterVO.class);
|
||||
when(dataCenterVO.getId()).thenReturn(zoneId);
|
||||
when(_datacenterDao.findById(zoneId)).thenReturn(dataCenterVO);
|
||||
ReservationContext context = new ReservationContextImpl(null, null, user);
|
||||
|
||||
HostVO hostVO = mock(HostVO.class);
|
||||
when(hostVO.getId()).thenReturn(globoDnsHostId);
|
||||
when(_hostDao.findByTypeNameAndZoneId(eq(zoneId), eq(Provider.GloboDns.getName()), eq(Type.L2Networking))).thenReturn(hostVO);
|
||||
|
||||
when(_agentMgr.easySend(eq(globoDnsHostId), isA(RemoveRecordCommand.class))).then(new org.mockito.stubbing.Answer<Answer>() {
|
||||
|
||||
@Override
|
||||
public Answer answer(InvocationOnMock invocation) throws Throwable {
|
||||
Command cmd = (Command)invocation.getArguments()[1];
|
||||
return new Answer(cmd);
|
||||
}
|
||||
});
|
||||
|
||||
_globodnsElement.release(network, nic, vm, context);
|
||||
verify(_agentMgr, times(1)).easySend(eq(globoDnsHostId), isA(RemoveRecordCommand.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = {GloboDnsElement.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, useDefaultFilters = false)
|
||||
public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public HostDao hostDao() {
|
||||
return mock(HostDao.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataCenterDao dataCenterDao() {
|
||||
return mock(DataCenterDao.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PhysicalNetworkDao physicalNetworkDao() {
|
||||
return mock(PhysicalNetworkDao.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NetworkDao networkDao() {
|
||||
return mock(NetworkDao.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ConfigurationDao configurationDao() {
|
||||
return mock(ConfigurationDao.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AgentManager agentManager() {
|
||||
return mock(AgentManager.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceManager resourceManager() {
|
||||
return mock(ResourceManager.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AccountManager accountManager() {
|
||||
return mock(AccountManager.class);
|
||||
}
|
||||
|
||||
public static class Library implements TypeFilter {
|
||||
|
||||
@Override
|
||||
public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException {
|
||||
ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class);
|
||||
return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,447 @@
|
||||
package com.globo.globodns.cloudstack.resource;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.any;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.globo.globodns.client.GloboDns;
|
||||
import com.globo.globodns.client.api.DomainAPI;
|
||||
import com.globo.globodns.client.api.ExportAPI;
|
||||
import com.globo.globodns.client.api.RecordAPI;
|
||||
import com.globo.globodns.client.model.Domain;
|
||||
import com.globo.globodns.client.model.Record;
|
||||
import com.globo.globodns.cloudstack.commands.CreateOrUpdateDomainCommand;
|
||||
import com.globo.globodns.cloudstack.commands.CreateOrUpdateRecordAndReverseCommand;
|
||||
import com.globo.globodns.cloudstack.commands.RemoveDomainCommand;
|
||||
import com.globo.globodns.cloudstack.commands.RemoveRecordCommand;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
|
||||
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class GloboDnsResourceTest {
|
||||
|
||||
private GloboDnsResource _globoDnsResource;
|
||||
|
||||
private GloboDns _globoDnsApi;
|
||||
private DomainAPI _domainApi;
|
||||
private RecordAPI _recordApi;
|
||||
private ExportAPI _exportApi;
|
||||
|
||||
private static final Long TEMPLATE_ID = 1l;
|
||||
|
||||
private static long sequenceId = 10l;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// ComponentContext.initComponentsLifeCycle();
|
||||
|
||||
String name = "GloboDNS";
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
params.put("zoneId", "1");
|
||||
params.put("guid", "globodns");
|
||||
params.put("name", name);
|
||||
params.put("url", "http://example.com");
|
||||
params.put("username", "username");
|
||||
params.put("password", "password");
|
||||
|
||||
_globoDnsResource = new GloboDnsResource();
|
||||
_globoDnsResource.configure(name, params);
|
||||
|
||||
_globoDnsApi = spy(_globoDnsResource._globoDns);
|
||||
_globoDnsResource._globoDns = _globoDnsApi;
|
||||
|
||||
_domainApi = mock(DomainAPI.class);
|
||||
when(_globoDnsApi.getDomainAPI()).thenReturn(_domainApi);
|
||||
|
||||
_recordApi = mock(RecordAPI.class);
|
||||
when(_globoDnsApi.getRecordAPI()).thenReturn(_recordApi);
|
||||
|
||||
_exportApi = mock(ExportAPI.class);
|
||||
when(_globoDnsApi.getExportAPI()).thenReturn(_exportApi);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// Auxiliary Methods //
|
||||
///////////////////////
|
||||
|
||||
private Domain generateFakeDomain(String domainName, boolean reverse) {
|
||||
Domain domain = new Domain();
|
||||
domain.getDomainAttributes().setId(sequenceId++);
|
||||
domain.getDomainAttributes().setName(domainName);
|
||||
List<Domain> domainList = new ArrayList<Domain>();
|
||||
domainList.add(domain);
|
||||
if (reverse) {
|
||||
when(_domainApi.listReverseByQuery(eq(domainName))).thenReturn(domainList);
|
||||
} else {
|
||||
when(_domainApi.listByQuery(eq(domainName))).thenReturn(domainList);
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
private Record generateFakeRecord(Domain domain, String recordName, String recordContent, boolean reverse) {
|
||||
Record record = new Record();
|
||||
if (reverse) {
|
||||
record.getTypePTRRecordAttributes().setName(recordName);
|
||||
record.getTypePTRRecordAttributes().setContent(recordContent);
|
||||
record.getTypePTRRecordAttributes().setDomainId(domain.getId());
|
||||
record.getTypePTRRecordAttributes().setId(sequenceId++);
|
||||
} else {
|
||||
record.getTypeARecordAttributes().setName(recordName);
|
||||
record.getTypeARecordAttributes().setContent(recordContent);
|
||||
record.getTypeARecordAttributes().setDomainId(domain.getId());
|
||||
record.getTypeARecordAttributes().setId(sequenceId++);
|
||||
}
|
||||
List<Record> recordList = new ArrayList<Record>();
|
||||
recordList.add(record);
|
||||
when(_recordApi.listByQuery(eq(domain.getId()), eq(recordName))).thenReturn(recordList);
|
||||
return record;
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Create Domain tests //
|
||||
/////////////////////////
|
||||
|
||||
@Test
|
||||
public void testCreateDomainWithSuccessWhenDomainDoesntExistAndOverrideIsTrue() throws Exception {
|
||||
String domainName = "domain.name.com";
|
||||
|
||||
Domain domain = new Domain();
|
||||
domain.getDomainAttributes().setId(sequenceId++);
|
||||
domain.getDomainAttributes().setName(domainName);
|
||||
|
||||
when(_domainApi.createDomain(eq(domain.getName()), eq(TEMPLATE_ID), eq("M"))).thenReturn(domain);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new CreateOrUpdateDomainCommand(domainName, TEMPLATE_ID));
|
||||
assertNotNull(answer);
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void testCreateDomainWillSucceedWhenDomainAlreadyExistsAndOverrideIsFalse() throws Exception {
|
||||
String domainName = "domain.name.com";
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new CreateOrUpdateDomainCommand(domainName, TEMPLATE_ID));
|
||||
assertNotNull(answer);
|
||||
assertEquals(true, answer.getResult());
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Create Record tests //
|
||||
/////////////////////////
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void testCreateRecordAndReverseWithSuccessWhenDomainExistsAndRecordDoesntExistAndOverrideIsTrue() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String recordIp = "40.30.20.10";
|
||||
String domainName = "domain.name.com";
|
||||
String reverseDomainName = "20.30.40.in-addr.arpa";
|
||||
String reverseRecordName = "10";
|
||||
String reverseRecordContent = recordName + "." + domainName;
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
Record record = generateFakeRecord(domain, recordName, recordIp, false);
|
||||
Domain reverseDomain = generateFakeDomain(reverseDomainName, true);
|
||||
Record reverseRecord = generateFakeRecord(reverseDomain, reverseRecordName, reverseRecordContent, true);
|
||||
|
||||
when(_recordApi.createRecord(eq(domain.getId()), eq(recordName), eq(recordIp), eq("A"))).thenReturn(record);
|
||||
when(_recordApi.createRecord(eq(reverseDomain.getId()), eq(reverseRecordName), eq(reverseRecordContent), eq("PTR"))).thenReturn(record);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, true));
|
||||
assertNotNull(answer);
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void testCreateRecordAndReverseWillFailWhenRecordAlreadyExistsAndOverrideIsFalse() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String newIp = "40.30.20.10";
|
||||
String oldIp = "50.40.30.20";
|
||||
String domainName = "domain.name.com";
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
Record record = generateFakeRecord(domain, recordName, oldIp, false);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, newIp, domainName, TEMPLATE_ID, false));
|
||||
assertNotNull(answer);
|
||||
assertEquals(false, answer.getResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void testCreateRecordAndReverseWillFailWhenReverseRecordAlreadyExistsAndOverrideIsFalse() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String recordIp = "40.30.20.10";
|
||||
String domainName = "domain.name.com";
|
||||
String reverseDomainName = "20.30.40.in-addr.arpa";
|
||||
String reverseRecordName = "10";
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
Record record = generateFakeRecord(domain, recordName, recordIp, false);
|
||||
Domain reverseDomain = generateFakeDomain(reverseDomainName, true);
|
||||
Record reverseRecord = generateFakeRecord(reverseDomain, reverseRecordName, "X", true);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, false));
|
||||
assertNotNull(answer);
|
||||
assertEquals(false, answer.getResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRecordAndReverseWhenDomainDoesNotExist() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String recordIp = "40.30.20.10";
|
||||
String domainName = "domain.name.com";
|
||||
String reverseDomainName = "20.30.40.in-addr.arpa";
|
||||
String reverseRecordName = "10";
|
||||
String reverseRecordContent = recordName + "." + domainName;
|
||||
|
||||
Domain domain = new Domain();
|
||||
domain.getDomainAttributes().setId(sequenceId++);
|
||||
Domain reverseDomain = new Domain();
|
||||
reverseDomain.getDomainAttributes().setId(sequenceId++);
|
||||
|
||||
Record record = new Record();
|
||||
|
||||
when(_domainApi.listByQuery(domainName)).thenReturn(new ArrayList<Domain>());
|
||||
when(_domainApi.createDomain(eq(domainName), eq(TEMPLATE_ID), eq("M"))).thenReturn(domain);
|
||||
when(_recordApi.createRecord(eq(domain.getId()), eq(recordName), eq(recordIp), eq("A"))).thenReturn(record);
|
||||
when(_domainApi.createReverseDomain(eq(reverseDomainName), eq(TEMPLATE_ID), eq("M"))).thenReturn(reverseDomain);
|
||||
when(_recordApi.createRecord(eq(reverseDomain.getId()), eq(reverseRecordName), eq(reverseRecordContent), eq("PTR"))).thenReturn(record);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, true));
|
||||
assertNotNull(answer);
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRecordAndReverseWhenDomainDoesNotExistAndOverrideIsFalse() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String recordIp = "40.30.20.10";
|
||||
String domainName = "domain.name.com";
|
||||
String reverseDomainName = "20.30.40.in-addr.arpa";
|
||||
String reverseRecordName = "10";
|
||||
String reverseRecordContent = recordName + "." + domainName;
|
||||
|
||||
Domain domain = new Domain();
|
||||
domain.getDomainAttributes().setId(sequenceId++);
|
||||
Domain reverseDomain = new Domain();
|
||||
reverseDomain.getDomainAttributes().setId(sequenceId++);
|
||||
|
||||
Record record = new Record();
|
||||
|
||||
when(_domainApi.listByQuery(domainName)).thenReturn(new ArrayList<Domain>());
|
||||
when(_domainApi.createDomain(eq(domainName), eq(TEMPLATE_ID), eq("M"))).thenReturn(domain);
|
||||
when(_recordApi.createRecord(eq(domain.getId()), eq(recordName), eq(recordIp), eq("A"))).thenReturn(record);
|
||||
when(_domainApi.createReverseDomain(eq(reverseDomainName), eq(TEMPLATE_ID), eq("M"))).thenReturn(reverseDomain);
|
||||
when(_recordApi.createRecord(eq(reverseDomain.getId()), eq(reverseRecordName), eq(reverseRecordContent), eq("PTR"))).thenReturn(record);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, false));
|
||||
assertNotNull(answer);
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Update Record tests //
|
||||
/////////////////////////
|
||||
|
||||
@Test
|
||||
public void testUpdateRecordAndReverseWhenDomainExistsAndOverrideIsTrue() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String oldRecordIp = "40.30.20.10";
|
||||
String newRecordIp = "50.40.30.20";
|
||||
String domainName = "domain.name.com";
|
||||
String reverseDomainName = "30.40.50.in-addr.arpa";
|
||||
String reverseRecordName = "20";
|
||||
String reverseRecordContent = recordName + "." + domainName;
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
Record record = generateFakeRecord(domain, recordName, oldRecordIp, false);
|
||||
Domain reverseDomain = generateFakeDomain(reverseDomainName, true);
|
||||
Record reverseRecord = generateFakeRecord(reverseDomain, reverseRecordName, "X", true);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, newRecordIp, domainName, TEMPLATE_ID, true));
|
||||
|
||||
// ensure calls in sequence to ensure this call are the only ones.
|
||||
InOrder inOrder = inOrder(_recordApi);
|
||||
inOrder.verify(_recordApi, times(1)).updateRecord(eq(record.getId()), eq(domain.getId()), eq(recordName), eq(newRecordIp));
|
||||
inOrder.verify(_recordApi, times(1)).updateRecord(eq(reverseRecord.getId()), eq(reverseDomain.getId()), eq(reverseRecordName), eq(reverseRecordContent));
|
||||
|
||||
assertNotNull(answer);
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Remove Record tests //
|
||||
/////////////////////////
|
||||
|
||||
@Test
|
||||
public void testRemoveRecordWhenRecordExists() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String recordIp = "40.30.20.10";
|
||||
String domainName = "domain.name.com";
|
||||
String reverseDomainName = "20.30.40.in-addr.arpa";
|
||||
String reverseRecordName = "10";
|
||||
String reverseRecordContent = recordName + "." + domainName;
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
Record record = generateFakeRecord(domain, recordName, recordIp, false);
|
||||
Domain reverseDomain = generateFakeDomain(reverseDomainName, true);
|
||||
Record reverseRecord = generateFakeRecord(reverseDomain, reverseRecordName, reverseRecordContent, true);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new RemoveRecordCommand(recordName, recordIp, domainName, true));
|
||||
|
||||
assertNotNull(answer);
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_recordApi, times(1)).removeRecord(eq(record.getId()));
|
||||
verify(_recordApi, times(1)).removeRecord(eq(reverseRecord.getId()));
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveRecordWithSuccessAndReverseRecordNotRemovedWhenReverseRecordExistsWithDifferentValueAndOverrideIsFalse() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String recordIp = "40.30.20.10";
|
||||
String domainName = "domain.name.com";
|
||||
String reverseDomainName = "20.30.40.in-addr.arpa";
|
||||
String reverseRecordName = "10";
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
Record record = generateFakeRecord(domain, recordName, recordIp, false);
|
||||
Domain reverseDomain = generateFakeDomain(reverseDomainName, true);
|
||||
Record reverseRecord = generateFakeRecord(reverseDomain, reverseRecordName, "X", true);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new RemoveRecordCommand(recordName, recordIp, domainName, false));
|
||||
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_recordApi, times(1)).removeRecord(eq(record.getId()));
|
||||
verify(_recordApi, never()).removeRecord(eq(reverseRecord.getId()));
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveReverseRecordButNotRemoveRecordWhenRecordExistsWithDifferentValueAndOverrideIsFalse() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String recordIp = "40.30.20.10";
|
||||
String domainName = "domain.name.com";
|
||||
String reverseDomainName = "20.30.40.in-addr.arpa";
|
||||
String reverseRecordName = "10";
|
||||
String reverseRecordContent = recordName + "." + domainName;
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
Record record = generateFakeRecord(domain, recordName, "X", false);
|
||||
Domain reverseDomain = generateFakeDomain(reverseDomainName, true);
|
||||
Record reverseRecord = generateFakeRecord(reverseDomain, reverseRecordName, reverseRecordContent, true);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new RemoveRecordCommand(recordName, recordIp, domainName, false));
|
||||
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_recordApi, never()).removeRecord(eq(record.getId()));
|
||||
verify(_recordApi, times(1)).removeRecord(eq(reverseRecord.getId()));
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Remove Domain tests //
|
||||
/////////////////////////
|
||||
|
||||
@Test
|
||||
public void testRemoveDomainWithSuccessButDomainKeptWhenDomainExistsAndThereAreRecordsAndOverrideIsFalse() throws Exception {
|
||||
String recordName = "recordname";
|
||||
String recordIp = "40.30.20.10";
|
||||
String domainName = "domain.name.com";
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
Record record = generateFakeRecord(domain, recordName, "X", false);
|
||||
when(_recordApi.listAll(domain.getId())).thenReturn(Arrays.asList(record));
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new RemoveRecordCommand(recordName, recordIp, domainName, false));
|
||||
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_domainApi, never()).removeDomain(any(Long.class));
|
||||
verify(_exportApi, never()).scheduleExport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveDomainWithSuccessWhenDomainExistsAndThereAreOnlyNSRecordsAndOverrideIsFalse() throws Exception {
|
||||
String domainName = "domain.name.com";
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
List<Record> recordList = new ArrayList<Record>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Record record = new Record();
|
||||
record.getTypeNSRecordAttributes().setDomainId(domain.getId());
|
||||
record.getTypeNSRecordAttributes().setId(sequenceId++);
|
||||
record.getTypeNSRecordAttributes().setType("NS");
|
||||
recordList.add(record);
|
||||
}
|
||||
when(_recordApi.listAll(domain.getId())).thenReturn(recordList);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new RemoveDomainCommand(domainName, false));
|
||||
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_domainApi, times(1)).removeDomain(eq(domain.getId()));
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveDomainWithSuccessWhenDomainExistsAndThereAreRecordsAndOverrideIsTrue() throws Exception {
|
||||
String domainName = "domain.name.com";
|
||||
|
||||
Domain domain = generateFakeDomain(domainName, false);
|
||||
List<Record> recordList = new ArrayList<Record>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Record record = new Record();
|
||||
record.getTypeNSRecordAttributes().setDomainId(domain.getId());
|
||||
record.getTypeNSRecordAttributes().setId(sequenceId++);
|
||||
record.getTypeNSRecordAttributes().setType(new String[] {"A", "NS", "PTR"}[i % 3]);
|
||||
recordList.add(record);
|
||||
}
|
||||
when(_recordApi.listAll(domain.getId())).thenReturn(recordList);
|
||||
|
||||
Answer answer = _globoDnsResource.execute(new RemoveDomainCommand(domainName, true));
|
||||
|
||||
assertEquals(true, answer.getResult());
|
||||
verify(_domainApi, times(1)).removeDomain(eq(domain.getId()));
|
||||
verify(_exportApi, times(1)).scheduleExport();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
# management server clustering parameters, change cluster.node.IP to the machine IP address
|
||||
# in which the management server(Tomcat) is running
|
||||
cluster.node.IP=127.0.0.1
|
||||
cluster.servlet.port=9090
|
||||
region.id=1
|
||||
|
||||
# CloudStack database settings
|
||||
db.cloud.username=cloud
|
||||
db.cloud.password=cloud
|
||||
db.root.password=
|
||||
db.cloud.host=localhost
|
||||
db.cloud.port=3306
|
||||
db.cloud.name=cloud
|
||||
|
||||
# CloudStack database tuning parameters
|
||||
db.cloud.maxActive=250
|
||||
db.cloud.maxIdle=30
|
||||
db.cloud.maxWait=10000
|
||||
db.cloud.autoReconnect=true
|
||||
db.cloud.validationQuery=SELECT 1
|
||||
db.cloud.testOnBorrow=true
|
||||
db.cloud.testWhileIdle=true
|
||||
db.cloud.timeBetweenEvictionRunsMillis=40000
|
||||
db.cloud.minEvictableIdleTimeMillis=240000
|
||||
db.cloud.poolPreparedStatements=false
|
||||
db.cloud.url.params=prepStmtCacheSize=517&cachePrepStmts=true&prepStmtCacheSqlLimit=4096
|
||||
|
||||
# usage database settings
|
||||
db.usage.username=cloud
|
||||
db.usage.password=cloud
|
||||
db.usage.host=localhost
|
||||
db.usage.port=3306
|
||||
db.usage.name=cloud_usage
|
||||
|
||||
# usage database tuning parameters
|
||||
db.usage.maxActive=100
|
||||
db.usage.maxIdle=30
|
||||
db.usage.maxWait=10000
|
||||
db.usage.autoReconnect=true
|
||||
|
||||
# awsapi database settings
|
||||
db.awsapi.username=cloud
|
||||
db.awsapi.password=cloud
|
||||
db.awsapi.host=localhost
|
||||
db.awsapi.port=3306
|
||||
db.awsapi.name=cloudbridge
|
||||
|
||||
# Simulator database settings
|
||||
db.simulator.username=cloud
|
||||
db.simulator.password=cloud
|
||||
db.simulator.host=localhost
|
||||
db.simulator.port=3306
|
||||
db.simulator.name=simulator
|
||||
db.simulator.maxActive=250
|
||||
db.simulator.maxIdle=30
|
||||
db.simulator.maxWait=10000
|
||||
db.simulator.autoReconnect=true
|
||||
@ -0,0 +1,16 @@
|
||||
# Define the root logger with appender file
|
||||
#log = /var/log/log4j
|
||||
#log4j.rootLogger = DEBUG, FILE
|
||||
log4j.rootLogger = DEBUG, stdout
|
||||
|
||||
# File appender
|
||||
#log4j.appender.FILE=org.apache.log4j.FileAppender
|
||||
#log4j.appender.FILE.File=${log}/log.out
|
||||
#log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
|
||||
#log4j.appender.FILE.layout.conversionPattern=%m%n
|
||||
|
||||
# Stdout appender
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
@ -88,6 +88,7 @@
|
||||
<module>alert-handlers/syslog-alerts</module>
|
||||
<module>network-elements/internal-loadbalancer</module>
|
||||
<module>network-elements/vxlan</module>
|
||||
<module>network-elements/globodns</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@ -7424,7 +7424,211 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// GloboDns provider detail view
|
||||
GloboDns: {
|
||||
isMaximized: true,
|
||||
type: 'detailView',
|
||||
id: 'globoDnsProvider',
|
||||
label: 'GloboDNS',
|
||||
tabs: {
|
||||
details: {
|
||||
title: 'label.details',
|
||||
fields: [{
|
||||
name: {
|
||||
label: 'label.name'
|
||||
}
|
||||
}, {
|
||||
state: {
|
||||
label: 'label.state'
|
||||
}
|
||||
}],
|
||||
dataProvider: function(args) {
|
||||
refreshNspData("GloboDns");
|
||||
var providerObj;
|
||||
$(nspHardcodingArray).each(function() {
|
||||
if (this.id == "GloboDns") {
|
||||
providerObj = this;
|
||||
return false; //break each loop
|
||||
}
|
||||
});
|
||||
args.response.success({
|
||||
data: providerObj,
|
||||
actionFilter: networkProviderActionFilter('GloboDns')
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
add: {
|
||||
label: 'GloboDNS Configuration',
|
||||
createForm: {
|
||||
title: 'GloboDNS Configuration',
|
||||
preFilter: function(args) {},
|
||||
fields: {
|
||||
username: {
|
||||
label: 'Username',
|
||||
validation: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
password: {
|
||||
label: 'Password',
|
||||
isPassword: true,
|
||||
validation: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
url: {
|
||||
label: 'URL',
|
||||
validation: {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
action: function(args) {
|
||||
if (nspMap["GloboDns"] == null) {
|
||||
$.ajax({
|
||||
url: createURL("addNetworkServiceProvider&name=GloboDns&physicalnetworkid=" + selectedPhysicalNetworkObj.id),
|
||||
dataType: "json",
|
||||
async: true,
|
||||
success: function(json) {
|
||||
var jobId = json.addnetworkserviceproviderresponse.jobid;
|
||||
var addGloboDnsProviderIntervalID = setInterval(function() {
|
||||
$.ajax({
|
||||
url: createURL("queryAsyncJobResult&jobId=" + jobId),
|
||||
dataType: "json",
|
||||
success: function(json) {
|
||||
var result = json.queryasyncjobresultresponse;
|
||||
if (result.jobstatus == 0) {
|
||||
return; //Job has not completed
|
||||
} else {
|
||||
clearInterval(addGloboDnsProviderIntervalID);
|
||||
if (result.jobstatus == 1) {
|
||||
nspMap["GloboDns"] = json.queryasyncjobresultresponse.jobresult.networkserviceprovider;
|
||||
addGloboDnsHost(args, selectedPhysicalNetworkObj, "addGloboDnsHost", "addglobodnshostresponse");
|
||||
} else if (result.jobstatus == 2) {
|
||||
alert("addNetworkServiceProvider&name=GloboDns failed. Error: " + _s(result.jobresult.errortext));
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(XMLHttpResponse) {
|
||||
var errorMsg = parseXMLHttpResponse(XMLHttpResponse);
|
||||
alert("addNetworkServiceProvider&name=GloboDns failed. Error: " + errorMsg);
|
||||
}
|
||||
});
|
||||
}, g_queryAsyncJobResultInterval);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addGloboDnsHost(args, selectedPhysicalNetworkObj, "addGloboDnsHost", "addglobodnshostresponse");
|
||||
}
|
||||
},
|
||||
messages: {
|
||||
notification: function(args) {
|
||||
return 'Add GloboDNS';
|
||||
}
|
||||
},
|
||||
notification: {
|
||||
poll: pollAsyncJobResult
|
||||
}
|
||||
},
|
||||
enable: {
|
||||
label: 'label.enable.provider',
|
||||
action: function(args) {
|
||||
$.ajax({
|
||||
url: createURL("updateNetworkServiceProvider&id=" + nspMap["GloboDns"].id + "&state=Enabled"),
|
||||
dataType: "json",
|
||||
success: function(json) {
|
||||
var jid = json.updatenetworkserviceproviderresponse.jobid;
|
||||
args.response.success({
|
||||
_custom: {
|
||||
jobId: jid,
|
||||
getUpdatedItem: function(json) {
|
||||
$(window).trigger('cloudStack.fullRefresh');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
messages: {
|
||||
confirm: function(args) {
|
||||
return 'message.confirm.enable.provider';
|
||||
},
|
||||
notification: function() {
|
||||
return 'label.enable.provider';
|
||||
}
|
||||
},
|
||||
notification: {
|
||||
poll: pollAsyncJobResult
|
||||
}
|
||||
},
|
||||
disable: {
|
||||
label: 'label.disable.provider',
|
||||
action: function(args) {
|
||||
$.ajax({
|
||||
url: createURL("updateNetworkServiceProvider&id=" + nspMap["GloboDns"].id + "&state=Disabled"),
|
||||
dataType: "json",
|
||||
success: function(json) {
|
||||
var jid = json.updatenetworkserviceproviderresponse.jobid;
|
||||
args.response.success({
|
||||
_custom: {
|
||||
jobId: jid,
|
||||
getUpdatedItem: function(json) {
|
||||
$(window).trigger('cloudStack.fullRefresh');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
messages: {
|
||||
confirm: function(args) {
|
||||
return 'message.confirm.disable.provider';
|
||||
},
|
||||
notification: function() {
|
||||
return 'label.disable.provider';
|
||||
}
|
||||
},
|
||||
notification: {
|
||||
poll: pollAsyncJobResult
|
||||
}
|
||||
},
|
||||
destroy: {
|
||||
label: 'label.shutdown.provider',
|
||||
action: function(args) {
|
||||
$.ajax({
|
||||
url: createURL("deleteNetworkServiceProvider&id=" + nspMap["GloboDns"].id),
|
||||
dataType: "json",
|
||||
success: function(json) {
|
||||
var jid = json.deletenetworkserviceproviderresponse.jobid;
|
||||
args.response.success({
|
||||
_custom: {
|
||||
jobId: jid
|
||||
}
|
||||
});
|
||||
|
||||
$(window).trigger('cloudStack.fullRefresh');
|
||||
}
|
||||
});
|
||||
},
|
||||
messages: {
|
||||
confirm: function(args) {
|
||||
return 'message.confirm.shutdown.provider';
|
||||
},
|
||||
notification: function(args) {
|
||||
return 'label.shutdown.provider';
|
||||
}
|
||||
},
|
||||
notification: {
|
||||
poll: pollAsyncJobResult
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -19809,7 +20013,30 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addGloboDnsHost(args, physicalNetworkObj, apiCmd, apiCmdRes) {
|
||||
var array1 = [];
|
||||
array1.push("&physicalnetworkid=" + physicalNetworkObj.id);
|
||||
array1.push("&username=" + todb(args.data.username));
|
||||
array1.push("&password=" + todb(args.data.password));
|
||||
array1.push("&url=" + todb(args.data.url));
|
||||
|
||||
$.ajax({
|
||||
url: createURL(apiCmd + array1.join("")),
|
||||
dataType: "json",
|
||||
type: "POST",
|
||||
success: function(json) {
|
||||
var jid = json[apiCmdRes].jobid;
|
||||
args.response.success({
|
||||
_custom: {
|
||||
jobId: jid,
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var afterCreateZonePhysicalNetworkTrafficTypes = function (args, newZoneObj, newPhysicalnetwork) {
|
||||
$.ajax({
|
||||
@ -20495,6 +20722,9 @@
|
||||
case "NuageVsp":
|
||||
nspMap["nuageVsp"] = items[i];
|
||||
break;
|
||||
case "GloboDns":
|
||||
nspMap["GloboDns"] = items[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20598,6 +20828,11 @@
|
||||
name: 'Palo Alto',
|
||||
state: nspMap.pa ? nspMap.pa.state: 'Disabled'
|
||||
});
|
||||
nspHardcodingArray.push({
|
||||
id: 'GloboDns',
|
||||
name: 'GloboDNS',
|
||||
state: nspMap.GloboDns ? nspMap.GloboDns.state : 'Disabled'
|
||||
});
|
||||
}
|
||||
|
||||
if ($.grep(nspHardcodingArray, function(e) { return e.id == 'Ovs'; }).length == 0 ) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user