Repository: libcloud Updated Branches: refs/heads/trunk 0789cb58d -> b9a5586e9
Adding tagging feature to DimensionDataNodeDriver Closes #773 Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/b9a5586e Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/b9a5586e Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/b9a5586e Branch: refs/heads/trunk Commit: b9a5586e9672a64fcc618f087d8675c902a4b265 Parents: 0789cb5 Author: Jeffrey Dunham <jeffrey.a.dun...@gmail.com> Authored: Fri Apr 22 19:51:27 2016 -0400 Committer: anthony-shaw <anthonys...@apache.org> Committed: Sat Apr 23 13:20:05 2016 +1000 ---------------------------------------------------------------------- libcloud/common/dimensiondata.py | 84 +++- libcloud/compute/drivers/dimensiondata.py | 373 ++++++++++++++++++ .../fixtures/dimensiondata/tag_applyTags.xml | 6 + .../dimensiondata/tag_applyTags_BADREQUEST.xml | 6 + .../fixtures/dimensiondata/tag_createTagKey.xml | 7 + .../tag_createTagKey_BADREQUEST.xml | 6 + .../fixtures/dimensiondata/tag_deleteTagKey.xml | 6 + .../tag_deleteTagKey_BADREQUEST.xml | 6 + .../fixtures/dimensiondata/tag_editTagKey.xml | 6 + .../dimensiondata/tag_editTagKey_BADREQUEST.xml | 6 + .../fixtures/dimensiondata/tag_removeTag.xml | 6 + .../dimensiondata/tag_removeTag_BADREQUEST.xml | 6 + ...Key_5ab77f5f_5aa9_426f_8459_4eab34e03d54.xml | 6 + ...f_5aa9_426f_8459_4eab34e03d54_BADREQUEST.xml | 6 + .../fixtures/dimensiondata/tag_tagKey_list.xml | 19 + .../dimensiondata/tag_tagKey_list_SINGLE.xml | 8 + .../fixtures/dimensiondata/tag_tag_list.xml | 36 ++ libcloud/test/compute/test_dimensiondata.py | 388 +++++++++++++++++++ 18 files changed, 980 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/common/dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/common/dimensiondata.py b/libcloud/common/dimensiondata.py index 50870e7..8410106 100644 --- a/libcloud/common/dimensiondata.py +++ b/libcloud/common/dimensiondata.py @@ -472,7 +472,8 @@ class DimensionDataConnection(ConnectionUserAndKey): yield paged_resp paged_resp = paged_resp or {} - while paged_resp.get('pageCount') >= paged_resp.get('pageSize'): + while int(paged_resp.get('pageCount')) >= \ + int(paged_resp.get('pageSize')): params['pageNumber'] = int(paged_resp.get('pageNumber')) + 1 paged_resp = self.request_with_orgId_api_2(action, params, data, headers, @@ -1408,3 +1409,84 @@ class DimensionDataBackupSchedulePolicy(object): def __repr__(self): return (('<DimensionDataBackupSchedulePolicy: name=%s>') % (self.name)) + + +class DimensionDataTag(object): + """ + A representation of a Tag in Dimension Data + A Tag first must have a Tag Key, then an asset is tag with + a key and an option value. Tags can be queried later to filter assets + and also show up on usage report if so desired. + """ + def __init__(self, asset_type, asset_id, asset_name, + datacenter, key, value): + """ + Initialize an instance of :class:`DimensionDataTag` + + :param asset_type: The type of asset. Current asset types: + SERVER, VLAN, NETWORK_DOMAIN, CUSTOMER_IMAGE, + PUBLIC_IP_BLOCK, ACCOUNT + :type asset_type: ``str`` + + :param asset_id: The GUID of the asset that is tagged + :type asset_id: ``str`` + + :param asset_name: The name of the asset that is tagged + :type asset_name: ``str`` + + :param datacenter: The short datacenter name of the tagged asset + :type datacenter: ``str`` + + :param key: The tagged key + :type key: :class:`DimensionDataTagKey` + + :param value: The tagged value + :type value: ``None`` or ``str`` + """ + self.asset_type = asset_type + self.asset_id = asset_id + self.asset_name = asset_name + self.datacenter = datacenter + self.key = key + self.value = value + + def __repr__(self): + return (('<DimensionDataTag: asset_name=%s, tag_name=%s, value=%s>') + % (self.asset_name, self.key.name, self.value)) + + +class DimensionDataTagKey(object): + """ + A representation of a Tag Key in Dimension Data + A tag key is required to tag an asset + """ + def __init__(self, id, name, description, + value_required, display_on_report): + """ + Initialize an instance of :class:`DimensionDataTagKey` + + :param id: GUID of the tag key + :type id: ``str`` + + :param name: Name of the tag key + :type name: ``str`` + + :param description: Description of the tag key + :type description: ``str`` + + :param value_required: If a value is required for this tag key + :type value_required: ``bool`` + + :param display_on_report: If this tag key should be displayed on + usage reports + :type display_on_report: ``bool`` + """ + self.id = id + self.name = name + self.description = description + self.value_required = value_required + self.display_on_report = display_on_report + + def __repr__(self): + return (('<DimensionDataTagKey: name=%s>') + % (self.name)) http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/compute/drivers/dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/dimensiondata.py b/libcloud/compute/drivers/dimensiondata.py index 7a969e8..a3881f6 100644 --- a/libcloud/compute/drivers/dimensiondata.py +++ b/libcloud/compute/drivers/dimensiondata.py @@ -39,6 +39,8 @@ from libcloud.common.dimensiondata import DimensionDataFirewallAddress from libcloud.common.dimensiondata import DimensionDataNatRule from libcloud.common.dimensiondata import DimensionDataAntiAffinityRule from libcloud.common.dimensiondata import NetworkDomainServicePlan +from libcloud.common.dimensiondata import DimensionDataTagKey +from libcloud.common.dimensiondata import DimensionDataTag from libcloud.common.dimensiondata import API_ENDPOINTS, DEFAULT_REGION from libcloud.common.dimensiondata import TYPES_URN from libcloud.common.dimensiondata import SERVER_NS, NETWORK_NS, GENERAL_NS @@ -73,6 +75,14 @@ NODE_STATE_MAP = { NodeState.RECONFIGURING, } +OBJECT_TO_TAGGING_ASSET_TYPE_MAP = { + 'Node': 'SERVER', + 'NodeImage': 'CUSTOMER_IMAGE', + 'DimensionDataNetworkDomain': 'NETWORK_DOMAIN', + 'DimensionDataVlan': 'VLAN', + 'DimensionDataPublicIpBlock': 'PUBLIC_IP_BLOCK' +} + class DimensionDataNodeDriver(NodeDriver): """ @@ -1959,10 +1969,361 @@ class DimensionDataNodeDriver(NodeDriver): raise e return self.ex_get_customer_image_by_id(id) + def ex_create_tag_key(self, name, description=None, + value_required=True, display_on_report=True): + """ + Creates a tag key in the Dimension Data Cloud + + :param name: The name of the tag key (required) + :type name: ``str`` + + :param description: The description of the tag key + :type description: ``str`` + + :param value_required: If a value is required for the tag + Tags themselves can be just a tag, + or be a key/value pair + :type value_required: ``bool`` + + :param display_on_report: Should this key show up on the usage reports + :type display_on_report: ``bool`` + + :rtype: ``bool`` + """ + create_tag_key = ET.Element('createTagKey', {'xmlns': TYPES_URN}) + ET.SubElement(create_tag_key, 'name').text = name + if description is not None: + ET.SubElement(create_tag_key, 'description').text = description + ET.SubElement(create_tag_key, 'valueRequired').text = \ + str(value_required).lower() + ET.SubElement(create_tag_key, 'displayOnReport').text = \ + str(display_on_report).lower() + response = self.connection.request_with_orgId_api_2( + 'tag/createTagKey', + method='POST', + data=ET.tostring(create_tag_key)).object + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_list_tag_keys(self, id=None, name=None, + value_required=None, display_on_report=None): + """ + List tag keys in the Dimension Data Cloud + + :param id: Filter the list to the id of the tag key + :type id: ``str`` + + :param name: Filter the list to the name of the tag key + :type name: ``str`` + + :param value_required: Filter the list to if a value is required + for a tag key + :type value_required: ``bool`` + + :param display_on_report: Filter the list to if the tag key should + show up on usage reports + :type display_on_report: ``bool`` + + :rtype: ``list`` of :class:`DimensionDataTagKey` + """ + params = {} + if id is not None: + params['id'] = id + if name is not None: + params['name'] = name + if value_required is not None: + params['valueRequired'] = str(value_required).lower() + if display_on_report is not None: + params['displayOnReport'] = str(display_on_report).lower() + + paged_result = self.connection.paginated_request_with_orgId_api_2( + 'tag/tagKey', + method='GET', + params=params + ) + + tag_keys = [] + for result in paged_result: + tag_keys.extend(self._to_tag_keys(result)) + return tag_keys + + def ex_get_tag_key_by_id(self, id): + """ + Get a specific tag key by ID + + :param id: ID of the tag key you want (required) + :type id: ``str`` + + :rtype: :class:`DimensionDataTagKey` + """ + tag_key = self.connection.request_with_orgId_api_2( + 'tag/tagKey/%s' % id).object + return self._to_tag_key(tag_key) + + def ex_get_tag_key_by_name(self, name): + """ + Get a specific tag key by Name + + :param name: Name of the tag key you want (required) + :type name: ``str`` + + :rtype: :class:`DimensionDataTagKey` + """ + tag_keys = self.ex_list_tag_keys(name=name) + if len(tag_keys) != 1: + raise ValueError("No tags found with name %s" % name) + return tag_keys[0] + + def ex_modify_tag_key(self, tag_key, name=None, description=None, + value_required=None, display_on_report=None): + + """ + Modify a specific tag key + + :param tag_key: The tag key you want to modify (required) + :type tag_key: :class:`DimensionDataTagKey` or ``str`` + + :param name: Set to modifiy the name of the tag key + :type name: ``str`` + + :param description: Set to modify the description of the tag key + :type description: ``str`` + + :param value_required: Set to modify if a value is required for + the tag key + :type value_required: ``bool`` + + :param display_on_report: Set to modify if this tag key should display + on the usage reports + :type display_on_report: ``bool`` + + :rtype: ``bool`` + """ + tag_key_id = self._tag_key_to_tag_key_id(tag_key) + modify_tag_key = ET.Element('editTagKey', + {'xmlns': TYPES_URN, 'id': tag_key_id}) + if name is not None: + ET.SubElement(modify_tag_key, 'name').text = name + if description is not None: + ET.SubElement(modify_tag_key, 'description').text = description + if value_required is not None: + ET.SubElement(modify_tag_key, 'valueRequired').text = \ + str(value_required).lower() + if display_on_report is not None: + ET.SubElement(modify_tag_key, 'displayOnReport').text = \ + str(display_on_report).lower() + + response = self.connection.request_with_orgId_api_2( + 'tag/editTagKey', + method='POST', + data=ET.tostring(modify_tag_key)).object + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_remove_tag_key(self, tag_key): + """ + Modify a specific tag key + + :param tag_key: The tag key you want to remove (required) + :type tag_key: :class:`DimensionDataTagKey` or ``str`` + + :rtype: ``bool`` + """ + tag_key_id = self._tag_key_to_tag_key_id(tag_key) + remove_tag_key = ET.Element('deleteTagKey', + {'xmlns': TYPES_URN, 'id': tag_key_id}) + response = self.connection.request_with_orgId_api_2( + 'tag/deleteTagKey', + method='POST', + data=ET.tostring(remove_tag_key)).object + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_apply_tag_to_asset(self, asset, tag_key, value=None): + """ + Apply a tag to a Dimension Data Asset + + :param asset: The asset to apply a tag to. (required) + :type asset: :class:`Node` or :class:`NodeImage` or + :class:`DimensionDataNewtorkDomain` or + :class:`DimensionDataVlan` or + :class:`DimensionDataPublicIpBlock` + + :param tag_key: The tag_key to apply to the asset. (required) + :type tag_key: :class:`DimensionDataTagKey` or ``str`` + + :param value: The value to be assigned to the tag key + This is only required if the :class:`DimensionDataTagKey` + requires it + :type value: ``str`` + + :rtype: ``bool`` + """ + asset_type = self._get_tagging_asset_type(asset) + tag_key_name = self._tag_key_to_tag_key_name(tag_key) + + apply_tags = ET.Element('applyTags', {'xmlns': TYPES_URN}) + ET.SubElement(apply_tags, 'assetType').text = asset_type + ET.SubElement(apply_tags, 'assetId').text = asset.id + + tag_ele = ET.SubElement(apply_tags, 'tag') + ET.SubElement(tag_ele, 'tagKeyName').text = tag_key_name + if value is not None: + ET.SubElement(tag_ele, 'value').text = value + + response = self.connection.request_with_orgId_api_2( + 'tag/applyTags', + method='POST', + data=ET.tostring(apply_tags)).object + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_remove_tag_from_asset(self, asset, tag_key): + """ + Remove a tag from an asset + + :param asset: The asset to remove a tag from. (required) + :type asset: :class:`Node` or :class:`NodeImage` or + :class:`DimensionDataNewtorkDomain` or + :class:`DimensionDataVlan` or + :class:`DimensionDataPublicIpBlock` + + :param tag_key: The tag key you want to remove (required) + :type tag_key: :class:`DimensionDataTagKey` or ``str`` + + :rtype: ``bool`` + """ + asset_type = self._get_tagging_asset_type(asset) + tag_key_name = self._tag_key_to_tag_key_name(tag_key) + + apply_tags = ET.Element('removeTags', {'xmlns': TYPES_URN}) + ET.SubElement(apply_tags, 'assetType').text = asset_type + ET.SubElement(apply_tags, 'assetId').text = asset.id + ET.SubElement(apply_tags, 'tagKeyName').text = tag_key_name + response = self.connection.request_with_orgId_api_2( + 'tag/removeTags', + method='POST', + data=ET.tostring(apply_tags)).object + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_list_tags(self, asset_id=None, asset_type=None, location=None, + tag_key_name=None, tag_key_id=None, value=None, + value_required=None, display_on_report=None): + """ + List tags in the Dimension Data Cloud + + :param asset_id: Filter the list by asset id + :type asset_id: ``str`` + + :param asset_type: Filter the list by asset type + :type asset_type: ``str`` + + :param location: Filter the list by the assets location + :type location: :class:``NodeLocation`` or ``str`` + + :param tag_key_name: Filter the list by a tag key name + :type tag_key_name: ``str`` + + :param tag_key_id: Filter the list by a tag key id + :type tag_key_id: ``str`` + + :param value: Filter the list by a tag value + :type value: ``str`` + + :param value_required: Filter the list to if a value is required + for a tag + :type value_required: ``bool`` + + :param display_on_report: Filter the list to if the tag should + show up on usage reports + :type display_on_report: ``bool`` + + :rtype: ``list`` of :class:`DimensionDataTag` + """ + params = {} + if asset_id is not None: + params['assetId'] = asset_id + if asset_type is not None: + params['assetType'] = asset_type + if location is not None: + params['datacenterId'] = self._location_to_location_id(location) + if tag_key_name is not None: + params['tagKeyName'] = tag_key_name + if tag_key_id is not None: + params['tagKeyId'] = tag_key_id + if value is not None: + params['value'] = value + if value_required is not None: + params['valueRequired'] = str(value_required).lower() + if display_on_report is not None: + params['displayOnReport'] = str(display_on_report).lower() + + paged_result = self.connection.paginated_request_with_orgId_api_2( + 'tag/tag', + method='GET', + params=params + ) + + tags = [] + for result in paged_result: + tags.extend(self._to_tags(result)) + return tags + + @staticmethod + def _get_tagging_asset_type(asset): + objecttype = type(asset) + if objecttype.__name__ in OBJECT_TO_TAGGING_ASSET_TYPE_MAP: + return OBJECT_TO_TAGGING_ASSET_TYPE_MAP[objecttype.__name__] + raise TypeError("Asset type %s cannot be tagged" % objecttype.__name__) + def _list_nodes_single_page(self, params={}): return self.connection.request_with_orgId_api_2( 'server/server', params=params).object + def _to_tags(self, object): + tags = [] + for element in object.findall(fixxpath('tag', TYPES_URN)): + tags.append(self._to_tag(element)) + return tags + + def _to_tag(self, element): + tag_key = self._to_tag_key(element, from_tag_api=True) + return DimensionDataTag( + asset_type=findtext(element, 'assetType', TYPES_URN), + asset_id=findtext(element, 'assetId', TYPES_URN), + asset_name=findtext(element, 'assetId', TYPES_URN), + datacenter=findtext(element, 'datacenterId', TYPES_URN), + key=tag_key, + value=findtext(element, 'value', TYPES_URN) + ) + + def _to_tag_keys(self, object): + keys = [] + for element in object.findall(fixxpath('tagKey', TYPES_URN)): + keys.append(self._to_tag_key(element)) + return keys + + def _to_tag_key(self, element, from_tag_api=False): + if from_tag_api: + id = findtext(element, 'tagKeyId', TYPES_URN) + name = findtext(element, 'tagKeyName', TYPES_URN) + else: + id = element.get('id') + name = findtext(element, 'name', TYPES_URN) + + return DimensionDataTagKey( + id=id, + name=name, + description=findtext(element, 'description', TYPES_URN), + value_required=self._str2bool( + findtext(element, 'valueRequired', TYPES_URN) + ), + display_on_report=self._str2bool( + findtext(element, 'displayOnReport', TYPES_URN) + ) + ) + def _to_images(self, object, el_name='osImage'): images = [] locations = self.list_locations() @@ -2382,3 +2743,15 @@ class DimensionDataNodeDriver(NodeDriver): @staticmethod def _network_domain_to_network_domain_id(network_domain): return dd_object_to_id(network_domain, DimensionDataNetworkDomain) + + @staticmethod + def _tag_key_to_tag_key_id(tag_key): + return dd_object_to_id(tag_key, DimensionDataTagKey) + + @staticmethod + def _tag_key_to_tag_key_name(tag_key): + return dd_object_to_id(tag_key, DimensionDataTagKey, id_value='name') + + @staticmethod + def _str2bool(string): + return string.lower() in ("true") http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_applyTags.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_applyTags.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_applyTags.xml new file mode 100644 index 0000000..91114bb --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_applyTags.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160421T194729995-0400_3409863f-ad9c-4381-b841-762e311aaebc"> + <operation>APPLY_TAGS</operation> + <responseCode>OK</responseCode> + <message>Tag(s) successfully applied.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_applyTags_BADREQUEST.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_applyTags_BADREQUEST.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_applyTags_BADREQUEST.xml new file mode 100644 index 0000000..dec66ad --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_applyTags_BADREQUEST.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160421T194841441-0400_567f2bdf-88a5-4460-87be-1cc972eeb34c"> + <operation>APPLY_TAGS</operation> + <responseCode>RESOURCE_NOT_FOUND</responseCode> + <message>Tag Key(s) (ChangeNameTes) not found.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_createTagKey.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_createTagKey.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_createTagKey.xml new file mode 100644 index 0000000..927812d --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_createTagKey.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160421T171444589-0400_7abb497e-403a-4b4a-a073-671212e77a14"> + <operation>CREATE_TAG_KEY</operation> + <responseCode>OK</responseCode> + <message>Tag Key 'MyTestKey' has been created.</message> + <info name="tagKeyId" value="4f921962-402d-438d-aa37-6f6a0392a1a9"/> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_createTagKey_BADREQUEST.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_createTagKey_BADREQUEST.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_createTagKey_BADREQUEST.xml new file mode 100644 index 0000000..eaa73ab --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_createTagKey_BADREQUEST.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160421T171806338-0400_7606f82d-900e-41a7-864d-de4412c38eaf"> + <operation>CREATE_TAG_KEY</operation> + <responseCode>NAME_NOT_UNIQUE</responseCode> + <message>Another Tag Key named 'MyTestKey' already exists.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_deleteTagKey.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_deleteTagKey.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_deleteTagKey.xml new file mode 100644 index 0000000..9fa8b59 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_deleteTagKey.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160421T193459513-0400_d0f68e46-acd1-4af0-a41c-21b6e90695cb"> + <operation>DELETE_TAG_KEY</operation> + <responseCode>OK</responseCode> + <message>Tag Key (Id:4f921962-402d-438d-aa37-6f6a0392a1a9) has been deleted.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_deleteTagKey_BADREQUEST.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_deleteTagKey_BADREQUEST.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_deleteTagKey_BADREQUEST.xml new file mode 100644 index 0000000..357f2d8 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_deleteTagKey_BADREQUEST.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160421T193649125-0400_88027795-0516-4f8d-be8d-15e7505adcef"> + <operation>DELETE_TAG_KEY</operation> + <responseCode>RESOURCE_NOT_FOUND</responseCode> + <message>Tag Key fdafdsa not found.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_editTagKey.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_editTagKey.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_editTagKey.xml new file mode 100644 index 0000000..f5e4d77 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_editTagKey.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160421T192343920-0400_d0381407-8141-4ddb-a08e-dae4ddde5cda"> + <operation>EDIT_TAG_KEY</operation> + <responseCode>OK</responseCode> + <message>Tag Key 'ChangeNameTest' has been edited.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_editTagKey_BADREQUEST.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_editTagKey_BADREQUEST.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_editTagKey_BADREQUEST.xml new file mode 100644 index 0000000..cb0c706 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_editTagKey_BADREQUEST.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160421T192212756-0400_1439e70b-a681-4d5b-939d-dc4264f0f66d"> + <operation>EDIT_TAG_KEY</operation> + <responseCode>NO_CHANGE</responseCode> + <message>At least one of name, description, valueRequired or displayOnReport must be changed from its current value.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_removeTag.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_removeTag.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_removeTag.xml new file mode 100644 index 0000000..1d3c9a3 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_removeTag.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160422T150018531-0400_f9fe6110-6156-4f8b-8c6e-97927e87f744"> + <operation>REMOVE_TAGS</operation> + <responseCode>OK</responseCode> + <message>Tag(s) successfully removed.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_removeTag_BADREQUEST.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_removeTag_BADREQUEST.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_removeTag_BADREQUEST.xml new file mode 100644 index 0000000..c686a20 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_removeTag_BADREQUEST.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160422T181236250-0400_0aca7bf9-2a5c-4650-b648-d9356cb3309b"> + <operation>REMOVE_TAGS</operation> + <responseCode>RESOURCE_NOT_FOUND</responseCode> + <message>Tag Key(s) (AaronTestModified) not applied to Server eb222a4a-fffd-4e4a-8346-1279ef621ab0.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54.xml new file mode 100644 index 0000000..6d1aa13 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tagKey xmlns="urn:didata.com:api:cloud:types" id="5ab77f5f-5aa9-426f-8459-4eab34e03d54"> + <name>LibcloudTest</name> + <valueRequired>true</valueRequired> + <displayOnReport>true</displayOnReport> +</tagKey> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54_BADREQUEST.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54_BADREQUEST.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54_BADREQUEST.xml new file mode 100644 index 0000000..d585d5a --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54_BADREQUEST.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<response xmlns="urn:didata.com:api:cloud:types" requestId="na_20160421T190934127-0400_6504c4a8-afa1-476f-b9d4-a01d8e52d599"> + <operation>GET_TAG_KEY</operation> + <responseCode>RESOURCE_NOT_FOUND</responseCode> + <message>Tag Key 5ab77f5f-5aa9-426f-8459-4eab34e03d5 not found.</message> +</response> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_list.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_list.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_list.xml new file mode 100644 index 0000000..a75b8c5 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_list.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tagKeys xmlns="urn:didata.com:api:cloud:types" pageNumber="1" pageCount="3" totalCount="3" pageSize="250"> + <tagKey id="d047c609-93d7-4bc5-8fc9-732c85840075"> + <name>AaronTestModified</name> + <description>Testing for VMWare</description> + <valueRequired>true</valueRequired> + <displayOnReport>true</displayOnReport> + </tagKey> + <tagKey id="5ab77f5f-5aa9-426f-8459-4eab34e03d54"> + <name>LibcloudTest</name> + <valueRequired>true</valueRequired> + <displayOnReport>true</displayOnReport> + </tagKey> + <tagKey id="4f921962-402d-438d-aa37-6f6a0392a1a9"> + <name>MyTestKey</name> + <valueRequired>true</valueRequired> + <displayOnReport>true</displayOnReport> + </tagKey> +</tagKeys> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_list_SINGLE.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_list_SINGLE.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_list_SINGLE.xml new file mode 100644 index 0000000..8deaa96 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_tagKey_list_SINGLE.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tagKeys xmlns="urn:didata.com:api:cloud:types" pageNumber="1" pageCount="3" totalCount="3" pageSize="250"> + <tagKey id="5ab77f5f-5aa9-426f-8459-4eab34e03d54"> + <name>LibcloudTest</name> + <valueRequired>true</valueRequired> + <displayOnReport>true</displayOnReport> + </tagKey> +</tagKeys> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/fixtures/dimensiondata/tag_tag_list.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/dimensiondata/tag_tag_list.xml b/libcloud/test/compute/fixtures/dimensiondata/tag_tag_list.xml new file mode 100644 index 0000000..d79b648 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/tag_tag_list.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tags xmlns="urn:didata.com:api:cloud:types" pageNumber="1" pageCount="3" totalCount="3" pageSize="1000"> + <tag> + <assetType>SERVER</assetType> + <assetId>09242b55-3bc8-4cb7-b30c-4158267f58e6</assetId> + <assetName>App server</assetName> + <datacenterId>NA9</datacenterId> + <tagKeyId>5ab77f5f-5aa9-426f-8459-4eab34e03d54</tagKeyId> + <tagKeyName>ChangeNameTest</tagKeyName> + <value>No way!</value> + <displayOnReport>true</displayOnReport> + <valueRequired>true</valueRequired> + </tag> + <tag> + <assetType>NETWORK_DOMAIN</assetType> + <assetId>1a16bf5e-583b-42c9-af94-a92d9ee1607f</assetId> + <assetName>An Ho1a Demo</assetName> + <datacenterId>NA9</datacenterId> + <tagKeyId>d047c609-93d7-4bc5-8fc9-732c85840075</tagKeyId> + <tagKeyName>AaronTestModified</tagKeyName> + <value>Success</value> + <displayOnReport>true</displayOnReport> + <valueRequired>true</valueRequired> + </tag> + <tag> + <assetType>SERVER</assetType> + <assetId>77da591d-b58e-43ef-8bc2-ddde3f732893</assetId> + <assetName>Test 1</assetName> + <datacenterId>NA9</datacenterId> + <tagKeyId>d047c609-93d7-4bc5-8fc9-732c85840075</tagKeyId> + <tagKeyName>AaronTestModified</tagKeyName> + <value>Test VMware</value> + <displayOnReport>true</displayOnReport> + <valueRequired>true</valueRequired> + </tag> +</tags> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b9a5586e/libcloud/test/compute/test_dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_dimensiondata.py b/libcloud/test/compute/test_dimensiondata.py index 1e1fa2a..f4f03bc 100644 --- a/libcloud/test/compute/test_dimensiondata.py +++ b/libcloud/test/compute/test_dimensiondata.py @@ -25,6 +25,7 @@ from libcloud.utils.py3 import httplib from libcloud.common.types import InvalidCredsError from libcloud.common.dimensiondata import DimensionDataAPIException, NetworkDomainServicePlan from libcloud.common.dimensiondata import DimensionDataServerCpuSpecification, DimensionDataServerDisk, DimensionDataServerVMWareTools +from libcloud.common.dimensiondata import DimensionDataTag, DimensionDataTagKey from libcloud.common.dimensiondata import TYPES_URN from libcloud.compute.drivers.dimensiondata import DimensionDataNodeDriver as DimensionData from libcloud.compute.base import Node, NodeAuthPassword, NodeLocation @@ -964,6 +965,133 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin): with self.assertRaises(ValueError): self.driver.ex_list_anti_affinity_rules(network='fake_network', network_domain='fake_network_domain') + def test_ex_create_tag_key(self): + success = self.driver.ex_create_tag_key('MyTestKey') + self.assertTrue(success) + + def test_ex_create_tag_key_ALLPARAMS(self): + self.driver.connection._get_orgId() + DimensionDataMockHttp.type = 'ALLPARAMS' + success = self.driver.ex_create_tag_key('MyTestKey', description="Test Key Desc.", value_required=False, display_on_report=False) + self.assertTrue(success) + + def test_ex_create_tag_key_BADREQUEST(self): + self.driver.connection._get_orgId() + DimensionDataMockHttp.type = 'BADREQUEST' + with self.assertRaises(DimensionDataAPIException): + self.driver.ex_create_tag_key('MyTestKey') + + def test_ex_list_tag_keys(self): + tag_keys = self.driver.ex_list_tag_keys() + self.assertTrue(isinstance(tag_keys, list)) + self.assertTrue(isinstance(tag_keys[0], DimensionDataTagKey)) + self.assertTrue(isinstance(tag_keys[0].id, str)) + + def test_ex_list_tag_keys_ALLFILTERS(self): + self.driver.connection._get_orgId() + DimensionDataMockHttp.type = 'ALLFILTERS' + self.driver.ex_list_tag_keys(id='fake_id', name='fake_name', value_required=False, display_on_report=False) + + def test_ex_get_tag_by_id(self): + tag = self.driver.ex_get_tag_key_by_id('d047c609-93d7-4bc5-8fc9-732c85840075') + self.assertTrue(isinstance(tag, DimensionDataTagKey)) + + def test_ex_get_tag_by_id_NOEXIST(self): + self.driver.connection._get_orgId() + DimensionDataMockHttp.type = 'NOEXIST' + with self.assertRaises(DimensionDataAPIException): + self.driver.ex_get_tag_key_by_id('d047c609-93d7-4bc5-8fc9-732c85840075') + + def test_ex_get_tag_by_name(self): + self.driver.connection._get_orgId() + DimensionDataMockHttp.type = 'SINGLE' + tag = self.driver.ex_get_tag_key_by_name('LibcloudTest') + self.assertTrue(isinstance(tag, DimensionDataTagKey)) + + def test_ex_get_tag_by_name_NOEXIST(self): + with self.assertRaises(ValueError): + self.driver.ex_get_tag_key_by_name('LibcloudTest') + + def test_ex_modify_tag_key_NAME(self): + tag_key = self.driver.ex_list_tag_keys()[0] + DimensionDataMockHttp.type = 'NAME' + success = self.driver.ex_modify_tag_key(tag_key, name='NewName') + self.assertTrue(success) + + def test_ex_modify_tag_key_NOTNAME(self): + tag_key = self.driver.ex_list_tag_keys()[0] + DimensionDataMockHttp.type = 'NOTNAME' + success = self.driver.ex_modify_tag_key(tag_key, description='NewDesc', value_required=False, display_on_report=True) + self.assertTrue(success) + + def test_ex_modify_tag_key_NOCHANGE(self): + tag_key = self.driver.ex_list_tag_keys()[0] + DimensionDataMockHttp.type = 'NOCHANGE' + with self.assertRaises(DimensionDataAPIException): + self.driver.ex_modify_tag_key(tag_key) + + def test_ex_remove_tag_key(self): + tag_key = self.driver.ex_list_tag_keys()[0] + success = self.driver.ex_remove_tag_key(tag_key) + self.assertTrue(success) + + def test_ex_remove_tag_key_NOEXIST(self): + tag_key = self.driver.ex_list_tag_keys()[0] + DimensionDataMockHttp.type = 'NOEXIST' + with self.assertRaises(DimensionDataAPIException): + self.driver.ex_remove_tag_key(tag_key) + + def test_ex_apply_tag_to_asset(self): + node = self.driver.list_nodes()[0] + success = self.driver.ex_apply_tag_to_asset(node, 'TagKeyName', 'FakeValue') + self.assertTrue(success) + + def test_ex_apply_tag_to_asset_NOVALUE(self): + node = self.driver.list_nodes()[0] + DimensionDataMockHttp.type = 'NOVALUE' + success = self.driver.ex_apply_tag_to_asset(node, 'TagKeyName') + self.assertTrue(success) + + def test_ex_apply_tag_to_asset_NOTAGKEY(self): + node = self.driver.list_nodes()[0] + DimensionDataMockHttp.type = 'NOTAGKEY' + with self.assertRaises(DimensionDataAPIException): + self.driver.ex_apply_tag_to_asset(node, 'TagKeyNam') + + def test_ex_apply_tag_to_asset_BADASSETTYPE(self): + network = self.driver.list_networks()[0] + DimensionDataMockHttp.type = 'NOTAGKEY' + with self.assertRaises(TypeError): + self.driver.ex_apply_tag_to_asset(network, 'TagKeyNam') + + def test_ex_remove_tag_from_asset(self): + node = self.driver.list_nodes()[0] + success = self.driver.ex_remove_tag_from_asset(node, 'TagKeyName') + self.assertTrue(success) + + def test_ex_remove_tag_from_asset_NOTAG(self): + node = self.driver.list_nodes()[0] + DimensionDataMockHttp.type = 'NOTAG' + with self.assertRaises(DimensionDataAPIException): + self.driver.ex_remove_tag_from_asset(node, 'TagKeyNam') + + def test_ex_list_tags(self): + tags = self.driver.ex_list_tags() + self.assertTrue(isinstance(tags, list)) + self.assertTrue(isinstance(tags[0], DimensionDataTag)) + self.assertTrue(len(tags) == 3) + + def test_ex_list_tags_ALLPARAMS(self): + self.driver.connection._get_orgId() + DimensionDataMockHttp.type = 'ALLPARAMS' + tags = self.driver.ex_list_tags(asset_id='fake_asset_id', asset_type='fake_asset_type', + location='fake_location', tag_key_name='fake_tag_key_name', + tag_key_id='fake_tag_key_id', value='fake_value', + value_required=False, display_on_report=False) + self.assertTrue(isinstance(tags, list)) + self.assertTrue(isinstance(tags[0], DimensionDataTag)) + self.assertTrue(len(tags) == 3) + def test_priv_location_to_location_id(self): location = self.driver.ex_get_location_by_id('NA9') self.assertEqual( @@ -1757,6 +1885,266 @@ class DimensionDataMockHttp(MockHttp): 'server_removeDisk.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_createTagKey(self, method, url, body, headers): + request = ET.fromstring(body) + if request.tag != "{urn:didata.com:api:cloud:types}createTagKey": + raise InvalidRequestError(request.tag) + name = findtext(request, 'name', TYPES_URN) + description = findtext(request, 'description', TYPES_URN) + value_required = findtext(request, 'valueRequired', TYPES_URN) + display_on_report = findtext(request, 'displayOnReport', TYPES_URN) + if name is None: + raise ValueError("Name must have a value in the request") + if description is not None: + raise ValueError("Default description for a tag should be blank") + if value_required is None or value_required != 'true': + raise ValueError("Default valueRequired should be true") + if display_on_report is None or display_on_report != 'true': + raise ValueError("Default displayOnReport should be true") + + body = self.fixtures.load( + 'tag_createTagKey.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_createTagKey_ALLPARAMS(self, method, url, body, headers): + request = ET.fromstring(body) + if request.tag != "{urn:didata.com:api:cloud:types}createTagKey": + raise InvalidRequestError(request.tag) + name = findtext(request, 'name', TYPES_URN) + description = findtext(request, 'description', TYPES_URN) + value_required = findtext(request, 'valueRequired', TYPES_URN) + display_on_report = findtext(request, 'displayOnReport', TYPES_URN) + if name is None: + raise ValueError("Name must have a value in the request") + if description is None: + raise ValueError("Description should have a value") + if value_required is None or value_required != 'false': + raise ValueError("valueRequired should be false") + if display_on_report is None or display_on_report != 'false': + raise ValueError("displayOnReport should be false") + + body = self.fixtures.load( + 'tag_createTagKey.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_createTagKey_BADREQUEST(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_createTagKey_BADREQUEST.xml') + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_tagKey(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_tagKey_list.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_tagKey_SINGLE(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_tagKey_list_SINGLE.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_tagKey_ALLFILTERS(self, method, url, body, headers): + (_, params) = url.split('?') + parameters = params.split('&') + for parameter in parameters: + (key, value) = parameter.split('=') + if key == 'id': + assert value == 'fake_id' + elif key == 'name': + assert value == 'fake_name' + elif key == 'valueRequired': + assert value == 'false' + elif key == 'displayOnReport': + assert value == 'false' + elif key == 'pageSize': + assert value == '250' + else: + raise ValueError("Could not find in url parameters {0}:{1}".format(key, value)) + body = self.fixtures.load( + 'tag_tagKey_list.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_tagKey_d047c609_93d7_4bc5_8fc9_732c85840075(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_tagKey_d047c609_93d7_4bc5_8fc9_732c85840075_NOEXIST(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_tagKey_5ab77f5f_5aa9_426f_8459_4eab34e03d54_BADREQUEST.xml' + ) + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_editTagKey_NAME(self, method, url, body, headers): + request = ET.fromstring(body) + if request.tag != "{urn:didata.com:api:cloud:types}editTagKey": + raise InvalidRequestError(request.tag) + name = findtext(request, 'name', TYPES_URN) + description = findtext(request, 'description', TYPES_URN) + value_required = findtext(request, 'valueRequired', TYPES_URN) + display_on_report = findtext(request, 'displayOnReport', TYPES_URN) + if name is None: + raise ValueError("Name must have a value in the request") + if description is not None: + raise ValueError("Description should be empty") + if value_required is not None: + raise ValueError("valueRequired should be empty") + if display_on_report is not None: + raise ValueError("displayOnReport should be empty") + body = self.fixtures.load( + 'tag_editTagKey.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_editTagKey_NOTNAME(self, method, url, body, headers): + request = ET.fromstring(body) + if request.tag != "{urn:didata.com:api:cloud:types}editTagKey": + raise InvalidRequestError(request.tag) + name = findtext(request, 'name', TYPES_URN) + description = findtext(request, 'description', TYPES_URN) + value_required = findtext(request, 'valueRequired', TYPES_URN) + display_on_report = findtext(request, 'displayOnReport', TYPES_URN) + if name is not None: + raise ValueError("Name should be empty") + if description is None: + raise ValueError("Description should not be empty") + if value_required is None: + raise ValueError("valueRequired should not be empty") + if display_on_report is None: + raise ValueError("displayOnReport should not be empty") + body = self.fixtures.load( + 'tag_editTagKey.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_editTagKey_NOCHANGE(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_editTagKey_BADREQUEST.xml' + ) + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_deleteTagKey(self, method, url, body, headers): + request = ET.fromstring(body) + if request.tag != "{urn:didata.com:api:cloud:types}deleteTagKey": + raise InvalidRequestError(request.tag) + body = self.fixtures.load( + 'tag_deleteTagKey.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_deleteTagKey_NOEXIST(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_deleteTagKey_BADREQUEST.xml' + ) + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_applyTags(self, method, url, body, headers): + request = ET.fromstring(body) + if request.tag != "{urn:didata.com:api:cloud:types}applyTags": + raise InvalidRequestError(request.tag) + asset_type = findtext(request, 'assetType', TYPES_URN) + asset_id = findtext(request, 'assetId', TYPES_URN) + tag = request.find(fixxpath('tag', TYPES_URN)) + tag_key_name = findtext(tag, 'tagKeyName', TYPES_URN) + value = findtext(tag, 'value', TYPES_URN) + if asset_type is None: + raise ValueError("assetType should not be empty") + if asset_id is None: + raise ValueError("assetId should not be empty") + if tag_key_name is None: + raise ValueError("tagKeyName should not be empty") + if value is None: + raise ValueError("value should not be empty") + + body = self.fixtures.load( + 'tag_applyTags.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_applyTags_NOVALUE(self, method, url, body, headers): + request = ET.fromstring(body) + if request.tag != "{urn:didata.com:api:cloud:types}applyTags": + raise InvalidRequestError(request.tag) + asset_type = findtext(request, 'assetType', TYPES_URN) + asset_id = findtext(request, 'assetId', TYPES_URN) + tag = request.find(fixxpath('tag', TYPES_URN)) + tag_key_name = findtext(tag, 'tagKeyName', TYPES_URN) + value = findtext(tag, 'value', TYPES_URN) + if asset_type is None: + raise ValueError("assetType should not be empty") + if asset_id is None: + raise ValueError("assetId should not be empty") + if tag_key_name is None: + raise ValueError("tagKeyName should not be empty") + if value is not None: + raise ValueError("value should be empty") + + body = self.fixtures.load( + 'tag_applyTags.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_applyTags_NOTAGKEY(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_applyTags_BADREQUEST.xml' + ) + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_removeTags(self, method, url, body, headers): + request = ET.fromstring(body) + if request.tag != "{urn:didata.com:api:cloud:types}removeTags": + raise InvalidRequestError(request.tag) + body = self.fixtures.load( + 'tag_removeTag.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_removeTags_NOTAG(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_removeTag_BADREQUEST.xml' + ) + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_tag(self, method, url, body, headers): + body = self.fixtures.load( + 'tag_tag_list.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_2_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_tag_tag_ALLPARAMS(self, method, url, body, headers): + (_, params) = url.split('?') + parameters = params.split('&') + for parameter in parameters: + (key, value) = parameter.split('=') + if key == 'assetId': + assert value == 'fake_asset_id' + elif key == 'assetType': + assert value == 'fake_asset_type' + elif key == 'valueRequired': + assert value == 'false' + elif key == 'displayOnReport': + assert value == 'false' + elif key == 'pageSize': + assert value == '250' + elif key == 'datacenterId': + assert value == 'fake_location' + elif key == 'value': + assert value == 'fake_value' + elif key == 'tagKeyName': + assert value == 'fake_tag_key_name' + elif key == 'tagKeyId': + assert value == 'fake_tag_key_id' + else: + raise ValueError("Could not find in url parameters {0}:{1}".format(key, value)) + body = self.fixtures.load( + 'tag_tag_list.xml' + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) if __name__ == '__main__': sys.exit(unittest.main())