From 233445ed68e3f903367e14a277ec2a5de9597b52 Mon Sep 17 00:00:00 2001 From: Daniel Vega Date: Wed, 20 Aug 2014 15:49:40 -0300 Subject: [PATCH] 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 --- api/src/com/cloud/network/Network.java | 2 + .../network/ExternalNetworkDeviceManager.java | 1 + client/pom.xml | 5 + client/tomcatconf/commands.properties.in | 3 + plugins/network-elements/globodns/pom.xml | 37 ++ .../cloudstack/globodns/module.properties | 18 + .../globodns/spring-globodns-context.xml | 22 + .../cloudstack/api/AddGloboDnsHostCmd.java | 124 +++++ .../commands/CreateOrUpdateDomainCommand.java | 44 ++ ...CreateOrUpdateRecordAndReverseCommand.java | 66 +++ .../commands/RemoveDomainCommand.java | 44 ++ .../commands/RemoveRecordCommand.java | 59 +++ .../cloudstack/commands/SignInCommand.java | 44 ++ .../cloudstack/element/GloboDnsElement.java | 388 +++++++++++++++ .../element/GloboDnsElementService.java | 25 + .../cloudstack/resource/GloboDnsResource.java | 456 ++++++++++++++++++ .../response/GloboDnsDomainListResponse.java | 38 ++ .../response/GloboDnsDomainResponse.java | 20 + .../response/GloboDnsExportResponse.java | 36 ++ .../response/GloboDnsRecordListResponse.java | 38 ++ .../response/GloboDnsRecordResponse.java | 36 ++ .../element/GloboDnsElementTest.java | 250 ++++++++++ .../resource/GloboDnsResourceTest.java | 447 +++++++++++++++++ .../globodns/test/resources/db.properties | 75 +++ .../globodns/test/resources/log4j.properties | 16 + plugins/pom.xml | 1 + ui/scripts/system.js | 239 ++++++++- 27 files changed, 2532 insertions(+), 2 deletions(-) create mode 100644 plugins/network-elements/globodns/pom.xml create mode 100644 plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/module.properties create mode 100644 plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/spring-globodns-context.xml create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/api/AddGloboDnsHostCmd.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateDomainCommand.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateRecordAndReverseCommand.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveDomainCommand.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveRecordCommand.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/SignInCommand.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElement.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElementService.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/resource/GloboDnsResource.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainListResponse.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainResponse.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsExportResponse.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordListResponse.java create mode 100644 plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordResponse.java create mode 100644 plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/element/GloboDnsElementTest.java create mode 100644 plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/resource/GloboDnsResourceTest.java create mode 100644 plugins/network-elements/globodns/test/resources/db.properties create mode 100644 plugins/network-elements/globodns/test/resources/log4j.properties diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index 55502df669d..c5a9bf286df 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -136,6 +136,8 @@ public interface Network extends ControlledEntity, StateObject, 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; diff --git a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java index f38e9e61561..b3493561102 100644 --- a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java +++ b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java @@ -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; diff --git a/client/pom.xml b/client/pom.xml index d87fc4573ab..dccf18d2d4d 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -346,6 +346,11 @@ cloud-plugin-api-solidfire-intg-test ${project.version} + + org.apache.cloudstack + cloud-plugin-network-globodns + ${project.version} + diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 3191bbf41b2..18ac2bf236a 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -763,3 +763,6 @@ createServiceInstance=1 addOpenDaylightController=1 deleteOpenDaylightController=1 listOpenDaylightControllers=1 + +### GloboDNS commands +addGloboDnsHost=1 diff --git a/plugins/network-elements/globodns/pom.xml b/plugins/network-elements/globodns/pom.xml new file mode 100644 index 00000000000..0f17bba85b3 --- /dev/null +++ b/plugins/network-elements/globodns/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + cloud-plugin-network-globodns + Apache CloudStack Plugin - GloboDNS + + org.apache.cloudstack + cloudstack-plugins + 4.5.0-SNAPSHOT + ../../pom.xml + + + + com.globo.globodns + globodns-client + 0.0.15 + + + diff --git a/plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/module.properties b/plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/module.properties new file mode 100644 index 00000000000..6c74bd2315e --- /dev/null +++ b/plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/module.properties @@ -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 \ No newline at end of file diff --git a/plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/spring-globodns-context.xml b/plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/spring-globodns-context.xml new file mode 100644 index 00000000000..7e2e809cb15 --- /dev/null +++ b/plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/spring-globodns-context.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/api/AddGloboDnsHostCmd.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/api/AddGloboDnsHostCmd.java new file mode 100644 index 00000000000..1694a329520 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/api/AddGloboDnsHostCmd.java @@ -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"; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateDomainCommand.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateDomainCommand.java new file mode 100644 index 00000000000..83564cb9c86 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateDomainCommand.java @@ -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; + } +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateRecordAndReverseCommand.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateRecordAndReverseCommand.java new file mode 100644 index 00000000000..a5bafe95dea --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateRecordAndReverseCommand.java @@ -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; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveDomainCommand.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveDomainCommand.java new file mode 100644 index 00000000000..dffdce14e7a --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveDomainCommand.java @@ -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; + } +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveRecordCommand.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveRecordCommand.java new file mode 100644 index 00000000000..3479b26c39d --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveRecordCommand.java @@ -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; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/SignInCommand.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/SignInCommand.java new file mode 100644 index 00000000000..86f82db0fa8 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/SignInCommand.java @@ -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; + } +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElement.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElement.java new file mode 100644 index 00000000000..c5a184c8f29 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElement.java @@ -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> capabilities = setCapabilities(); + + private static final ConfigKey GloboDNSTemplateId = new ConfigKey("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 GloboDNSOverride = new ConfigKey("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> getCapabilities() { + return capabilities; + } + + private static Map> setCapabilities() { + Map> caps = new HashMap>(); + Map dnsCapabilities = new HashMap(); + // 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 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> getCommands() { + List> cmdList = new ArrayList>(); + 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 details, List 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 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 params = new HashMap(); + 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 hostDetails = new HashMap(); + hostDetails.putAll(params); + + Host host = Transaction.execute(new TransactionCallbackWithException() { + + @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; + } +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElementService.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElementService.java new file mode 100644 index 00000000000..c141ad1c8e4 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElementService.java @@ -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); +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/resource/GloboDnsResource.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/resource/GloboDnsResource.java new file mode 100644 index 00000000000..c1678458b61 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/resource/GloboDnsResource.java @@ -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 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 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 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; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainListResponse.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainListResponse.java new file mode 100644 index 00000000000..7bddab8a7aa --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainListResponse.java @@ -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 domainList; + + public GloboDnsDomainListResponse(Command command, List domainList) { + super(command, true, null); + this.domainList = domainList; + } + + public List getDomainList() { + return domainList; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainResponse.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainResponse.java new file mode 100644 index 00000000000..23b8f3fe2d1 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainResponse.java @@ -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; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsExportResponse.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsExportResponse.java new file mode 100644 index 00000000000..a51f540c49f --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsExportResponse.java @@ -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; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordListResponse.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordListResponse.java new file mode 100644 index 00000000000..edd1bb7daef --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordListResponse.java @@ -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 recordList; + + public GloboDnsRecordListResponse(Command command, List recordList) { + super(command, true, null); + this.recordList = recordList; + } + + public List getRecordList() { + return recordList; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordResponse.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordResponse.java new file mode 100644 index 00000000000..9236d11ec79 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordResponse.java @@ -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; + } + +} diff --git a/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/element/GloboDnsElementTest.java b/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/element/GloboDnsElementTest.java new file mode 100644 index 00000000000..7c05622be86 --- /dev/null +++ b/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/element/GloboDnsElementTest.java @@ -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() { + + @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() { + + @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); + } + } + } +} diff --git a/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/resource/GloboDnsResourceTest.java b/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/resource/GloboDnsResourceTest.java new file mode 100644 index 00000000000..0fd0e115640 --- /dev/null +++ b/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/resource/GloboDnsResourceTest.java @@ -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 params = new HashMap(); + 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 domainList = new ArrayList(); + 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 recordList = new ArrayList(); + 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()); + 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()); + 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 recordList = new ArrayList(); + 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 recordList = new ArrayList(); + 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(); + } + +} diff --git a/plugins/network-elements/globodns/test/resources/db.properties b/plugins/network-elements/globodns/test/resources/db.properties new file mode 100644 index 00000000000..0dd35f64726 --- /dev/null +++ b/plugins/network-elements/globodns/test/resources/db.properties @@ -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 diff --git a/plugins/network-elements/globodns/test/resources/log4j.properties b/plugins/network-elements/globodns/test/resources/log4j.properties new file mode 100644 index 00000000000..32322a01212 --- /dev/null +++ b/plugins/network-elements/globodns/test/resources/log4j.properties @@ -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 diff --git a/plugins/pom.xml b/plugins/pom.xml index 0b1b62d9ac6..b190ee5b135 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -88,6 +88,7 @@ alert-handlers/syslog-alerts network-elements/internal-loadbalancer network-elements/vxlan + network-elements/globodns diff --git a/ui/scripts/system.js b/ui/scripts/system.js index fe51acb42f3..fdd0f9ad3bf 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -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 ) {