You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by al...@apache.org on 2012/06/21 00:48:03 UTC

[2/4] git commit: Initial checkin for resource tags feature

Initial checkin for resource tags feature

Conflicts:

	api/src/com/cloud/api/ApiConstants.java
	client/tomcatconf/commands.properties.in
	server/src/com/cloud/api/ApiDBUtils.java
	server/src/com/cloud/uuididentity/dao/IdentityDao.java
	server/src/com/cloud/uuididentity/dao/IdentityDaoImpl.java
	setup/db/create-schema.sql


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

Branch: refs/heads/master
Commit: 62d45b9670520a1ee8b520509393d4258c689b50
Parents: 01f172c
Author: Alena Prokharchyk <al...@citrix.com>
Authored: Wed Jun 6 17:21:38 2012 -0700
Committer: Alena Prokharchyk <al...@citrix.com>
Committed: Wed Jun 20 15:45:08 2012 -0700

----------------------------------------------------------------------
 api/src/com/cloud/api/ApiConstants.java            |    2 +
 api/src/com/cloud/api/BaseCmd.java                 |    3 +
 api/src/com/cloud/api/ResponseGenerator.java       |   10 +
 api/src/com/cloud/api/commands/CreateTagsCmd.java  |  123 ++++++
 api/src/com/cloud/api/commands/ListTagsCmd.java    |   87 ++++
 .../cloud/api/response/ResourceTagResponse.java    |   94 +++++
 api/src/com/cloud/configuration/Resource.java      |   16 +
 api/src/com/cloud/event/EventTypes.java            |    5 +
 api/src/com/cloud/server/ResourceTag.java          |   48 +++
 .../com/cloud/server/TaggedResourceService.java    |   49 +++
 client/tomcatconf/commands.properties.in           |    7 +-
 server/src/com/cloud/api/ApiDBUtils.java           |   10 +
 server/src/com/cloud/api/ApiResponseHelper.java    |   44 ++-
 .../configuration/DefaultComponentLibrary.java     |   14 +-
 server/src/com/cloud/tags/ResourceTagVO.java       |  139 +++++++
 .../com/cloud/tags/TaggedResourceManagerImpl.java  |  251 ++++++++++++
 server/src/com/cloud/tags/dao/ResourceTagDao.java  |   23 +
 .../com/cloud/tags/dao/ResourceTagsDaoImpl.java    |   29 ++
 .../com/cloud/uuididentity/dao/IdentityDao.java    |   26 +-
 .../cloud/uuididentity/dao/IdentityDaoImpl.java    |  318 ++++++++-------
 setup/db/create-schema.sql                         |   18 +
 wscript                                            |    1 +
 22 files changed, 1155 insertions(+), 162 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/api/ApiConstants.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/ApiConstants.java b/api/src/com/cloud/api/ApiConstants.java
index 7872cba..54bfaff 100755
--- a/api/src/com/cloud/api/ApiConstants.java
+++ b/api/src/com/cloud/api/ApiConstants.java
@@ -358,6 +358,8 @@ public class ApiConstants {
     public static final String VSM_DEVICE_STATE = "vsmdevicestate";
     public static final String ADD_VSM_FLAG = "addvsmflag";
     public static final String CAN_USE_FOR_DEPLOY = "canusefordeploy";
+    public static final String RESOURCE_IDS = "resourceids";
+    public static final String RESOURCE_ID = "resourceid";
     
     public enum HostDetails {
         all, capacity, events, stats, min;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/api/BaseCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/BaseCmd.java b/api/src/com/cloud/api/BaseCmd.java
index a9b6f60..0924796 100755
--- a/api/src/com/cloud/api/BaseCmd.java
+++ b/api/src/com/cloud/api/BaseCmd.java
@@ -49,6 +49,7 @@ import com.cloud.projects.Project;
 import com.cloud.projects.ProjectService;
 import com.cloud.resource.ResourceService;
 import com.cloud.server.ManagementService;
+import com.cloud.server.TaggedResourceService;
 import com.cloud.storage.StorageService;
 import com.cloud.storage.snapshot.SnapshotService;
 import com.cloud.template.TemplateService;
@@ -128,6 +129,7 @@ public abstract class BaseCmd {
     public static ResourceLimitService _resourceLimitService;
     public static IdentityService _identityService;
     public static StorageNetworkService _storageNetworkService;
+    public static TaggedResourceService _taggedResourceService;
 
     static void setComponents(ResponseGenerator generator) {
         ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name);
@@ -155,6 +157,7 @@ public abstract class BaseCmd {
         _resourceLimitService = locator.getManager(ResourceLimitService.class);
         _identityService = locator.getManager(IdentityService.class);
         _storageNetworkService = locator.getManager(StorageNetworkService.class);
+        _taggedResourceService = locator.getManager(TaggedResourceService.class);
     }
 
     public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/api/ResponseGenerator.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/ResponseGenerator.java b/api/src/com/cloud/api/ResponseGenerator.java
index 5f55705..864a9e6 100755
--- a/api/src/com/cloud/api/ResponseGenerator.java
+++ b/api/src/com/cloud/api/ResponseGenerator.java
@@ -56,6 +56,7 @@ import com.cloud.api.response.ProviderResponse;
 import com.cloud.api.response.RemoteAccessVpnResponse;
 import com.cloud.api.response.ResourceCountResponse;
 import com.cloud.api.response.ResourceLimitResponse;
+import com.cloud.api.response.ResourceTagResponse;
 import com.cloud.api.response.SecurityGroupResponse;
 import com.cloud.api.response.ServiceOfferingResponse;
 import com.cloud.api.response.ServiceResponse;
@@ -114,6 +115,7 @@ import com.cloud.org.Cluster;
 import com.cloud.projects.Project;
 import com.cloud.projects.ProjectAccount;
 import com.cloud.projects.ProjectInvitation;
+import com.cloud.server.ResourceTag;
 import com.cloud.storage.Snapshot;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.Swift;
@@ -280,4 +282,12 @@ public interface ResponseGenerator {
      * @return
      */
     Long getIdentiyId(String tableName, String token);
+
+    /**
+     * @param resourceTag
+     * @return
+     */
+    ResourceTagResponse createResourceTagResponse(ResourceTag resourceTag);
+
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/api/commands/CreateTagsCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/commands/CreateTagsCmd.java b/api/src/com/cloud/api/commands/CreateTagsCmd.java
new file mode 100644
index 0000000..c0ac974
--- /dev/null
+++ b/api/src/com/cloud/api/commands/CreateTagsCmd.java
@@ -0,0 +1,123 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License.  Citrix Systems, Inc.
+// reserves all rights not expressly granted by 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.
+// 
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.api.commands;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.api.ApiConstants;
+import com.cloud.api.BaseAsyncCmd;
+import com.cloud.api.BaseCmd;
+import com.cloud.api.Implementation;
+import com.cloud.api.Parameter;
+import com.cloud.api.ServerApiException;
+import com.cloud.api.response.SuccessResponse;
+import com.cloud.configuration.Resource;
+import com.cloud.event.EventTypes;
+import com.cloud.server.ResourceTag;
+
+/**
+ * @author Alena Prokharchyk
+ */
+
+@Implementation(description = "Creates resource tag(s)", responseObject = SuccessResponse.class, since = "Burbank")
+public class CreateTagsCmd extends BaseAsyncCmd{
+    public static final Logger s_logger = Logger.getLogger(CreateTagsCmd.class.getName());
+
+    private static final String s_name = "createtagsresponse";
+
+    // ///////////////////////////////////////////////////
+    // ////////////// API parameters /////////////////////
+    // ///////////////////////////////////////////////////
+    
+    @Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, required=true, description = "Map of tags (key/value pairs)")
+    private Map tag;
+    
+    @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.STRING, required=true, description="type of the resource")
+    private String resourceType;
+    
+    @Parameter(name=ApiConstants.RESOURCE_IDS, type=CommandType.LIST, required=true, 
+            collectionType=CommandType.STRING, description="list of resources to create the tags for")
+    private List<String> resourceIds;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    
+    public Resource.TaggedResourceType getResourceType(){
+        return _taggedResourceService.getResourceType(resourceType);
+    } 
+    
+    public Map<String, String> getTags() {
+        Map<String, String> tagsMap = null;
+        if (!tag.isEmpty()) {
+            tagsMap = new HashMap<String, String>();
+            Collection<?> servicesCollection = tag.values();
+            Iterator<?> iter = servicesCollection.iterator();
+            while (iter.hasNext()) {
+                HashMap<String, String> services = (HashMap<String, String>) iter.next();
+                String key = services.get("key");
+                String value = services.get("value");
+                tagsMap.put(key, value);
+            }
+        }
+        return tagsMap;
+    }
+    
+    public List<String> getResourceIds() {
+        return resourceIds;
+    }
+
+    // ///////////////////////////////////////////////////
+    // ///////////// API Implementation///////////////////
+    // ///////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        //FIXME - validate the owner here
+       return 1;
+    }
+
+    @Override
+    public void execute() {
+        List<ResourceTag> tags = _taggedResourceService.createTags(getResourceIds(), getResourceType(), getTags());
+        
+        if (tags != null && !tags.isEmpty()) {
+                SuccessResponse response = new SuccessResponse(getCommandName());
+                this.setResponseObject(response);
+        } else {
+            throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create tags");
+        }
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_TAGS_CREATE;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "creating tags";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/api/commands/ListTagsCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/commands/ListTagsCmd.java b/api/src/com/cloud/api/commands/ListTagsCmd.java
new file mode 100644
index 0000000..80e2a2a
--- /dev/null
+++ b/api/src/com/cloud/api/commands/ListTagsCmd.java
@@ -0,0 +1,87 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License.  Citrix Systems, Inc.
+// reserves all rights not expressly granted by 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.
+// 
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.api.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.cloud.api.ApiConstants;
+import com.cloud.api.BaseListProjectAndAccountResourcesCmd;
+import com.cloud.api.Implementation;
+import com.cloud.api.Parameter;
+import com.cloud.api.response.ListResponse;
+import com.cloud.api.response.ResourceTagResponse;
+import com.cloud.server.ResourceTag;
+
+/**
+ * @author Alena Prokharchyk
+ */
+
+@Implementation(description = "List resource tag(s)", responseObject = ResourceTagResponse.class, since = "Burbank")
+public class ListTagsCmd extends BaseListProjectAndAccountResourcesCmd{
+    private static final String s_name = "listtagsresponse";
+    
+    @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.STRING, description="list by resource type")
+    private String resourceType;
+    
+    @Parameter(name=ApiConstants.RESOURCE_ID, type=CommandType.STRING, description="list by resource id")
+    private String resourceId;
+    
+    @Parameter(name=ApiConstants.KEY, type=CommandType.STRING, description="list by key")
+    private String key;
+    
+    @Parameter(name=ApiConstants.VALUE, type=CommandType.STRING, description="list by value")
+    private String value;
+    
+    
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() {
+      
+      List<? extends ResourceTag> tags = _taggedResourceService.listTags(this);
+      ListResponse<ResourceTagResponse> response = new ListResponse<ResourceTagResponse>();
+      List<ResourceTagResponse> tagResponses = new ArrayList<ResourceTagResponse>();
+      for (ResourceTag tag : tags) {
+          ResourceTagResponse tagResponse = _responseGenerator.createResourceTagResponse(tag);
+          tagResponses.add(tagResponse);
+      }
+      response.setResponses(tagResponses);
+      
+      response.setResponseName(getCommandName());
+      this.setResponseObject(response);
+    }
+
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    public String getResourceId() {
+        return resourceId;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/api/response/ResourceTagResponse.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/response/ResourceTagResponse.java b/api/src/com/cloud/api/response/ResourceTagResponse.java
new file mode 100644
index 0000000..fc8d43c
--- /dev/null
+++ b/api/src/com/cloud/api/response/ResourceTagResponse.java
@@ -0,0 +1,94 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License.  Citrix Systems, Inc.
+// reserves all rights not expressly granted by 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.
+// 
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.api.response;
+
+import com.cloud.api.ApiConstants;
+import com.cloud.serializer.Param;
+import com.cloud.utils.IdentityProxy;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Alena Prokharchyk
+ */
+
+@SuppressWarnings("unused")
+public class ResourceTagResponse extends BaseResponse implements ControlledEntityResponse{
+    @SerializedName(ApiConstants.KEY) @Param(description="tag key name")
+    private String key;
+    
+    @SerializedName(ApiConstants.VALUE) @Param(description="tag value")
+    private String value;
+    
+    @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type")
+    private String resourceType;
+    
+    @SerializedName(ApiConstants.RESOURCE_ID) @Param(description="id of the resource")
+    private String id;
+    
+    @SerializedName(ApiConstants.ACCOUNT)
+    @Param(description = "the account associated with the tag")
+    private String accountName;
+    
+    @SerializedName(ApiConstants.PROJECT_ID) @Param(description="the project id the tag belongs to")
+    private IdentityProxy projectId = new IdentityProxy("projects");
+    
+    @SerializedName(ApiConstants.PROJECT) @Param(description="the project name where tag belongs to")
+    private String projectName;
+    
+    @SerializedName(ApiConstants.DOMAIN_ID)
+    @Param(description = "the ID of the domain associated with the tag")
+    private IdentityProxy domainId = new IdentityProxy("domain");
+
+    @SerializedName(ApiConstants.DOMAIN)
+    @Param(description = "the domain associated with the tag")
+    private String domainName;
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public void setResourceType(String resourceType) {
+        this.resourceType = resourceType;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+    
+    public void setAccountName(String accountName) {
+        this.accountName = accountName;
+    }
+
+    public void setDomainId(Long domainId) {
+        this.domainId.setValue(domainId);
+    }
+    
+    public void setDomainName(String domainName) {
+        this.domainName = domainName;
+    }
+    
+    @Override
+    public void setProjectId(Long projectId) {
+        this.projectId.setValue(projectId);
+    }
+    
+    @Override
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }   
+   
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/configuration/Resource.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/configuration/Resource.java b/api/src/com/cloud/configuration/Resource.java
index 24bb669..bb2121b 100644
--- a/api/src/com/cloud/configuration/Resource.java
+++ b/api/src/com/cloud/configuration/Resource.java
@@ -19,6 +19,22 @@ package com.cloud.configuration;
 public interface Resource {
 
     public static final short RESOURCE_UNLIMITED = -1;
+    
+    public enum  TaggedResourceType {
+        UserVm,
+        Template,
+        ISO,
+        Volume,
+        Snapshot,
+        Network,
+        LoadBalancingRule,
+        PortForwardingRule,
+        FirewallRule,
+        SecurityGroup,
+        RemoteAccessVpn,
+        PublicIpAdddress,
+        SecondaryStorageVm
+    }
 
     public enum ResourceType {
         user_vm("user_vm", 0, ResourceOwnerType.Account, ResourceOwnerType.Domain),

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/event/EventTypes.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java
index 424ddc9..c42db5d 100755
--- a/api/src/com/cloud/event/EventTypes.java
+++ b/api/src/com/cloud/event/EventTypes.java
@@ -259,4 +259,9 @@ public class EventTypes {
     public static final String EVENT_EXTERNAL_FIREWALL_DEVICE_ADD = "PHYSICAL.FIREWALL.ADD";
     public static final String EVENT_EXTERNAL_FIREWALL_DEVICE_DELETE = "PHYSICAL.FIREWALL.DELETE";
     public static final String EVENT_EXTERNAL_FIREWALL_DEVICE_CONFIGURE = "PHYSICAL.FIREWALL.CONFIGURE";
+    
+    // tag related events
+    public static final String EVENT_TAGS_CREATE = "CREATE_TAGS";
+    public static final String EVENT_TAGS_DELETE = "DELETE_TAGS";
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/server/ResourceTag.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/server/ResourceTag.java b/api/src/com/cloud/server/ResourceTag.java
new file mode 100644
index 0000000..050c8c8
--- /dev/null
+++ b/api/src/com/cloud/server/ResourceTag.java
@@ -0,0 +1,48 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License.  Citrix Systems, Inc.
+// reserves all rights not expressly granted by 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.
+// 
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.server;
+
+import com.cloud.acl.ControlledEntity;
+import com.cloud.configuration.Resource;
+
+/**
+ * @author Alena Prokharchyk
+ */
+public interface ResourceTag extends ControlledEntity{
+
+    /**
+     * @return
+     */
+    long getId();
+
+    /**
+     * @return
+     */
+    String getKey();
+
+    /**
+     * @return
+     */
+    String getValue();
+
+    /**
+     * @return
+     */
+    long getResourceId();
+
+    /**
+     * @return
+     */
+    Resource.TaggedResourceType getResourceType();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/api/src/com/cloud/server/TaggedResourceService.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/server/TaggedResourceService.java b/api/src/com/cloud/server/TaggedResourceService.java
new file mode 100644
index 0000000..9a4bd32
--- /dev/null
+++ b/api/src/com/cloud/server/TaggedResourceService.java
@@ -0,0 +1,49 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License.  Citrix Systems, Inc.
+// reserves all rights not expressly granted by 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.
+// 
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.server;
+
+import java.util.List;
+import java.util.Map;
+
+import com.cloud.api.commands.ListTagsCmd;
+import com.cloud.configuration.Resource;
+import com.cloud.configuration.Resource.TaggedResourceType;
+
+/**
+ * @author Alena Prokharchyk
+ */
+public interface TaggedResourceService {
+    
+    Resource.TaggedResourceType getResourceType (String resourceTypeStr);
+
+    /**
+     * @param resourceIds TODO
+     * @param resourceType
+     * @param tags
+     * @return
+     */
+    List<ResourceTag> createTags(List<String> resourceIds, TaggedResourceType resourceType, Map<String, String> tags);
+
+    /**
+     * @param resourceId
+     * @param resourceType
+     * @return
+     */
+    String getUuid(String resourceId, TaggedResourceType resourceType);
+
+    /**
+     * @param listTagsCmd
+     * @return
+     */
+    List<? extends ResourceTag> listTags(ListTagsCmd listTagsCmd);
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/client/tomcatconf/commands.properties.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index a939eb7..08c175b 100755
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -331,4 +331,9 @@ updateStorageNetworkIpRange=com.cloud.api.commands.UpdateStorageNetworkIpRangeCm
 ### Network Devices commands
 addNetworkDevice=com.cloud.api.commands.AddNetworkDeviceCmd;1
 listNetworkDevice=com.cloud.api.commands.ListNetworkDeviceCmd;1
-deleteNetworkDevice=com.cloud.api.commands.DeleteNetworkDeviceCmd;1
+deleteNetworkDevice=com.cloud.api.commands.DeleteNetworkDeviceCmd;1
+
+#### Tags commands
+createTags=com.cloud.api.commands.CreateTagsCmd;15
+deleteTags=com.cloud.api.commands.DeleteTagsCmd;15
+listTags=com.cloud.api.commands.ListTagsCmd;15

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/server/src/com/cloud/api/ApiDBUtils.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java
index 4d2eea4..d75ab73 100755
--- a/server/src/com/cloud/api/ApiDBUtils.java
+++ b/server/src/com/cloud/api/ApiDBUtils.java
@@ -25,6 +25,7 @@ import com.cloud.capacity.dao.CapacityDaoImpl.SummedCapacity;
 import com.cloud.configuration.Config;
 import com.cloud.configuration.ConfigurationService;
 import com.cloud.configuration.Resource.ResourceType;
+import com.cloud.configuration.Resource.TaggedResourceType;
 import com.cloud.configuration.dao.ConfigurationDao;
 import com.cloud.dc.AccountVlanMapVO;
 import com.cloud.dc.ClusterVO;
@@ -78,6 +79,7 @@ import com.cloud.resource.ResourceManager;
 import com.cloud.server.Criteria;
 import com.cloud.server.ManagementServer;
 import com.cloud.server.StatsCollector;
+import com.cloud.server.TaggedResourceService;
 import com.cloud.service.ServiceOfferingVO;
 import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.DiskOfferingVO;
@@ -189,6 +191,7 @@ public class ApiDBUtils {
     private static AccountDetailsDao _accountDetailsDao;
     private static NetworkDomainDao _networkDomainDao;
     private static HighAvailabilityManager _haMgr;
+    private static TaggedResourceService _taggedResourceService;
 
     static {
         _ms = (ManagementServer) ComponentLocator.getComponent(ManagementServer.Name);
@@ -242,6 +245,7 @@ public class ApiDBUtils {
         _accountDetailsDao = locator.getDao(AccountDetailsDao.class);
         _networkDomainDao = locator.getDao(NetworkDomainDao.class);
         _haMgr = locator.getManager(HighAvailabilityManager.class);
+        _taggedResourceService = locator.getManager(TaggedResourceService.class);
 
         // Note: stats collector should already have been initialized by this time, otherwise a null instance is returned
         _statsCollector = StatsCollector.getInstance();
@@ -749,7 +753,13 @@ public class ApiDBUtils {
     public static String getHaTag() {
         return _haMgr.getHaTag();
     }
+
     public static boolean canUseForDeploy(Network network) {
         return _networkMgr.canUseForDeploy(network);
     }
+    
+    public static String getUuid(String resourceId, TaggedResourceType resourceType) {
+        return _taggedResourceService.getUuid(resourceId, resourceType);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index 1112dba..d3c91b8 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -70,6 +70,7 @@ import com.cloud.api.response.ProviderResponse;
 import com.cloud.api.response.RemoteAccessVpnResponse;
 import com.cloud.api.response.ResourceCountResponse;
 import com.cloud.api.response.ResourceLimitResponse;
+import com.cloud.api.response.ResourceTagResponse;
 import com.cloud.api.response.SecurityGroupResponse;
 import com.cloud.api.response.SecurityGroupResultObject;
 import com.cloud.api.response.SecurityGroupRuleResponse;
@@ -151,6 +152,7 @@ import com.cloud.projects.Project;
 import com.cloud.projects.ProjectAccount;
 import com.cloud.projects.ProjectInvitation;
 import com.cloud.server.Criteria;
+import com.cloud.server.ResourceTag;
 import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.GuestOS;
 import com.cloud.storage.GuestOSCategoryVO;
@@ -2305,10 +2307,11 @@ public class ApiResponseHelper implements ResponseGenerator {
 
         boolean savedValue = SerializationContext.current().getUuidTranslation();
         SerializationContext.current().setUuidTranslation(false);
-        jobResponse.setJobResult((ResponseObject) ApiSerializerHelper.fromSerializedString(job.getResult()));
-        SerializationContext.current().setUuidTranslation(savedValue);
-
+        
         Object resultObject = ApiSerializerHelper.fromSerializedString(job.getResult());
+        jobResponse.setJobResult((ResponseObject) resultObject);
+        SerializationContext.current().setUuidTranslation(savedValue);
+        
         if (resultObject != null) {
             Class<?> clz = resultObject.getClass();
             if (clz.isPrimitive() || clz.getSuperclass() == Number.class || clz == String.class || clz == Date.class) {
@@ -3399,5 +3402,38 @@ public class ApiResponseHelper implements ResponseGenerator {
     public Long getIdentiyId(String tableName, String token) {
         return ApiDispatcher.getIdentiyId(tableName, token);
     }
-
+    
+    @Override
+    public ResourceTagResponse createResourceTagResponse(ResourceTag resourceTag) {
+        ResourceTagResponse response = new ResourceTagResponse();
+        response.setKey(resourceTag.getKey());
+        response.setValue(resourceTag.getValue());
+        response.setResourceType(resourceTag.getResourceType().toString());        
+        response.setId(ApiDBUtils.getUuid(String.valueOf(resourceTag.getResourceId()),resourceTag.getResourceType()));
+        Long accountId = resourceTag.getAccountId();
+        Long domainId = resourceTag.getDomainId();
+        if (accountId != null) {
+            Account account = ApiDBUtils.findAccountByIdIncludingRemoved(resourceTag.getAccountId());
+
+            if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
+                // find the project
+                Project project = ApiDBUtils.findProjectByProjectAccountId(account.getId());
+                response.setProjectId(project.getId());
+                response.setProjectName(project.getName());
+            } else {
+                response.setAccountName(account.getAccountName());
+            }
+        }
+        
+        if (domainId != null) {
+            response.setDomainId(domainId);
+            response.setDomainName(ApiDBUtils.findDomainById(domainId).getName());
+        }
+        
+        response.setObjectName("tag");
+  
+        return response;
+    }
+    
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/server/src/com/cloud/configuration/DefaultComponentLibrary.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
index 150d7af..df833f4 100755
--- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java
+++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
@@ -45,6 +45,7 @@ import com.cloud.dao.EntityManagerImpl;
 import com.cloud.dc.ClusterDetailsDaoImpl;
 import com.cloud.dc.dao.AccountVlanMapDaoImpl;
 import com.cloud.dc.dao.ClusterDaoImpl;
+import com.cloud.dc.dao.ClusterVSMMapDaoImpl;
 import com.cloud.dc.dao.DataCenterDaoImpl;
 import com.cloud.dc.dao.DataCenterIpAddressDaoImpl;
 import com.cloud.dc.dao.DcDetailsDaoImpl;
@@ -70,11 +71,9 @@ import com.cloud.maint.dao.AgentUpgradeDaoImpl;
 import com.cloud.network.ExternalLoadBalancerUsageManagerImpl;
 import com.cloud.network.NetworkManagerImpl;
 import com.cloud.network.StorageNetworkManagerImpl;
+import com.cloud.network.dao.CiscoNexusVSMDeviceDaoImpl;
 import com.cloud.network.dao.ExternalFirewallDeviceDaoImpl;
 import com.cloud.network.dao.ExternalLoadBalancerDeviceDaoImpl;
-import com.cloud.network.dao.CiscoNexusVSMDeviceDaoImpl;
-import com.cloud.dc.dao.ClusterVSMMapDaoImpl;
-import com.cloud.network.dao.PortProfileDaoImpl;
 import com.cloud.network.dao.FirewallRulesCidrsDaoImpl;
 import com.cloud.network.dao.FirewallRulesDaoImpl;
 import com.cloud.network.dao.IPAddressDaoImpl;
@@ -92,16 +91,17 @@ import com.cloud.network.dao.NetworkServiceMapDaoImpl;
 import com.cloud.network.dao.PhysicalNetworkDaoImpl;
 import com.cloud.network.dao.PhysicalNetworkServiceProviderDaoImpl;
 import com.cloud.network.dao.PhysicalNetworkTrafficTypeDaoImpl;
+import com.cloud.network.dao.PortProfileDaoImpl;
 import com.cloud.network.dao.RemoteAccessVpnDaoImpl;
 import com.cloud.network.dao.VirtualRouterProviderDaoImpl;
 import com.cloud.network.dao.VpnUserDaoImpl;
+import com.cloud.network.element.CiscoNexusVSMElement;
+import com.cloud.network.element.CiscoNexusVSMElementService;
 import com.cloud.network.element.F5ExternalLoadBalancerElement;
 import com.cloud.network.element.F5ExternalLoadBalancerElementService;
 import com.cloud.network.element.JuniperSRXExternalFirewallElement;
 import com.cloud.network.element.JuniperSRXFirewallElementService;
 import com.cloud.network.element.NetscalerElement;
-import com.cloud.network.element.CiscoNexusVSMElement;
-import com.cloud.network.element.CiscoNexusVSMElementService;
 import com.cloud.network.element.NetscalerLoadBalancerElementService;
 import com.cloud.network.element.VirtualRouterElement;
 import com.cloud.network.element.VirtualRouterElementService;
@@ -160,6 +160,8 @@ import com.cloud.storage.snapshot.SnapshotManagerImpl;
 import com.cloud.storage.snapshot.SnapshotSchedulerImpl;
 import com.cloud.storage.swift.SwiftManagerImpl;
 import com.cloud.storage.upload.UploadMonitorImpl;
+import com.cloud.tags.TaggedResourceManagerImpl;
+import com.cloud.tags.dao.ResourceTagsDaoImpl;
 import com.cloud.template.HyervisorTemplateAdapter;
 import com.cloud.template.TemplateAdapter;
 import com.cloud.template.TemplateAdapter.TemplateAdapterType;
@@ -329,6 +331,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
         addDao("NetworkServiceMapDao", NetworkServiceMapDaoImpl.class);
         addDao("StorageNetworkIpAddressDao", StorageNetworkIpAddressDaoImpl.class);
         addDao("StorageNetworkIpRangeDao", StorageNetworkIpRangeDaoImpl.class);
+        addDao("TagsDao", ResourceTagsDaoImpl.class);
     }
 
     @Override
@@ -385,6 +388,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
         addManager("StorageNetworkManager", StorageNetworkManagerImpl.class);
         addManager("ExternalLoadBalancerUsageManager", ExternalLoadBalancerUsageManagerImpl.class);
         addManager("HA Manager", HighAvailabilityManagerImpl.class);
+        addManager("TaggedResourcesManager", TaggedResourceManagerImpl.class);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/server/src/com/cloud/tags/ResourceTagVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/tags/ResourceTagVO.java b/server/src/com/cloud/tags/ResourceTagVO.java
new file mode 100644
index 0000000..74786ba
--- /dev/null
+++ b/server/src/com/cloud/tags/ResourceTagVO.java
@@ -0,0 +1,139 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License.  Citrix Systems, Inc.
+// reserves all rights not expressly granted by 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.
+// 
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.tags;
+
+import java.util.UUID;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import com.cloud.api.Identity;
+import com.cloud.configuration.Resource;
+import com.cloud.configuration.Resource.TaggedResourceType;
+import com.cloud.server.ResourceTag;
+
+/**
+ * @author Alena Prokharchyk
+ */
+
+@Entity
+@Table(name="resource_tags")
+public class ResourceTagVO implements Identity, ResourceTag{
+    
+    @Id
+    @GeneratedValue(strategy=GenerationType.IDENTITY)
+    @Column(name="id")
+    private long id;
+    
+    @Column(name="uuid")
+    private String uuid;
+    
+    @Column(name="key")
+    private String key;
+    
+    @Column(name="value")
+    String value;
+    
+    @Column(name="domain_id")
+    long domainId;
+
+    @Column(name="account_id")
+    long accountId;
+    
+    @Column(name="resource_id")
+    long resourceId;
+    
+    @Column(name="resource_type")
+    @Enumerated(value=EnumType.STRING)
+    private Resource.TaggedResourceType resourceType;
+
+    
+    protected ResourceTagVO(){
+        this.uuid = UUID.randomUUID().toString();
+    }
+    
+    /**
+     * @param key
+     * @param value
+     * @param accountId
+     * @param domainId
+     * @param resourceId
+     * @param resourceType
+     */
+    public ResourceTagVO(String key, String value, long accountId, long domainId, long resourceId, TaggedResourceType resourceType) {
+        super();
+        this.key = key;
+        this.value = value;
+        this.domainId = domainId;
+        this.accountId = accountId;
+        this.resourceId = resourceId;
+        this.resourceType = resourceType;
+        this.uuid = UUID.randomUUID().toString();
+    }
+    
+    
+    @Override
+    public String toString() {
+        StringBuilder buf = new StringBuilder("Tag[");
+        buf.append(id).append("|key=").append(key).append("|value=").append(domainId).append("|value=").
+        append("|resourceType=").append(resourceType).append("|resourceId=").append(resourceId)
+        .append("|accountId=").append(accountId).append("]");
+        return buf.toString();
+    }
+    
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @Override
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public long getDomainId() {
+        return domainId;
+    }
+
+    @Override
+    public long getAccountId() {
+        return accountId;
+    }
+
+    @Override
+    public long getResourceId() {
+        return resourceId;
+    }
+
+    @Override
+    public Resource.TaggedResourceType getResourceType() {
+        return resourceType;
+    }
+
+    @Override
+    public String getUuid() {
+        return uuid;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/server/src/com/cloud/tags/TaggedResourceManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java
new file mode 100644
index 0000000..7e442a9
--- /dev/null
+++ b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java
@@ -0,0 +1,251 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License.  Citrix Systems, Inc.
+// reserves all rights not expressly granted by 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.
+// 
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.tags;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.api.commands.ListTagsCmd;
+import com.cloud.configuration.Resource;
+import com.cloud.configuration.Resource.TaggedResourceType;
+import com.cloud.domain.Domain;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.projects.Project.ListProjectResourcesCriteria;
+import com.cloud.server.ResourceTag;
+import com.cloud.server.TaggedResourceService;
+import com.cloud.tags.dao.ResourceTagDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.DomainManager;
+import com.cloud.user.UserContext;
+import com.cloud.utils.Pair;
+import com.cloud.utils.Ternary;
+import com.cloud.utils.component.Inject;
+import com.cloud.utils.component.Manager;
+import com.cloud.utils.db.DB;
+import com.cloud.utils.db.DbUtil;
+import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.GenericDao;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.uuididentity.dao.IdentityDao;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.UserVmDao;
+
+/**
+ * @author Alena Prokharchyk
+ */
+@Local(value = { TaggedResourceService.class})
+public class TaggedResourceManagerImpl implements TaggedResourceService, Manager{
+    public static final Logger s_logger = Logger.getLogger(TaggedResourceManagerImpl.class);
+    private String _name;
+    
+    private static Map<Resource.TaggedResourceType, String> _resourceMap= 
+            new HashMap<Resource.TaggedResourceType, String>();
+    
+    private static Map<Resource.TaggedResourceType, GenericDao<?, Long>> _daoMap= 
+            new HashMap<Resource.TaggedResourceType, GenericDao<?, Long>>();
+    
+    @Inject
+    AccountManager _accountMgr;
+    @Inject
+    ResourceTagDao _resourceTagDao;
+    @Inject
+    IdentityDao _identityDao;
+    @Inject
+    DomainManager _domainMgr;
+    @Inject
+    UserVmDao _userVmDao;
+
+    
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+        _name = name; 
+        _resourceMap.put(TaggedResourceType.UserVm, DbUtil.getTableName(VMInstanceVO.class));
+        _daoMap.put(TaggedResourceType.UserVm, _userVmDao);
+        
+        
+        return true;
+    }
+
+    @Override
+    public boolean start() {
+        return true;
+    }
+
+    @Override
+    public boolean stop() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return _name;
+    }
+
+    
+    private Long getResourceId(String resourceId, Resource.TaggedResourceType resourceType) {
+        String tableName = _resourceMap.get(resourceType);
+
+        if (tableName == null) {
+            throw new InvalidParameterValueException("Unable to find resource of type " + resourceType + " in the database");
+        }
+        
+        return _identityDao.getIdentityId(tableName, resourceId);
+    }
+    
+    private Pair<Long, Long> getAccountDomain(long resourceId, Resource.TaggedResourceType resourceType) {
+        String tableName = _resourceMap.get(resourceType);
+
+        if (tableName == null) {
+            throw new InvalidParameterValueException("Unable to find resource of type " + resourceType + " in the database");
+        }
+        
+        Pair<Long, Long> pair = _identityDao.getAccountDomainInfo(tableName, resourceId);
+        Long accountId = pair.first();
+        Long domainId = pair.second();
+        
+        if (accountId == null) {
+            accountId = Account.ACCOUNT_ID_SYSTEM;
+        }
+        
+        if (domainId == null) {
+            domainId = Domain.ROOT_DOMAIN;
+        }
+        
+        return new Pair<Long, Long>(accountId, domainId);
+    }
+
+    @Override
+    public TaggedResourceType getResourceType(String resourceTypeStr) {
+        Resource.TaggedResourceType resourceType = null;
+        try {
+            resourceType = Resource.TaggedResourceType.valueOf(resourceTypeStr);
+         } catch (IllegalArgumentException ex) {
+             throw new InvalidParameterValueException("Invalid resource type " + resourceType);
+         }
+        
+        return resourceType;
+    }
+
+    @Override
+    @DB
+    public List<ResourceTag> createTags(List<String> resourceIds, TaggedResourceType resourceType, Map<String, String> tags) {
+        Account caller = UserContext.current().getCaller();
+        
+        List<ResourceTag> resourceTags = new ArrayList<ResourceTag>(tags.size());
+        
+        Transaction txn = Transaction.currentTxn();
+        txn.start();
+        
+        for (String tag : tags.keySet()) {
+            for (String resourceId : resourceIds) {
+                Long id = getResourceId(resourceId, resourceType);
+                
+                //check if object exists
+                if (_daoMap.get(resourceType).findById(id) == null) {
+                    throw new InvalidParameterValueException("Unable to find resource by id " + resourceId + " and type " + resourceType);
+                }
+                
+                Pair<Long, Long> accountDomainPair = getAccountDomain(id, resourceType);
+                Long domainId = accountDomainPair.second();
+                Long accountId = accountDomainPair.first();
+                if (accountId != null) {
+                    _accountMgr.checkAccess(caller, null, false, _accountMgr.getAccount(accountId));
+                } else if (domainId != null && caller.getType() != Account.ACCOUNT_TYPE_NORMAL) {
+                    //check permissions;
+                    _accountMgr.checkAccess(caller, _domainMgr.getDomain(domainId));
+                } else {
+                    throw new PermissionDeniedException("Account " + caller + " doesn't have permissions to create tags" +
+                    		" for resource " + tag);
+                }
+               
+                ResourceTagVO resourceTag = new ResourceTagVO(tag, tags.get(tag), accountDomainPair.first(),
+                        accountDomainPair.second(), 
+                        id, resourceType);
+                resourceTag = _resourceTagDao.persist(resourceTag);
+                resourceTags.add(resourceTag);
+
+            }
+        }
+        
+        txn.commit();
+        
+        return resourceTags;
+    }
+    
+    @Override
+    public String getUuid(String resourceId, TaggedResourceType resourceType) {
+        return _identityDao.getIdentityUuid(_resourceMap.get(resourceType), resourceId);
+    }
+
+    @Override
+    public List<? extends ResourceTag> listTags(ListTagsCmd cmd) {
+        Account caller = UserContext.current().getCaller();
+        List<Long> permittedAccounts = new ArrayList<Long>();
+        String key = cmd.getKey();
+        String value = cmd.getValue();
+        String resourceId = cmd.getResourceId();
+        String resourceType = cmd.getResourceType();
+        boolean listAll = cmd.listAll();
+
+        Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = 
+                new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
+       _accountMgr.buildACLSearchParameters(caller, null, cmd.getAccountName(), 
+               cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, listAll, false);
+           Long domainId = domainIdRecursiveListProject.first();
+       Boolean isRecursive = domainIdRecursiveListProject.second();
+       ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
+       Filter searchFilter = new Filter(ResourceTagVO.class, "resourceType", false, cmd.getStartIndex(), cmd.getPageSizeVal());
+       
+       SearchBuilder<ResourceTagVO> sb = _resourceTagDao.createSearchBuilder();
+       _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
+
+       sb.and("key", sb.entity().getKey(), SearchCriteria.Op.EQ);
+       sb.and("value", sb.entity().getValue(), SearchCriteria.Op.EQ);
+       sb.and("resourceId", sb.entity().getResourceId(), SearchCriteria.Op.EQ);
+       sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ);
+       
+       // now set the SC criteria...
+       SearchCriteria<ResourceTagVO> sc = sb.create();
+       _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
+       
+       if (key != null) {
+           sc.setParameters("key", key);
+       }
+       
+       if (value != null) {
+           sc.setParameters("value", value);
+       }
+       
+       if (resourceId != null) {
+           sc.setParameters("resourceId", resourceId);
+       }
+       
+       if (resourceType != null) {
+           sc.setParameters("resourceType", resourceType);
+       }
+       
+       return _resourceTagDao.search(sc, searchFilter);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/server/src/com/cloud/tags/dao/ResourceTagDao.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/tags/dao/ResourceTagDao.java b/server/src/com/cloud/tags/dao/ResourceTagDao.java
new file mode 100644
index 0000000..a2d3851
--- /dev/null
+++ b/server/src/com/cloud/tags/dao/ResourceTagDao.java
@@ -0,0 +1,23 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License.  Citrix Systems, Inc.
+// reserves all rights not expressly granted by 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.
+// 
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.tags.dao;
+
+import com.cloud.tags.ResourceTagVO;
+import com.cloud.utils.db.GenericDao;
+
+/**
+ * @author Alena Prokharchyk
+ */
+public interface ResourceTagDao extends GenericDao<ResourceTagVO, Long>{
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/server/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java b/server/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java
new file mode 100644
index 0000000..0285dad
--- /dev/null
+++ b/server/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java
@@ -0,0 +1,29 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License.  Citrix Systems, Inc.
+// reserves all rights not expressly granted by 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.
+// 
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.tags.dao;
+
+import javax.ejb.Local;
+
+import com.cloud.tags.ResourceTagVO;
+import com.cloud.utils.db.GenericDaoBase;
+
+/**
+ * @author Alena Prokharchyk
+ */
+
+@Local(value = { ResourceTagDao.class })
+public class ResourceTagsDaoImpl extends GenericDaoBase<ResourceTagVO, Long> implements ResourceTagDao{
+    
+    protected ResourceTagsDaoImpl() {  
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/server/src/com/cloud/uuididentity/dao/IdentityDao.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/uuididentity/dao/IdentityDao.java b/server/src/com/cloud/uuididentity/dao/IdentityDao.java
index f5acf71..769aebf 100644
--- a/server/src/com/cloud/uuididentity/dao/IdentityDao.java
+++ b/server/src/com/cloud/uuididentity/dao/IdentityDao.java
@@ -10,14 +10,22 @@
 // limitations under the License.
 // 
 // Automatically generated by addcopyright.py at 04/03/2012
-package com.cloud.uuididentity.dao;
 
-import com.cloud.api.IdentityMapper;
-import com.cloud.utils.db.GenericDao;
-
-public interface IdentityDao extends GenericDao<IdentityVO, Long> {
-	Long getIdentityId(IdentityMapper mapper, String identityString);
-    Long getIdentityId(String tableName, String identityString);
-	String getIdentityUuid(String tableName, String identityString);
+package com.cloud.uuididentity.dao;
+
+import com.cloud.api.IdentityMapper;
+import com.cloud.utils.Pair;
+import com.cloud.utils.db.GenericDao;
+
+public interface IdentityDao extends GenericDao<IdentityVO, Long> {
+	Long getIdentityId(IdentityMapper mapper, String identityString);
+    Long getIdentityId(String tableName, String identityString);
+	String getIdentityUuid(String tableName, String identityString);
 	void initializeDefaultUuid(String tableName);
-}
+    /**
+     * @param tableName
+     * @param identityId
+     * @return
+     */
+    Pair<Long, Long> getAccountDomainInfo(String tableName, Long identityId);
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/server/src/com/cloud/uuididentity/dao/IdentityDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/uuididentity/dao/IdentityDaoImpl.java b/server/src/com/cloud/uuididentity/dao/IdentityDaoImpl.java
index abe2011..219ed90 100644
--- a/server/src/com/cloud/uuididentity/dao/IdentityDaoImpl.java
+++ b/server/src/com/cloud/uuididentity/dao/IdentityDaoImpl.java
@@ -10,8 +10,8 @@
 // limitations under the License.
 // 
 // Automatically generated by addcopyright.py at 04/03/2012
-package com.cloud.uuididentity.dao;
-
+package com.cloud.uuididentity.dao;
+
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -25,147 +25,89 @@ import org.apache.log4j.Logger;
 
 import com.cloud.api.IdentityMapper;
 import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.utils.Pair;
 import com.cloud.utils.db.DB;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.Transaction;
-
-@Local(value={IdentityDao.class})
-public class IdentityDaoImpl extends GenericDaoBase<IdentityVO, Long> implements IdentityDao {
-    private static final Logger s_logger = Logger.getLogger(IdentityDaoImpl.class);
-    
-    public IdentityDaoImpl() {
-    }
-    
-    @DB
-	public Long getIdentityId(IdentityMapper mapper, String identityString) {
-    	assert(mapper.entityTableName() != null);
-    	return getIdentityId(mapper.entityTableName(), identityString);
-	}
-    
-    @DB
-    public Long getIdentityId(String tableName, String identityString) {
-		assert(tableName != null);
-		assert(identityString != null);
-
-        PreparedStatement pstmt = null;
-		Transaction txn = Transaction.open(Transaction.CLOUD_DB);
-		try {
-	        try {
-	            pstmt = txn.prepareAutoCloseStatement(
-	        		String.format("SELECT id FROM `%s` WHERE id=? OR uuid=?", tableName)
-	        		
-	        		// TODO : after graceful period, use following line turn on more secure check
-	        		// String.format("SELECT id FROM %s WHERE (id=? AND uuid IS NULL) OR uuid=?", mapper.entityTableName())
-	            );
-	            
-	            long id = 0;
-	            try {
-	            	// TODO : use regular expression to determine
-	            	id = Long.parseLong(identityString);
-	            } catch(NumberFormatException e) {
-	            	// this could happen when it is a uuid string, so catch and ignore it
-	            }
-	            
-	            pstmt.setLong(1, id);
-	            pstmt.setString(2, identityString);
-	            
-	            ResultSet rs = pstmt.executeQuery();
-	            if(rs.next()) {
-	            	return rs.getLong(1);
-	            } else {
-	            	if(id == -1L)
-	            		return id;
-	            	
-	        		throw new InvalidParameterValueException("Object " + tableName + "(uuid: " + identityString + ") does not exist.");
-	            }
-	        } catch (SQLException e) {
-	        	s_logger.error("Unexpected exception ", e);
-	        }
-		} finally {
-			txn.close();
-		}
-		return null;
+
+@Local(value={IdentityDao.class})
+public class IdentityDaoImpl extends GenericDaoBase<IdentityVO, Long> implements IdentityDao {
+    private static final Logger s_logger = Logger.getLogger(IdentityDaoImpl.class);
+    
+    public IdentityDaoImpl() {
+    }
+    
+    @DB
+	public Long getIdentityId(IdentityMapper mapper, String identityString) {
+    	assert(mapper.entityTableName() != null);
+    	return getIdentityId(mapper.entityTableName(), identityString);
+	}
+    
+    @DB
+    public Long getIdentityId(String tableName, String identityString) {
+		assert(tableName != null);
+		assert(identityString != null);
+
+        PreparedStatement pstmt = null;
+		Transaction txn = Transaction.open(Transaction.CLOUD_DB);
+		try {
+	        try {
+	            pstmt = txn.prepareAutoCloseStatement(
+	        		String.format("SELECT id FROM `%s` WHERE id=? OR uuid=?", tableName)
+	        		
+	        		// TODO : after graceful period, use following line turn on more secure check
+	        		// String.format("SELECT id FROM %s WHERE (id=? AND uuid IS NULL) OR uuid=?", mapper.entityTableName())
+	            );
+	            
+	            long id = 0;
+	            try {
+	            	// TODO : use regular expression to determine
+	            	id = Long.parseLong(identityString);
+	            } catch(NumberFormatException e) {
+	            	// this could happen when it is a uuid string, so catch and ignore it
+	            }
+	            
+	            pstmt.setLong(1, id);
+	            pstmt.setString(2, identityString);
+	            
+	            ResultSet rs = pstmt.executeQuery();
+	            if(rs.next()) {
+	            	return rs.getLong(1);
+	            } else {
+	            	if(id == -1L)
+	            		return id;
+	            	
+	        		throw new InvalidParameterValueException("Object " + tableName + "(uuid: " + identityString + ") does not exist.");
+	            }
+	        } catch (SQLException e) {
+	        	s_logger.error("Unexpected exception ", e);
+	        }
+		} finally {
+			txn.close();
+		}
+		return null;
     }
-	
-    @DB
-	public String getIdentityUuid(String tableName, String identityString) {
-		assert(tableName != null);
-		assert(identityString != null);
-		
-        PreparedStatement pstmt = null;
-		Transaction txn = Transaction.open(Transaction.CLOUD_DB);
-		try {
-	        try {
-	            pstmt = txn.prepareAutoCloseStatement(
-	            	String.format("SELECT uuid FROM `%s` WHERE id=? OR uuid=?", tableName)
-	        		// String.format("SELECT uuid FROM %s WHERE (id=? AND uuid IS NULL) OR uuid=?", tableName)
-	        	);
-	            
-	            long id = 0;
-	            try {
-	            	// TODO : use regular expression to determine
-	            	id = Long.parseLong(identityString);
-	            } catch(NumberFormatException e) {
-	            	// this could happen when it is a uuid string, so catch and ignore it
-	            }
-	            
-	            pstmt.setLong(1, id);
-	            pstmt.setString(2, identityString);
-	            
-	            ResultSet rs = pstmt.executeQuery();
-	            if(rs.next()) {
-	            	String uuid = rs.getString(1);
-	            	if(uuid != null && !uuid.isEmpty())
-	            		return uuid;
-	            	return identityString;
-	            }
-	        } catch (SQLException e) {
-	        	s_logger.error("Unexpected exception ", e);
-	        }
-		} finally {
-			txn.close();
-		}
-		
-		return identityString;
-	}
     
     @DB
-    public void initializeDefaultUuid(String tableName) {
+    @Override
+    public Pair<Long, Long> getAccountDomainInfo(String tableName, Long identityId) {
         assert(tableName != null);
-        List<Long> l = getNullUuidRecords(tableName);
-        
-        Transaction txn = Transaction.open(Transaction.CLOUD_DB);
-        try {
-            try {
-                txn.start();
-                for(Long id : l) {
-                    setInitialUuid(tableName, id);
-                }
-                txn.commit();
-            } catch (SQLException e) {
-                txn.rollback();
-                s_logger.error("Unexpected exception ", e);
-            }
-        } finally {
-            txn.close();
-        }
-    }
-    
-    @DB
-    List<Long> getNullUuidRecords(String tableName) {
-        List<Long> l = new ArrayList<Long>();
         
         PreparedStatement pstmt = null;
         Transaction txn = Transaction.open(Transaction.CLOUD_DB);
         try {
             try {
                 pstmt = txn.prepareAutoCloseStatement(
-                    String.format("SELECT id FROM `%s` WHERE uuid IS NULL", tableName)
+                    String.format("SELECT account_id, domain_id FROM `%s` WHERE id=?", tableName)
                 );
                 
+                pstmt.setLong(1, identityId);
+                
                 ResultSet rs = pstmt.executeQuery();
-                while(rs.next()) {
-                    l.add(rs.getLong(1));
+                if(rs.next()) {
+                    return new Pair<Long, Long>(rs.getLong(1), rs.getLong(2));
+                } else {
+                    throw new InvalidParameterValueException("Object " + tableName + "(id: " + identityId + ") does not exist.");
                 }
             } catch (SQLException e) {
                 s_logger.error("Unexpected exception ", e);
@@ -173,20 +115,110 @@ public class IdentityDaoImpl extends GenericDaoBase<IdentityVO, Long> implements
         } finally {
             txn.close();
         }
-        return l;
-    }
-    
+        return null;
+    }
+	
     @DB
-    void setInitialUuid(String tableName, long id) throws SQLException {
-        Transaction txn = Transaction.currentTxn();
-        
-        PreparedStatement pstmtUpdate = null;
-        pstmtUpdate = txn.prepareAutoCloseStatement(
-            String.format("UPDATE `%s` SET uuid=? WHERE id=?", tableName)
-        );
-        
-        pstmtUpdate.setString(1, UUID.randomUUID().toString());
-        pstmtUpdate.setLong(2, id);
-        pstmtUpdate.executeUpdate();
-    }
-}
+    @Override
+	public String getIdentityUuid(String tableName, String identityString) {
+		assert(tableName != null);
+		assert(identityString != null);
+		
+        PreparedStatement pstmt = null;
+		Transaction txn = Transaction.open(Transaction.CLOUD_DB);
+		try {
+	        try {
+	            pstmt = txn.prepareAutoCloseStatement(
+	            	String.format("SELECT uuid FROM `%s` WHERE id=? OR uuid=?", tableName)
+	        		// String.format("SELECT uuid FROM %s WHERE (id=? AND uuid IS NULL) OR uuid=?", tableName)
+	        	);
+	            
+	            long id = 0;
+	            try {
+	            	// TODO : use regular expression to determine
+	            	id = Long.parseLong(identityString);
+	            } catch(NumberFormatException e) {
+	            	// this could happen when it is a uuid string, so catch and ignore it
+	            }
+	            
+	            pstmt.setLong(1, id);
+	            pstmt.setString(2, identityString);
+	            
+	            ResultSet rs = pstmt.executeQuery();
+	            if(rs.next()) {
+	            	String uuid = rs.getString(1);
+	            	if(uuid != null && !uuid.isEmpty())
+	            		return uuid;
+	            	return identityString;
+	            }
+	        } catch (SQLException e) {
+	        	s_logger.error("Unexpected exception ", e);
+	        }
+		} finally {
+			txn.close();
+		}
+		
+		return identityString;
+	}
+    
+    @DB
+    public void initializeDefaultUuid(String tableName) {
+        assert(tableName != null);
+        List<Long> l = getNullUuidRecords(tableName);
+        
+        Transaction txn = Transaction.open(Transaction.CLOUD_DB);
+        try {
+            try {
+                txn.start();
+                for(Long id : l) {
+                    setInitialUuid(tableName, id);
+                }
+                txn.commit();
+            } catch (SQLException e) {
+                txn.rollback();
+                s_logger.error("Unexpected exception ", e);
+            }
+        } finally {
+            txn.close();
+        }
+    }
+    
+    @DB
+    List<Long> getNullUuidRecords(String tableName) {
+        List<Long> l = new ArrayList<Long>();
+        
+        PreparedStatement pstmt = null;
+        Transaction txn = Transaction.open(Transaction.CLOUD_DB);
+        try {
+            try {
+                pstmt = txn.prepareAutoCloseStatement(
+                    String.format("SELECT id FROM `%s` WHERE uuid IS NULL", tableName)
+                );
+                
+                ResultSet rs = pstmt.executeQuery();
+                while(rs.next()) {
+                    l.add(rs.getLong(1));
+                }
+            } catch (SQLException e) {
+                s_logger.error("Unexpected exception ", e);
+            }
+        } finally {
+            txn.close();
+        }
+        return l;
+    }
+    
+    @DB
+    void setInitialUuid(String tableName, long id) throws SQLException {
+        Transaction txn = Transaction.currentTxn();
+        
+        PreparedStatement pstmtUpdate = null;
+        pstmtUpdate = txn.prepareAutoCloseStatement(
+            String.format("UPDATE `%s` SET uuid=? WHERE id=?", tableName)
+        );
+        
+        pstmtUpdate.setString(1, UUID.randomUUID().toString());
+        pstmtUpdate.setLong(2, id);
+        pstmtUpdate.executeUpdate();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/setup/db/create-schema.sql
----------------------------------------------------------------------
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql
index 488fb06..afcee3f 100755
--- a/setup/db/create-schema.sql
+++ b/setup/db/create-schema.sql
@@ -2132,4 +2132,22 @@ CREATE TABLE  `cloud`.`netscaler_pod_ref` (
   CONSTRAINT `fk_ns_pod_ref__device_id` FOREIGN KEY (`external_load_balancer_device_id`) REFERENCES `external_load_balancer_devices`(`id`) ON DELETE CASCADE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
+
+CREATE TABLE `cloud`.`resource_tags` (
+  `id` bigint unsigned NOT NULL auto_increment COMMENT 'id',
+  `uuid` varchar(40),
+  `key` varchar(255),
+  `value` varchar(255),
+  `resource_id` bigint unsigned NOT NULL,
+  `resource_type` varchar(255),
+  `customer` varchar(255),
+  `domain_id` bigint unsigned NOT NULL COMMENT 'foreign key to domain id',
+  `account_id` bigint unsigned NOT NULL COMMENT 'owner of this network',
+  PRIMARY KEY (`id`),
+  CONSTRAINT `fk_tags__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`),
+  CONSTRAINT `fk_tags__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`),
+  UNIQUE `i_tags__resource_id__resource_type__key`(`resource_id`, `resource_type`, `key`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
 SET foreign_key_checks = 1;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/62d45b96/wscript
----------------------------------------------------------------------
diff --git a/wscript b/wscript
index 1ead7af..7ff0de3 100644
--- a/wscript
+++ b/wscript
@@ -4,6 +4,7 @@
 # the following two variables are used by the target "waf dist"
 # if you change 'em here, you need to change it also in cloud.spec, add a %changelog entry there, and add an entry in debian/changelog
 VERSION = '3.0.3.2012-05-15T19:32:03Z'
+
 APPNAME = 'cloud'
 
 import shutil,os