cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bhais...@apache.org
Subject [2/3] git commit: updated refs/heads/master to 773c5e9
Date Thu, 21 Aug 2014 10:19:55 GMT
CLOUDSTACK-6998: GloboDNS, Integration with external DNS Provider

This is a feature to handle DNS entries by means of an external DNS Provider,
such as Bind. These entries include DNS domains and reverse domains, VM records
and reverse records.

For a complete description, please refer to the design document available at
https://cwiki.apache.org/confluence/display/CLOUDSTACK/Bind+and+PowerDNS+integration+by+Globo+DNSAPI

For the discussion about this feature on the dev mailing list, please refer to
http://markmail.org/thread/fvwf36hpxotiibka

Summary:
- new Network Service Provider called GloboDNS
- new Network Element to manage network domains and VM records (entries) on an external API
- new Network Resource to communicate with GloboDNS (open source)
- new API command to add DNS server
- new global option to determine if this provider should override VM entries on external DNS server
- changes in UI to include GloboDNS in Providers list

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/233445ed
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/233445ed
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/233445ed

Branch: refs/heads/master
Commit: 233445ed68e3f903367e14a277ec2a5de9597b52
Parents: 7ada4ad
Author: Daniel Vega <daniel.simoes@corp.globo.com>
Authored: Wed Aug 20 15:49:40 2014 -0300
Committer: Rohit Yadav <rohit.yadav@shapeblue.com>
Committed: Thu Aug 21 11:54:44 2014 +0200

----------------------------------------------------------------------
 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 ++
 .../cloudstack/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(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/api/src/com/cloud/network/Network.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java
index 55502df..c5a9bf2 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<Network.State>, I
         public static final Provider NuageVsp = new Provider("NuageVsp", false);
         public static final Provider NuageVspVpc = new Provider("NuageVspVpc", false);
         public static final Provider BrocadeVcs = new Provider("BrocadeVcs", false);
+        // add GloboDns provider
+        public static final Provider GloboDns = new Provider("GloboDns", true);
 
         private final String name;
         private final boolean isExternal;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java
index f38e9e6..b349356 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;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/client/pom.xml
----------------------------------------------------------------------
diff --git a/client/pom.xml b/client/pom.xml
index d87fc45..dccf18d 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -346,6 +346,11 @@
         <artifactId>cloud-plugin-api-solidfire-intg-test</artifactId>
         <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-plugin-network-globodns</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
   <build>
     <plugins>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/client/tomcatconf/commands.properties.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index 3191bbf..18ac2bf 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

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/network-elements/globodns/pom.xml b/plugins/network-elements/globodns/pom.xml
new file mode 100644
index 0000000..0f17bba
--- /dev/null
+++ b/plugins/network-elements/globodns/pom.xml
@@ -0,0 +1,37 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cloud-plugin-network-globodns</artifactId>
+  <name>Apache CloudStack Plugin - GloboDNS</name>
+  <parent>
+    <groupId>org.apache.cloudstack</groupId>
+    <artifactId>cloudstack-plugins</artifactId>
+    <version>4.5.0-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+  <dependencies>
+    <dependency>
+      <groupId>com.globo.globodns</groupId>
+      <artifactId>globodns-client</artifactId>
+      <version>0.0.15</version>
+    </dependency>
+  </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/module.properties
----------------------------------------------------------------------
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 0000000..6c74bd2
--- /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

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/spring-globodns-context.xml
----------------------------------------------------------------------
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 0000000..7e2e809
--- /dev/null
+++ b/plugins/network-elements/globodns/resources/META-INF/cloudstack/globodns/spring-globodns-context.xml
@@ -0,0 +1,22 @@
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
+	license agreements. See the NOTICE file distributed with this work for additional
+	information regarding copyright ownership. The ASF licenses this file to
+	you under the Apache License, Version 2.0 (the "License"); you may not use
+	this file except in compliance with the License. You may obtain a copy of
+	the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
+	by applicable law or agreed to in writing, software distributed under the
+	License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+	OF ANY KIND, either express or implied. See the License for the specific
+	language governing permissions and limitations under the License. -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+	xmlns:aop="http://www.springframework.org/schema/aop"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+                      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
+                      http://www.springframework.org/schema/context
+                      http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+	<context:component-scan base-package="com.globo.globodns.cloudstack" />
+
+</beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/api/AddGloboDnsHostCmd.java
----------------------------------------------------------------------
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 0000000..1694a32
--- /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";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateDomainCommand.java
----------------------------------------------------------------------
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 0000000..83564cb
--- /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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateRecordAndReverseCommand.java
----------------------------------------------------------------------
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 0000000..a5bafe9
--- /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;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveDomainCommand.java
----------------------------------------------------------------------
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 0000000..dffdce1
--- /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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveRecordCommand.java
----------------------------------------------------------------------
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 0000000..3479b26
--- /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;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/SignInCommand.java
----------------------------------------------------------------------
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 0000000..86f82db
--- /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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElement.java
----------------------------------------------------------------------
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 0000000..c5a184c
--- /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<Service, Map<Capability, String>> capabilities = setCapabilities();
+
+    private static final ConfigKey<Long> GloboDNSTemplateId = new ConfigKey<Long>("Advanced", Long.class, "globodns.domain.templateid", "1",
+            "Template id to be used when creating domains in GloboDNS", true, ConfigKey.Scope.Global);
+    private static final ConfigKey<Boolean> GloboDNSOverride = new ConfigKey<Boolean>("Advanced", Boolean.class, "globodns.override.entries", "true",
+            "Allow GloboDns to override entries that already exist", true, ConfigKey.Scope.Global);
+
+    // DAOs
+    @Inject
+    DataCenterDao _dcDao;
+    @Inject
+    HostDao _hostDao;
+    @Inject
+    PhysicalNetworkDao _physicalNetworkDao;
+
+    // Managers
+    @Inject
+    AgentManager _agentMgr;
+    @Inject
+    ResourceManager _resourceMgr;
+
+    protected boolean isTypeSupported(VirtualMachine.Type type) {
+        return type == VirtualMachine.Type.User || type == VirtualMachine.Type.ConsoleProxy || type == VirtualMachine.Type.DomainRouter;
+    }
+
+    @Override
+    @DB
+    public boolean implement(final Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException,
+            ResourceUnavailableException, InsufficientCapacityException {
+
+        Long zoneId = network.getDataCenterId();
+        DataCenter zone = _dcDao.findById(zoneId);
+        if (zone == null) {
+            throw new CloudRuntimeException("Could not find zone associated to this network");
+        }
+        CreateOrUpdateDomainCommand cmd = new CreateOrUpdateDomainCommand(network.getNetworkDomain(), GloboDNSTemplateId.value());
+        callCommand(cmd, zoneId);
+        return true;
+    }
+
+    protected String hostNameOfVirtualMachine(VirtualMachineProfile vm) {
+        return vm.getHostName().toLowerCase();
+    }
+
+    @Override
+    @DB
+    public boolean prepare(final Network network, final NicProfile nic, final VirtualMachineProfile vm, DeployDestination dest, ReservationContext context)
+            throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
+
+        if (!isTypeSupported(vm.getType())) {
+            s_logger.info("GloboDNS only manages records for VMs of type User, ConsoleProxy and DomainRouter. VM " + vm + " is " + vm.getType());
+            return false;
+        }
+
+        Long zoneId = network.getDataCenterId();
+        final DataCenter zone = _dcDao.findById(zoneId);
+        if (zone == null) {
+            throw new CloudRuntimeException("Could not find zone associated to this network");
+        }
+
+        /* Create new A record in GloboDNS */
+        // We allow only lower case names in DNS, so force lower case names for VMs
+        String vmName = vm.getHostName();
+        String vmHostname = hostNameOfVirtualMachine(vm);
+        if (!vmName.equals(vmHostname) && vm.getType() == VirtualMachine.Type.User) {
+            throw new InvalidParameterValueException("VM name should contain only lower case letters and digits: " + vmName + " - " + vm);
+        }
+
+        CreateOrUpdateRecordAndReverseCommand cmd = new CreateOrUpdateRecordAndReverseCommand(vmHostname, nic.getIp4Address(), network.getNetworkDomain(),
+                GloboDNSTemplateId.value(), GloboDNSOverride.value());
+        callCommand(cmd, zoneId);
+        return true;
+    }
+
+    @Override
+    @DB
+    public boolean release(final Network network, NicProfile nic, final VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException,
+            ResourceUnavailableException {
+
+        if (!isTypeSupported(vm.getType())) {
+            s_logger.info("GloboDNS only manages records for VMs of type User, ConsoleProxy and DomainRouter. VM " + vm + " is " + vm.getType());
+            return false;
+        }
+
+        Long zoneId = network.getDataCenterId();
+        final DataCenter zone = _dcDao.findById(zoneId);
+        if (zone == null) {
+            throw new CloudRuntimeException("Could not find zone associated to this network");
+        }
+
+        RemoveRecordCommand cmd = new RemoveRecordCommand(hostNameOfVirtualMachine(vm), nic.getIp4Address(), network.getNetworkDomain(), GloboDNSOverride.value());
+        callCommand(cmd, zoneId);
+        return true;
+    }
+
+    @Override
+    public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    @DB
+    public boolean destroy(final Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
+        Long zoneId = network.getDataCenterId();
+        final DataCenter zone = _dcDao.findById(zoneId);
+        if (zone == null) {
+            throw new CloudRuntimeException("Could not find zone associated to this network");
+        }
+
+        RemoveDomainCommand cmd = new RemoveDomainCommand(network.getNetworkDomain(), GloboDNSOverride.value());
+        callCommand(cmd, zoneId);
+        return true;
+    }
+
+    ///////// Provider control methods ////////////
+    private Answer callCommand(Command cmd, Long zoneId) {
+
+        HostVO globoDnsHost = getGloboDnsHost(zoneId);
+        if (globoDnsHost == null) {
+            throw new CloudRuntimeException("Could not find the GloboDNS resource");
+        }
+
+        Answer answer = _agentMgr.easySend(globoDnsHost.getId(), cmd);
+        if (answer == null || !answer.getResult()) {
+            String msg = "Error executing command " + cmd;
+            msg = answer == null ? msg : answer.getDetails();
+            throw new CloudRuntimeException(msg);
+        }
+
+        return answer;
+    }
+
+    @Override
+    public Map<Service, Map<Capability, String>> getCapabilities() {
+        return capabilities;
+    }
+
+    private static Map<Service, Map<Capability, String>> setCapabilities() {
+        Map<Service, Map<Capability, String>> caps = new HashMap<Service, Map<Capability, String>>();
+        Map<Capability, String> dnsCapabilities = new HashMap<Capability, String>();
+        // FIXME
+        dnsCapabilities.put(Capability.AllowDnsSuffixModification, "true");
+        caps.put(Service.Dns, dnsCapabilities);
+        return caps;
+    }
+
+    @Override
+    public Provider getProvider() {
+        return Provider.GloboDns;
+    }
+
+    @Override
+    public boolean isReady(PhysicalNetworkServiceProvider provider) {
+        return true;
+    }
+
+    @Override
+    public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
+        PhysicalNetwork pNtwk = _physicalNetworkDao.findById(provider.getPhysicalNetworkId());
+        Host host = getGloboDnsHost(pNtwk.getDataCenterId());
+        if (host != null) {
+            _resourceMgr.deleteHost(host.getId(), true, false);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean canEnableIndividualServices() {
+        return true;
+    }
+
+    @Override
+    public boolean verifyServicesCombination(Set<Service> services) {
+        return true;
+    }
+
+    ////// Configurable methods /////////////
+    @Override
+    public String getConfigComponentName() {
+        return GloboDnsElement.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {GloboDNSTemplateId, GloboDNSOverride};
+    }
+
+    ////////// Resource/Host methods ////////////
+    @Override
+    public List<Class<?>> getCommands() {
+        List<Class<?>> cmdList = new ArrayList<Class<?>>();
+        cmdList.add(AddGloboDnsHostCmd.class);
+        return cmdList;
+    }
+
+    @Override
+    public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
+        return null;
+    }
+
+    @Override
+    public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map<String, String> details, List<String> hostTags) {
+        if (!(startup[0] instanceof StartupCommand && resource instanceof GloboDnsResource)) {
+            return null;
+        }
+        host.setType(Host.Type.L2Networking);
+        return host;
+    }
+
+    @Override
+    public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException {
+        if (!(host.getType() == Host.Type.L2Networking)) {
+            return null;
+        }
+        return new DeleteHostAnswer(true);
+    }
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+        super.configure(name, params);
+        _resourceMgr.registerResourceStateAdapter(name, this);
+        return true;
+    }
+
+    private HostVO getGloboDnsHost(Long zoneId) {
+        return _hostDao.findByTypeNameAndZoneId(zoneId, Provider.GloboDns.getName(), Type.L2Networking);
+    }
+
+    @Override
+    @DB
+    public Host addGloboDnsHost(Long physicalNetworkId, final String username, final String password, String url) {
+
+        if (username == null || username.trim().isEmpty()) {
+            throw new InvalidParameterValueException("Invalid username: " + username);
+        }
+
+        if (password == null || password.trim().isEmpty()) {
+            throw new InvalidParameterValueException("Invalid password: " + password);
+        }
+
+        if (url == null || url.trim().isEmpty()) {
+            throw new InvalidParameterValueException("Invalid url: " + url);
+        }
+
+        // validate physical network and zone
+        // Check if physical network exists
+        PhysicalNetwork pNtwk = null;
+        if (physicalNetworkId != null) {
+            pNtwk = _physicalNetworkDao.findById(physicalNetworkId);
+            if (pNtwk == null) {
+                throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id");
+            }
+        } else {
+            throw new InvalidParameterValueException("Invalid physicalNetworkId: " + physicalNetworkId);
+        }
+
+        final Long zoneId = pNtwk.getDataCenterId();
+
+        final Map<String, String> params = new HashMap<String, String>();
+        params.put("guid", "globodns-" + String.valueOf(zoneId));
+        params.put("zoneId", String.valueOf(zoneId));
+        params.put("name", Provider.GloboDns.getName());
+
+        params.put("url", url);
+        params.put("username", username);
+        params.put("password", password);
+
+        final Map<String, Object> hostDetails = new HashMap<String, Object>();
+        hostDetails.putAll(params);
+
+        Host host = Transaction.execute(new TransactionCallbackWithException<Host, CloudRuntimeException>() {
+
+            @Override
+            public Host doInTransaction(TransactionStatus status) throws CloudRuntimeException {
+                try {
+                    GloboDnsResource resource = new GloboDnsResource();
+                    resource.configure(Provider.GloboDns.getName(), hostDetails);
+
+                    Host host = _resourceMgr.addHost(zoneId, resource, resource.getType(), params);
+
+                    if (host == null) {
+                        throw new CloudRuntimeException("Failed to add GloboDNS host");
+                    }
+
+                    // Validate username and password by logging in
+                    SignInCommand cmd = new SignInCommand(username, password);
+                    Answer answer = callCommand(cmd, zoneId);
+                    if (answer == null || !answer.getResult()) {
+                        // Could not sign in on GloboDNS
+                        throw new ConfigurationException("Could not sign in on GloboDNS. Please verify URL, username and password.");
+                    }
+
+                    return host;
+                } catch (ConfigurationException e) {
+                    throw new CloudRuntimeException(e);
+                }
+            }
+        });
+
+        return host;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElementService.java
----------------------------------------------------------------------
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 0000000..c141ad1
--- /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);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/resource/GloboDnsResource.java
----------------------------------------------------------------------
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 0000000..c167845
--- /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<String, Object> params) throws ConfigurationException {
+
+        _zoneId = (String)params.get("zoneId");
+        if (_zoneId == null) {
+            throw new ConfigurationException("Unable to find zone");
+        }
+
+        _guid = (String)params.get("guid");
+        if (_guid == null) {
+            throw new ConfigurationException("Unable to find guid");
+        }
+
+        _name = (String)params.get("name");
+        if (_name == null) {
+            throw new ConfigurationException("Unable to find name");
+        }
+
+        _url = (String)params.get("url");
+        if (_url == null) {
+            throw new ConfigurationException("Unable to find url");
+        }
+
+        _username = (String)params.get("username");
+        if (_username == null) {
+            throw new ConfigurationException("Unable to find username");
+        }
+
+        _password = (String)params.get("password");
+        if (_password == null) {
+            throw new ConfigurationException("Unable to find password");
+        }
+
+        _globoDns = GloboDns.buildHttpApi(_url, _username, _password);
+
+        return true;
+    }
+
+    @Override
+    public boolean start() {
+        return true;
+    }
+
+    @Override
+    public boolean stop() {
+        return true;
+    }
+
+    @Override
+    public Type getType() {
+        return Host.Type.L2Networking;
+    }
+
+    @Override
+    public StartupCommand[] initialize() {
+        s_logger.trace("initialize called");
+        StartupCommand cmd = new StartupCommand(getType());
+        cmd.setName(_name);
+        cmd.setGuid(_guid);
+        cmd.setDataCenter(_zoneId);
+        cmd.setPod("");
+        cmd.setPrivateIpAddress("");
+        cmd.setStorageIpAddress("");
+        cmd.setVersion(GloboDnsResource.class.getPackage().getImplementationVersion());
+        return new StartupCommand[] {cmd};
+    }
+
+    @Override
+    public PingCommand getCurrentStatus(long id) {
+        return new PingCommand(getType(), id);
+    }
+
+    @Override
+    public void disconnected() {
+        return;
+    }
+
+    @Override
+    public IAgentControl getAgentControl() {
+        return null;
+    }
+
+    @Override
+    public void setAgentControl(IAgentControl agentControl) {
+        return;
+    }
+
+    @Override
+    public Answer executeRequest(Command cmd) {
+        if (cmd instanceof ReadyCommand) {
+            return new ReadyAnswer((ReadyCommand)cmd);
+        } else if (cmd instanceof MaintainCommand) {
+            return new MaintainAnswer((MaintainCommand)cmd);
+        } else if (cmd instanceof SignInCommand) {
+            return execute((SignInCommand)cmd);
+        } else if (cmd instanceof RemoveDomainCommand) {
+            return execute((RemoveDomainCommand)cmd);
+        } else if (cmd instanceof RemoveRecordCommand) {
+            return execute((RemoveRecordCommand)cmd);
+        } else if (cmd instanceof CreateOrUpdateDomainCommand) {
+            return execute((CreateOrUpdateDomainCommand)cmd);
+        } else if (cmd instanceof CreateOrUpdateRecordAndReverseCommand) {
+            return execute((CreateOrUpdateRecordAndReverseCommand)cmd);
+        }
+        return Answer.createUnsupportedCommandAnswer(cmd);
+    }
+
+    public Answer execute(SignInCommand cmd) {
+        try {
+            Authentication auth = _globoDns.getAuthAPI().signIn(cmd.getEmail(), cmd.getPassword());
+            if (auth != null) {
+                return new Answer(cmd, true, "Signed in successfully");
+            } else {
+                return new Answer(cmd, false, "Unable to sign in on GloboDNS");
+            }
+        } catch (GloboDnsException e) {
+            return new Answer(cmd, false, e.getMessage());
+        }
+    }
+
+    public Answer execute(RemoveDomainCommand cmd) {
+        try {
+            Domain domain = searchDomain(cmd.getNetworkDomain(), false);
+            if (domain != null) {
+                if (!cmd.isOverride()) {
+                    for (Record record : _globoDns.getRecordAPI().listAll(domain.getId())) {
+                        if (record.getTypeNSRecordAttributes().getId() == null) {
+                            s_logger.warn("There are records in domain " + cmd.getNetworkDomain() + " and override is not enable. I will not delete this domain.");
+                            return new Answer(cmd, true, "Domain keeped");
+                        }
+                    }
+                }
+                _globoDns.getDomainAPI().removeDomain(domain.getId());
+                scheduleExportChangesToBind();
+            } else {
+                s_logger.warn("Domain " + cmd.getNetworkDomain() + " already been deleted.");
+            }
+
+            return new Answer(cmd, true, "Domain removed");
+        } catch (GloboDnsException e) {
+            return new Answer(cmd, false, e.getMessage());
+        }
+    }
+
+    public Answer execute(RemoveRecordCommand cmd) {
+        boolean needsExport = false;
+        try {
+            if (removeRecord(cmd.getRecordName(), cmd.getRecordIp(), cmd.getNetworkDomain(), false, cmd.isOverride())) {
+                needsExport = true;
+            }
+
+            // remove reverse
+            String reverseGloboDnsName = generateReverseDomainNameFromNetworkIp(cmd.getRecordIp());
+            String reverseRecordName = generateReverseRecordNameFromNetworkIp(cmd.getRecordIp());
+            String reverseRecordContent = cmd.getRecordName() + '.' + cmd.getNetworkDomain();
+
+            if (removeRecord(reverseRecordName, reverseRecordContent, reverseGloboDnsName, true, cmd.isOverride())) {
+                needsExport = true;
+            }
+
+            return new Answer(cmd, true, "Record removed");
+        } catch (GloboDnsException e) {
+            return new Answer(cmd, false, e.getMessage());
+        } finally {
+            if (needsExport) {
+                scheduleExportChangesToBind();
+            }
+        }
+    }
+
+    public Answer execute(CreateOrUpdateRecordAndReverseCommand cmd) {
+        boolean needsExport = false;
+        try {
+            Domain domain = searchDomain(cmd.getNetworkDomain(), false);
+            if (domain == null) {
+                domain = _globoDns.getDomainAPI().createDomain(cmd.getNetworkDomain(), cmd.getReverseTemplateId(), DEFAULT_AUTHORITY_TYPE);
+                s_logger.warn("Domain " + cmd.getNetworkDomain() + " doesn't exist, maybe someone removed it. It was automatically created with template "
+                        + cmd.getReverseTemplateId());
+            }
+
+            boolean created = createOrUpdateRecord(domain.getId(), cmd.getRecordName(), cmd.getRecordIp(), IPV4_RECORD_TYPE, cmd.isOverride());
+            if (!created) {
+                String msg = "Unable to create record " + cmd.getRecordName() + " at " + cmd.getNetworkDomain();
+                if (!cmd.isOverride()) {
+                    msg += ". Override record option is false, maybe record already exist.";
+                }
+                return new Answer(cmd, false, msg);
+            } else {
+                needsExport = true;
+            }
+
+            String reverseRecordContent = cmd.getRecordName() + '.' + cmd.getNetworkDomain();
+            if (createOrUpdateReverse(cmd.getRecordIp(), reverseRecordContent, cmd.getReverseTemplateId(), cmd.isOverride())) {
+                needsExport = true;
+            } else {
+                if (!cmd.isOverride()) {
+                    String msg = "Unable to create reverse record " + cmd.getRecordName() + " for ip " + cmd.getRecordIp();
+                    msg += ". Override record option is false, maybe record already exist.";
+                    return new Answer(cmd, false, msg);
+                }
+            }
+
+            return new Answer(cmd);
+        } catch (GloboDnsException e) {
+            return new Answer(cmd, false, e.getMessage());
+        } finally {
+            if (needsExport) {
+                scheduleExportChangesToBind();
+            }
+        }
+    }
+
+    protected boolean createOrUpdateReverse(String networkIp, String reverseRecordContent, Long templateId, boolean override) {
+        String reverseDomainName = generateReverseDomainNameFromNetworkIp(networkIp);
+        Domain reverseDomain = searchDomain(reverseDomainName, true);
+        if (reverseDomain == null) {
+            reverseDomain = _globoDns.getDomainAPI().createReverseDomain(reverseDomainName, templateId, DEFAULT_AUTHORITY_TYPE);
+            s_logger.info("Created reverse domain " + reverseDomainName + " with template " + templateId);
+        }
+
+        // create reverse
+        String reverseRecordName = generateReverseRecordNameFromNetworkIp(networkIp);
+        return createOrUpdateRecord(reverseDomain.getId(), reverseRecordName, reverseRecordContent, REVERSE_RECORD_TYPE, override);
+    }
+
+    public Answer execute(CreateOrUpdateDomainCommand cmd) {
+
+        boolean needsExport = false;
+        try {
+            Domain domain = searchDomain(cmd.getDomainName(), false);
+            if (domain == null) {
+                // create
+                domain = _globoDns.getDomainAPI().createDomain(cmd.getDomainName(), cmd.getTemplateId(), DEFAULT_AUTHORITY_TYPE);
+                s_logger.info("Created domain " + cmd.getDomainName() + " with template " + cmd.getTemplateId());
+                if (domain == null) {
+                    return new Answer(cmd, false, "Unable to create domain " + cmd.getDomainName());
+                } else {
+                    needsExport = true;
+                }
+            } else {
+                s_logger.warn("Domain " + cmd.getDomainName() + " already exist.");
+            }
+            return new Answer(cmd);
+        } catch (GloboDnsException e) {
+            return new Answer(cmd, false, e.getMessage());
+        } finally {
+            if (needsExport) {
+                scheduleExportChangesToBind();
+            }
+        }
+    }
+
+    /**
+     * Try to remove a record from bindZoneName. If record was removed returns true.
+     * @param recordName
+     * @param bindZoneName
+     * @return true if record exists and was removed.
+     */
+    protected boolean removeRecord(String recordName, String recordValue, String bindZoneName, boolean reverse, boolean override) {
+        Domain domain = searchDomain(bindZoneName, reverse);
+        if (domain == null) {
+            s_logger.warn("Domain " + bindZoneName + " doesn't exists in GloboDNS. Record " + recordName + " has already been removed.");
+            return false;
+        }
+        Record record = searchRecord(recordName, domain.getId());
+        if (record == null) {
+            s_logger.warn("Record " + recordName + " in domain " + bindZoneName + " has already been removed.");
+            return false;
+        } else {
+            if (!override && !record.getContent().equals(recordValue)) {
+                s_logger.warn("Record " + recordName + " in domain " + bindZoneName + " have different value from " + recordValue
+                        + " and override is not enable. I will not delete it.");
+                return false;
+            }
+            _globoDns.getRecordAPI().removeRecord(record.getId());
+        }
+
+        return true;
+    }
+
+    /**
+     * Create a new record in Zone, or update it if record has been exists.
+     * @param domainId
+     * @param name
+     * @param ip
+     * @param type
+     * @return if record was created or updated.
+     */
+    private boolean createOrUpdateRecord(Long domainId, String name, String ip, String type, boolean override) {
+        Record record = this.searchRecord(name, domainId);
+        if (record == null) {
+            // Create new record
+            record = _globoDns.getRecordAPI().createRecord(domainId, name, ip, type);
+            s_logger.info("Created record " + name + " in domain " + domainId);
+        } else {
+            if (!ip.equals(record.getContent())) {
+                if (Boolean.TRUE.equals(override)) {
+                    // ip is incorrect. Fix.
+                    _globoDns.getRecordAPI().updateRecord(record.getId(), domainId, name, ip);
+                } else {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * GloboDns export all changes to Bind server.
+     */
+    public void scheduleExportChangesToBind() {
+        try {
+            Export export = _globoDns.getExportAPI().scheduleExport();
+            if (export != null) {
+                s_logger.info("GloboDns Export: " + export.getResult());
+            }
+        } catch (GloboDnsException e) {
+            s_logger.warn("Error on scheduling export. Although everything was persist, someone need to manually force export in GloboDns", e);
+        }
+    }
+
+    /**
+     * Try to find bindZoneName in GloboDns.
+     * @param name
+     * @return Domain object or null if domain not exists.
+     */
+    private Domain searchDomain(String name, boolean reverse) {
+        if (name == null) {
+            return null;
+        }
+        List<Domain> candidates;
+        if (reverse) {
+            candidates = _globoDns.getDomainAPI().listReverseByQuery(name);
+        } else {
+            candidates = _globoDns.getDomainAPI().listByQuery(name);
+        }
+        for (Domain candidate : candidates) {
+            if (name.equals(candidate.getName())) {
+                return candidate;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Find recordName in domain.
+     * @param recordName
+     * @param domainId Id of BindZoneName. Maybe you need use searchDomain before to use BindZoneName.
+     * @return Record or null if not exists.
+     */
+    private Record searchRecord(String recordName, Long domainId) {
+        if (recordName == null || domainId == null) {
+            return null;
+        }
+        List<Record> candidates = _globoDns.getRecordAPI().listByQuery(domainId, recordName);
+        // GloboDns search name in name and content. We need to iterate to check if recordName exists only in name
+        for (Record candidate : candidates) {
+            if (recordName.equalsIgnoreCase(candidate.getName())) {
+                s_logger.debug("Record " + recordName + " in domain id " + domainId + " found in GloboDNS");
+                return candidate;
+            }
+        }
+        s_logger.debug("Record " + recordName + " in domain id " + domainId + " not found in GloboDNS");
+        return null;
+    }
+
+    /**
+     * Generate reverseBindZoneName of network. We ALWAYS use /24.
+     * @param networkIp
+     * @return Bind Zone Name reverse of network specified by networkIp
+     */
+    private String generateReverseDomainNameFromNetworkIp(String networkIp) {
+        String[] octets = networkIp.split("\\.");
+        String reverseDomainName = octets[2] + '.' + octets[1] + '.' + octets[0] + '.' + REVERSE_DOMAIN_SUFFIX;
+        return reverseDomainName;
+    }
+
+    private String generateReverseRecordNameFromNetworkIp(String networkIp) {
+        String[] octets = networkIp.split("\\.");
+        String reverseRecordName = octets[3];
+        return reverseRecordName;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainListResponse.java
----------------------------------------------------------------------
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 0000000..7bddab8
--- /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<Domain> domainList;
+
+    public GloboDnsDomainListResponse(Command command, List<Domain> domainList) {
+        super(command, true, null);
+        this.domainList = domainList;
+    }
+
+    public List<Domain> getDomainList() {
+        return domainList;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsDomainResponse.java
----------------------------------------------------------------------
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 0000000..23b8f3f
--- /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;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsExportResponse.java
----------------------------------------------------------------------
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 0000000..a51f540
--- /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;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordListResponse.java
----------------------------------------------------------------------
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 0000000..edd1bb7
--- /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<Record> recordList;
+
+    public GloboDnsRecordListResponse(Command command, List<Record> recordList) {
+        super(command, true, null);
+        this.recordList = recordList;
+    }
+
+    public List<Record> getRecordList() {
+        return recordList;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/response/GloboDnsRecordResponse.java
----------------------------------------------------------------------
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 0000000..9236d11
--- /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;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/233445ed/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/element/GloboDnsElementTest.java
----------------------------------------------------------------------
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 0000000..7c05622
--- /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<Answer>() {
+
+            @Override
+            public Answer answer(InvocationOnMock invocation) throws Throwable {
+                Command cmd = (Command)invocation.getArguments()[1];
+                return new Answer(cmd);
+            }
+        });
+
+        _globodnsElement.prepare(network, nic, vm, dest, context);
+        verify(_agentMgr, times(1)).easySend(eq(globoDnsHostId), isA(CreateOrUpdateRecordAndReverseCommand.class));
+    }
+
+    @Test
+    public void testReleaseMethodCallResource() throws Exception {
+        Network network = mock(Network.class);
+        when(network.getDataCenterId()).thenReturn(zoneId);
+        when(network.getId()).thenReturn(1l);
+        NicProfile nic = new NicProfile();
+        nic.setIp4Address("10.11.12.13");
+        VirtualMachineProfile vm = mock(VirtualMachineProfile.class);
+        when(vm.getHostName()).thenReturn("vm-name");
+        when(vm.getType()).thenReturn(VirtualMachine.Type.User);
+        DataCenterVO dataCenterVO = mock(DataCenterVO.class);
+        when(dataCenterVO.getId()).thenReturn(zoneId);
+        when(_datacenterDao.findById(zoneId)).thenReturn(dataCenterVO);
+        ReservationContext context = new ReservationContextImpl(null, null, user);
+
+        HostVO hostVO = mock(HostVO.class);
+        when(hostVO.getId()).thenReturn(globoDnsHostId);
+        when(_hostDao.findByTypeNameAndZoneId(eq(zoneId), eq(Provider.GloboDns.getName()), eq(Type.L2Networking))).thenReturn(hostVO);
+
+        when(_agentMgr.easySend(eq(globoDnsHostId), isA(RemoveRecordCommand.class))).then(new org.mockito.stubbing.Answer<Answer>() {
+
+            @Override
+            public Answer answer(InvocationOnMock invocation) throws Throwable {
+                Command cmd = (Command)invocation.getArguments()[1];
+                return new Answer(cmd);
+            }
+        });
+
+        _globodnsElement.release(network, nic, vm, context);
+        verify(_agentMgr, times(1)).easySend(eq(globoDnsHostId), isA(RemoveRecordCommand.class));
+    }
+
+    @Configuration
+    @ComponentScan(basePackageClasses = {GloboDnsElement.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, useDefaultFilters = false)
+    public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration {
+
+        @Bean
+        public HostDao hostDao() {
+            return mock(HostDao.class);
+        }
+
+        @Bean
+        public DataCenterDao dataCenterDao() {
+            return mock(DataCenterDao.class);
+        }
+
+        @Bean
+        public PhysicalNetworkDao physicalNetworkDao() {
+            return mock(PhysicalNetworkDao.class);
+        }
+
+        @Bean
+        public NetworkDao networkDao() {
+            return mock(NetworkDao.class);
+        }
+
+        @Bean
+        public ConfigurationDao configurationDao() {
+            return mock(ConfigurationDao.class);
+        }
+
+        @Bean
+        public AgentManager agentManager() {
+            return mock(AgentManager.class);
+        }
+
+        @Bean
+        public ResourceManager resourceManager() {
+            return mock(ResourceManager.class);
+        }
+
+        @Bean
+        public AccountManager accountManager() {
+            return mock(AccountManager.class);
+        }
+
+        public static class Library implements TypeFilter {
+
+            @Override
+            public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException {
+                ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class);
+                return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs);
+            }
+        }
+    }
+}


Mime
View raw message