Merge branch 'master' of github.com:BroganD1993/ec2stack into tag_support
diff --git a/ec2stack/configure.py b/ec2stack/configure.py
index f9ce76c..9c45cd9 100644
--- a/ec2stack/configure.py
+++ b/ec2stack/configure.py
@@ -96,30 +96,69 @@
     )
 
     if configure_instance_type_mapings.lower() in ['yes', 'y']:
-        instance_type_map = {}
-        while True:
-            key = raw_input(
-                'Insert the AWS EC2 instance type you wish to map: '
-            )
+        config_file = _read_user_instance_mappings(config_file)
 
-            value = raw_input(
-                'Insert the name of the instance type you wish to map this to: '
-            )
+    configure_resource_type_mapings = raw_input(
+        'Do you wish to input resource type to resource id mappings'
+        + ' for tag support? (Yes/No): '
+    )
 
-            instance_type_map[key] = value
-
-            add_more = raw_input(
-                'Do you wish to add more mappings? (Yes/No): ')
-            if add_more.lower() in ['no', 'n']:
-                break
-
-        config_file.write(
-            'INSTANCE_TYPE_MAP = %s\n' % instance_type_map
-        )
+    if configure_resource_type_mapings.lower() in ['yes', 'y']:
+        config_file = _read_user_resource_type_mappings(config_file)
 
     config_file.close()
 
 
+def _read_user_instance_mappings(config_file):
+    instance_type_map = {}
+    while True:
+        key = raw_input(
+            'Insert the AWS EC2 instance type you wish to map: '
+        )
+
+        value = raw_input(
+            'Insert the name of the instance type you wish to map this to: '
+        )
+
+        instance_type_map[key] = value
+
+        add_more = raw_input(
+            'Do you wish to add more mappings? (Yes/No): ')
+        if add_more.lower() in ['no', 'n']:
+            break
+
+    config_file.write(
+        'INSTANCE_TYPE_MAP = %s\n' % instance_type_map
+    )
+
+    return config_file
+
+
+def _read_user_resource_type_mappings(config_file):
+    resource_type_map = {}
+    while True:
+        key = raw_input(
+            'Insert the cloudstack resource id you wish to map: '
+        )
+
+        value = raw_input(
+            'Insert the cloudstack resource type you wish to map this to: '
+        )
+
+        resource_type_map[key] = value
+
+        add_more = raw_input(
+            'Do you wish to add more mappings? (Yes/No): ')
+        if add_more.lower() in ['no', 'n']:
+            break
+
+    config_file.write(
+        'RESOURCE_TYPE_MAP = %s\n' % resource_type_map
+    )
+
+    return config_file
+
+
 def _create_database():
     """
     Creates/Updates the database.
diff --git a/ec2stack/controllers/default.py b/ec2stack/controllers/default.py
index 42a7854..97293dd 100644
--- a/ec2stack/controllers/default.py
+++ b/ec2stack/controllers/default.py
@@ -8,7 +8,7 @@
 from ec2stack.core import Ec2stackError
 from ec2stack.services import USERS
 from ec2stack.providers.cloudstack import images, instances, keypairs, \
-    passwords, security_groups, zones, volumes
+    passwords, security_groups, zones, volumes, tags
 
 
 DEFAULT = Blueprint('default', __name__)
@@ -45,9 +45,11 @@
         security_groups.authenticate_security_group_ingress,
         'CreateKeyPair': keypairs.create_keypair,
         'CreateSecurityGroup': security_groups.create_security_group,
+        'CreateTags': tags.create_tags,
         'CreateVolume': volumes.create_volume,
         'DeleteKeyPair': keypairs.delete_keypair,
         'DeleteSecurityGroup': security_groups.delete_security_group,
+        'DeleteTags': tags.delete_tags,
         'DeleteVolume': volumes.delete_volume,
         'DescribeAvailabilityZones': zones.describe_zones,
         'DescribeImageAttribute': images.describe_image_attribute,
@@ -56,6 +58,7 @@
         'DescribeInstances': instances.describe_instances,
         'DescribeKeyPairs': keypairs.describe_keypairs,
         'DescribeSecurityGroups': security_groups.describe_security_groups,
+        'DescribeTags': tags.describe_tags,
         'DescribeVolumes': volumes.describe_volumes,
         'DetachVolume': volumes.detach_volume,
         'GetPasswordData': passwords.get_password_data,
diff --git a/ec2stack/providers/cloudstack/tags.py b/ec2stack/providers/cloudstack/tags.py
new file mode 100644
index 0000000..6cbd512
--- /dev/null
+++ b/ec2stack/providers/cloudstack/tags.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+"""This module contains functions for handling requests in relation to tags.
+"""
+
+from flask import current_app
+
+from ec2stack import helpers, errors
+from ec2stack.providers.cloudstack import requester
+
+
+@helpers.authentication_required
+def create_tags():
+    """
+    Create a tag.
+
+    @return: Response.
+    """
+    _create_tag_request()
+    return _create_tag_response()
+
+
+def _create_tag_request():
+    """
+    Request to create a tag.
+
+    @return: Response.
+    """
+
+    key = helpers.get('Tag.1.Key')
+    value = helpers.get('Tag.1.Value')
+    resource_id = helpers.get('ResourceId.1')
+
+    if resource_id in current_app.config['RESOURCE_TYPE_MAP']:
+        resource_type = current_app.config['RESOURCE_TYPE_MAP'][resource_id]
+    else:
+        errors.invalid_request(
+            str(resource_id) +
+            " not found in configuration")
+
+    args = {
+        'command': 'createTags',
+        'resourceIds': resource_id,
+        'resourceType': resource_type,
+        'tags[0].key': key,
+        'tags[0].value': value
+    }
+
+    response = requester.make_request_async(args)
+
+    response = response['createtagsresponse']
+
+    return response
+
+
+def _create_tag_response():
+    """
+    Generates a response for a create tag request.
+
+    @return: Response.
+    """
+    return {
+        'template_name_or_list': 'status.xml',
+        'response_type': 'CreateTagsResponse',
+        'return': 'true'
+    }
+
+
+@helpers.authentication_required
+def delete_tags():
+    """
+    delete a tag.
+
+    @return: Response.
+    """
+    _delete_tag_request()
+    return _delete_tag_response()
+
+
+def _delete_tag_request():
+    """
+    Request to delete a tag.
+
+    @return: Response.
+    """
+    key = helpers.get('Tag.1.Key')
+    resource_id = helpers.get('ResourceId.1')
+
+    args = {
+        'command': 'deleteTag',
+        'resourceIds': resource_id,
+        'tags[0].key': key
+    }
+
+    response = requester.make_request_async(args)
+
+    response = response['deletetagsresponse']
+
+    return response
+
+
+def _delete_tag_response():
+    """
+    Generates a response for a delete tag request.
+
+    @return: Response.
+    """
+    return {
+        'template_name_or_list': 'status.xml',
+        'response_type': 'DeleteTagsResponse',
+        'return': 'true'
+    }
+
+
+@helpers.authentication_required
+def describe_tags():
+    """
+    Describe all tags.
+
+    @return: Response.
+    """
+    args = {'command': 'listTags'}
+    response = requester.make_request(args)
+
+    return _describe_tags_response(
+        response
+    )
+
+
+def _describe_tags_response(response):
+    """
+    Generates a response for a describe tags request.
+
+    @param response: Response from Cloudstack.
+    @return: Response.
+    """
+    return {
+        'template_name_or_list': 'tags.xml',
+        'response_type': 'DescribeTagsResponse',
+        'response': response
+    }
diff --git a/ec2stack/templates/tags.xml b/ec2stack/templates/tags.xml
new file mode 100644
index 0000000..a28abdf
--- /dev/null
+++ b/ec2stack/templates/tags.xml
@@ -0,0 +1,11 @@
+{% extends "response.xml" %}
+{% block response_content %}
+    {% for tag in response.tag %}
+        <item>
+            <resourceId>{{tag.resourceid}}</resourceId>
+            <resourceType>{{tag.resourcetype}}</resourceType>
+            <key>{{tag.key}}</key>
+            <value>{{tag.value}}</value>
+        </item>
+    {% endfor %}
+{% endblock %}
\ No newline at end of file