Added Edit firewall rule feature and CRUD operation for IpAddressList, PortList in DD driver
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/2eb1b4a3 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/2eb1b4a3 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/2eb1b4a3 Branch: refs/heads/trunk Commit: 2eb1b4a32e1048d063da0ebea4cac01f2da60270 Parents: e6d582e Author: Samuel Chong <samuelcho...@gmail.com> Authored: Thu Oct 6 11:43:28 2016 +1100 Committer: Samuel Chong <samuelcho...@gmail.com> Committed: Thu Oct 6 11:43:28 2016 +1100 ---------------------------------------------------------------------- libcloud/compute/drivers/dimensiondata.py | 640 +++++++++++++++++++++++++ 1 file changed, 640 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/2eb1b4a3/libcloud/compute/drivers/dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/dimensiondata.py b/libcloud/compute/drivers/dimensiondata.py index 9bdeb86..89f7c66 100644 --- a/libcloud/compute/drivers/dimensiondata.py +++ b/libcloud/compute/drivers/dimensiondata.py @@ -38,6 +38,13 @@ from libcloud.common.dimensiondata import DimensionDataFirewallRule from libcloud.common.dimensiondata import DimensionDataFirewallAddress from libcloud.common.dimensiondata import DimensionDataNatRule from libcloud.common.dimensiondata import DimensionDataAntiAffinityRule +from libcloud.common.dimensiondata import DimensionDataIpAddressList +from libcloud.common.dimensiondata import DimensionDataChildIpAddressList +from libcloud.common.dimensiondata import DimensionDataIpAddress +from libcloud.common.dimensiondata import DimensionDataPortList +from libcloud.common.dimensiondata import DimensionDataPort +from libcloud.common.dimensiondata import DimensionDataChildPortList +from libcloud.common.dimensiondata import DimensionDataNic from libcloud.common.dimensiondata import NetworkDomainServicePlan from libcloud.common.dimensiondata import DimensionDataTagKey from libcloud.common.dimensiondata import DimensionDataTag @@ -1470,6 +1477,113 @@ class DimensionDataNodeDriver(NodeDriver): rule.id = rule_id return rule + def ex_edit_firewall_rule(self, rule, position, + relative_rule_for_position=None): + """ + Edit a firewall rule + + :param rule: (required) The rule in which to create + :type rule: :class:`DimensionDataFirewallRule` + + :param position: (required) There are two types of positions + with position_relative_to_rule arg and without it + With: 'BEFORE' or 'AFTER' + Without: 'FIRST' or 'LAST' + :type position: ``str`` + + :param relative_rule_for_position: (optional) The rule or rule name in + which to decide the relative rule + for positioning. + :type relative_rule_for_position: + :class:`DimensionDataFirewallRule` or ``str`` + + :rtype: ``bool`` + """ + + positions_without_rule = ('FIRST', 'LAST') + positions_with_rule = ('BEFORE', 'AFTER') + + edit_node = ET.Element('editFirewallRule', + {'xmlns': TYPES_URN, 'id': rule.id}) + ET.SubElement(edit_node, "action").text = rule.action + ET.SubElement(edit_node, "protocol").text = rule.protocol + + # Source address + source = ET.SubElement(edit_node, "source") + if rule.source.address_list_id is not None: + source_ip = ET.SubElement(source, 'ipAddressListId') + source_ip.text = rule.source.address_list_id + else: + source_ip = ET.SubElement(source, 'ip') + if rule.source.any_ip: + source_ip.set('address', 'ANY') + else: + source_ip.set('address', rule.source.ip_address) + if rule.source.ip_prefix_size is not None: + source_ip.set('prefixSize', + str(rule.source.ip_prefix_size)) + + # Setup source port rule + if rule.source.port_list_id is not None: + source_port = ET.SubElement(source, 'portListId') + source_port.text = rule.source.port_list_id + else: + if rule.source.port_begin is not None: + source_port = ET.SubElement(source, 'port') + source_port.set('begin', rule.source.port_begin) + if rule.source.port_end is not None: + source_port.set('end', rule.source.port_end) + # Setup destination port rule + dest = ET.SubElement(edit_node, "destination") + if rule.destination.address_list_id is not None: + dest_ip = ET.SubElement(dest, 'ipAddressListId') + dest_ip.text = rule.destination.address_list_id + else: + dest_ip = ET.SubElement(dest, 'ip') + if rule.destination.any_ip: + dest_ip.set('address', 'ANY') + else: + dest_ip.set('address', rule.destination.ip_address) + if rule.destination.ip_prefix_size is not None: + dest_ip.set('prefixSize', rule.destination.ip_prefix_size) + if rule.destination.port_list_id is not None: + dest_port = ET.SubElement(dest, 'portListId') + dest_port.text = rule.destination.port_list_id + else: + if rule.destination.port_begin is not None: + dest_port = ET.SubElement(dest, 'port') + dest_port.set('begin', rule.destination.port_begin) + if rule.destination.port_end is not None: + dest_port.set('end', rule.destination.port_end) + # Set up positioning of rule + ET.SubElement(edit_node, "enabled").text = str(rule.enabled).lower() + placement = ET.SubElement(edit_node, "placement") + if relative_rule_for_position is not None: + if position not in positions_with_rule: + raise ValueError("When position_relative_to_rule is specified" + " position must be %s" + % ', '.join(positions_with_rule)) + if isinstance(relative_rule_for_position, + DimensionDataFirewallRule): + rule_name = relative_rule_for_position.name + else: + rule_name = relative_rule_for_position + placement.set('relativeToRule', rule_name) + else: + if position not in positions_without_rule: + raise ValueError("When position_relative_to_rule is not" + " specified position must be %s" + % ', '.join(positions_without_rule)) + placement.set('position', position) + + response = self.connection.request_with_orgId_api_2( + 'network/editFirewallRule', + method='POST', + data=ET.tostring(edit_node)).object + + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + def ex_get_firewall_rule(self, network_domain, rule_id): locations = self.list_locations() rule = self.connection.request_with_orgId_api_2( @@ -2379,6 +2493,427 @@ class DimensionDataNodeDriver(NodeDriver): % (datacenter_id, start_date, end_date)) return self._format_csv(result.response) + def ex_list_ip_address_list(self, ex_network_domain): + """ + List IP Address List by network domain ID specified + + :param ex_network_domain: The network domain or network domain ID + :type ex_network_domain: :class:`DimensionDataNetworkDomain` or 'str' + + :return: a list of DimensionDataIpAddressList objects + :rtype: ``list`` of :class:`DimensionDataIpAddressList` + """ + params = {'networkDomainId': self._network_domain_to_network_domain_id( + ex_network_domain)} + response = self.connection.request_with_orgId_api_2( + 'network/ipAddressList', params=params).object + return self._to_ip_address_lists(response) + + def ex_get_ip_address_list(self, ex_network_domain, + ex_ip_address_list_name): + """ + Get IP Address List by name in network domain specified + + :param ex_network_domain: (required) The network domain or network + domain ID in which ipaddresslist resides. + :type ex_network_domain: :class:`DimensionDataNetworkDomain` or 'str' + + :param ex_ip_address_list_name: (required) Get 'IP Address List' by + name + :type ex_ip_address_list_name: :``str`` + + :return: a list of DimensionDataIpAddressList objects + :rtype: ``list`` of :class:`DimensionDataIpAddressList` + """ + + ip_address_lists = self.ex_list_ip_address_list(ex_network_domain) + return list(filter(lambda x: x.name == ex_ip_address_list_name, + ip_address_lists)) + + def ex_create_ip_address_list(self, ex_network_domain, name, + description, + ip_version, ip_address_collection, + child_ip_address_list=None): + """ + Create IP Address List. IP Address list. + + :param ex_network_domain: The network domain or network domain ID + :type ex_network_domain: :class:`DimensionDataNetworkDomain` or 'str' + + :param name: IP Address List Name (required) + :type name: :``str`` + + :param description: IP Address List Description (optional) + :type description: :``str`` + + :param ip_version: IP Version of ip address (required) + :type ip_version: :``str`` + + :param ip_address_collection: List of IP Address. At least one + ipAddress element or one + childIpAddressListId element must + be provided. + :type ip_address_collection: :``str`` + + :param child_ip_address_list: Child IP Address List or id to be + included in this IP Address List. + At least one ipAddress or + one childIpAddressListId + must be provided. + :type child_ip_address_list: + :class:'DimensionDataChildIpAddressList` or `str`` + + :return: a list of DimensionDataIpAddressList objects + :rtype: ``list`` of :class:`DimensionDataIpAddressList` + """ + if (ip_address_collection is None and + child_ip_address_list is None): + raise ValueError("At least one ipAddress element or one " + "childIpAddressListId element must be " + "provided.") + + create_ip_address_list = ET.Element('createIpAddressList', + {'xmlns': TYPES_URN}) + ET.SubElement( + create_ip_address_list, + 'networkDomainId' + ).text = self._network_domain_to_network_domain_id(ex_network_domain) + + ET.SubElement( + create_ip_address_list, + 'name' + ).text = name + + ET.SubElement( + create_ip_address_list, + 'description' + ).text = description + + ET.SubElement( + create_ip_address_list, + 'ipVersion' + ).text = ip_version + + for ip in ip_address_collection: + ip_address = ET.SubElement( + create_ip_address_list, + 'ipAddress', + ) + ip_address.set('begin', ip.begin) + + if ip.end: + ip_address.set('end', ip.end) + + if ip.prefix_size: + ip_address.set('prefixSize', ip.prefix_size) + + if child_ip_address_list is not None: + ET.SubElement( + create_ip_address_list, + 'childIpAddressListId' + ).text = \ + self._child_ip_address_list_to_child_ip_address_list_id( + child_ip_address_list) + + response = self.connection.request_with_orgId_api_2( + 'network/createIpAddressList', + method='POST', + data=ET.tostring(create_ip_address_list)).object + + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_edit_ip_address_list(self, ex_ip_address_list, description, + ip_address_collection, + child_ip_address_lists=None): + """ + Edit IP Address List. IP Address list. + + :param ex_ip_address_list: (required) IpAddressList object or + IpAddressList ID + :type ex_ip_address_list: :class:'DimensionDataIpAddressList' + or ``str`` + + :param description: IP Address List Description + :type description: :``str`` + + :param ip_address_collection: List of IP Address + :type ip_address_collection: ''list'' of + :class:'DimensionDataIpAddressList' + + :param child_ip_address_lists: Child IP Address List or id to be + included in this IP Address List + :type child_ip_address_lists: ``list`` of + :class:'DimensionDataChildIpAddressList' + or ``str`` + + :return: a list of DimensionDataIpAddressList objects + :rtype: ``list`` of :class:`DimensionDataIpAddressList` + """ + edit_ip_address_list = ET.Element( + 'editIpAddressList', + {'xmlns': TYPES_URN, + "id": self._ip_address_list_to_ip_address_list_id( + ex_ip_address_list), + 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance" + }) + + ET.SubElement( + edit_ip_address_list, + 'description' + ).text = description + + for ip in ip_address_collection: + ip_address = ET.SubElement( + edit_ip_address_list, + 'ipAddress', + ) + ip_address.set('begin', ip.begin) + + if ip.end: + ip_address.set('end', ip.end) + + if ip.prefix_size: + ip_address.set('prefixSize', ip.prefix_size) + + if child_ip_address_lists is not None: + ET.SubElement( + edit_ip_address_list, + 'childIpAddressListId' + ).text = self._child_ip_address_list_to_child_ip_address_list_id( + child_ip_address_lists) + else: + ET.SubElement( + edit_ip_address_list, + 'childIpAddressListId', + {'xsi:nil': 'true'} + ) + + response = self.connection.request_with_orgId_api_2( + 'network/editIpAddressList', + method='POST', + data=ET.tostring(edit_ip_address_list)).object + + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_delete_ip_address_list(self, ex_ip_address_list): + """ + Delete IP Address List by ID + + :param ex_ip_address_list: IP Address List object or IP Address + List ID (required) + :type ex_ip_address_list: :class:'DimensionDataIpAddressList' + or ``str`` + + :rtype: ``bool`` + """ + + delete_ip_address_list = \ + ET.Element('deleteIpAddressList', {'xmlns': TYPES_URN, 'id': self + ._ip_address_list_to_ip_address_list_id( + ex_ip_address_list)}) + + response = self.connection.request_with_orgId_api_2( + 'network/deleteIpAddressList', + method='POST', + data=ET.tostring(delete_ip_address_list)).object + + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_list_port_list(self, ex_network_domain): + """ + List Port List by network domain ID specified + + :param ex_network_domain: The network domain or network domain ID + :type ex_network_domain: :class:`DimensionDataNetworkDomain` or 'str' + + :return: a list of DimensionDataPortList objects + :rtype: ``list`` of :class:`DimensionDataPortList` + """ + params = {'networkDomainId': + self._network_domain_to_network_domain_id(ex_network_domain)} + response = self.connection.request_with_orgId_api_2( + 'network/portList', params=params).object + return self._to_port_lists(response) + + def ex_get_port_list(self, ex_port_list): + """ + Get Port List + + :param ex_port_list: The ex_port_list or ex_port_list ID + :type ex_port_list: :class:`DimensionDataNetworkDomain` or 'str' + + :return: DimensionDataPortList object + :rtype: :class:`DimensionDataPort` + """ + + url_path = ('network/portList/%s' % self._port_list_to_port_list_id( + ex_port_list)) + response = self.connection.request_with_orgId_api_2( + url_path).object + return self._to_port_list(response) + + def ex_create_port_list(self, ex_network_domain, name, description, + port_collection, child_port_list_lists=None): + """ + Create Port List. + + :param ex_network_domain: (required) The network domain in + which to create PortList. Provide + networkdomain object or its id. + :type ex_network_domain: :``str`` + + :param name: Port List Name + :type name: :``str`` + + :param description: IP Address List Description + :type description: :``str`` + + :param port_collection: List of Port Address + :type port_collection: :``str`` + + :param child_port_list_lists: Child Port List to be included in + this Port List + :type child_port_list_lists: :``str`` or ''list of + :class:'DimensionDataChildPortList' + + :return: result of operation + :rtype: ``bool`` + """ + new_port_list = ET.Element('createPortList', {'xmlns': TYPES_URN}) + ET.SubElement( + new_port_list, + 'networkDomainId' + ).text = self._network_domain_to_network_domain_id(ex_network_domain) + + ET.SubElement( + new_port_list, + 'name' + ).text = name + + ET.SubElement( + new_port_list, + 'description' + ).text = description + + for port in port_collection: + p = ET.SubElement( + new_port_list, + 'port' + ) + p.set('begin', port.begin) + + if port.end: + p.set('end', port.end) + + if child_port_list_lists is not None: + for child in child_port_list_lists: + ET.SubElement( + new_port_list, + 'childPortListId' + ).text = self._child_port_list_to_child_port_list_id(child) + + response = self.connection.request_with_orgId_api_2( + 'network/createPortList', + method='POST', + data=ET.tostring(new_port_list)).object + + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_edit_port_list(self, ex_port_list, description, + port_collection, child_port_list_lists=None): + """ + Edit Port List. + + :param ex_port_list: Port List to be edited + (required) + :type ex_port_list: :``str`` or :class:'DimensionDataPortList' + + :param description: Port List Description + :type description: :``str`` + + :param port_collection: List of Port Address + :type port_collection: :``str`` + + :param child_port_list_lists: Child Port List to be included in + this IP Address List + :type child_port_list_lists: :``list`` of + :class'DimensionDataChildPortList' + or ''str'' + + :return: a list of DimensionDataPortList objects + :rtype: ``list`` of :class:`DimensionDataPortList` + """ + + existing_port_address_list = ET.Element( + 'editPortList', + { + "id": self._port_list_to_port_list_id(ex_port_list), + 'xmlns': TYPES_URN, + 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance" + }) + + ET.SubElement( + existing_port_address_list, + 'description' + ).text = description + + for port in port_collection: + p = ET.SubElement( + existing_port_address_list, + 'port' + ) + p.set('begin', port.begin) + + if port.end: + p.set('end', port.end) + + if child_port_list_lists is not None: + for child in child_port_list_lists: + ET.SubElement( + existing_port_address_list, + 'childPortListId' + ).text = self._child_port_list_to_child_port_list_id(child) + else: + ET.SubElement( + existing_port_address_list, + 'childPortListId', + {'xsi:nil': 'true'} + ) + + response = self.connection.request_with_orgId_api_2( + 'network/editPortList', + method='POST', + data=ET.tostring(existing_port_address_list)).object + + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + + def ex_delete_port_list(self, ex_port_list): + """ + Delete Port List + :param ex_port_list: Port List to be deleted + :type ex_port_list: :``str`` or :class:'DimensionDataPortList' + + :rtype: ``bool`` + """ + + delete_port_list = ET.Element( + 'deletePortList', + {'xmlns': TYPES_URN, + 'id': self._port_list_to_port_list_id(ex_port_list)}) + + response = self.connection.request_with_orgId_api_2( + 'network/deletePortList', + method='POST', + data=ET.tostring(delete_port_list)).object + + response_code = findtext(response, 'responseCode', TYPES_URN) + return response_code in ['IN_PROGRESS', 'OK'] + def _format_csv(self, http_response): text = http_response.read() lines = str.splitlines(ensure_string(text)) @@ -2828,6 +3363,75 @@ class DimensionDataNodeDriver(NodeDriver): TYPES_URN)) return s + def _to_ip_address_lists(self, object): + ip_address_lists = [] + for element in findall(object, 'ipAddressList', TYPES_URN): + ip_address_lists.append(self._to_ip_address_list(element)) + + return ip_address_lists + + def _to_ip_address_list(self, element): + ipAddresses = [] + for ip in findall(element, 'ipAddress', TYPES_URN): + ipAddresses.append(self._to_ip_address(ip)) + + child_ip_address_lists = [] + for child_ip_list in findall(element, 'childIpAddressList', + TYPES_URN): + child_ip_address_lists.append(self + ._to_child_ip_list(child_ip_list)) + + return DimensionDataIpAddressList( + id=element.get('id'), + name=findtext(element, 'name', TYPES_URN), + description=findtext(element, 'description', TYPES_URN), + ip_version=findtext(element, 'ipVersion', TYPES_URN), + ip_address_collection=ipAddresses, + state=findtext(element, 'state', TYPES_URN), + create_time=findtext(element, 'createTime', TYPES_URN), + child_ip_address_lists=child_ip_address_lists + ) + + def _to_child_ip_list(self, element): + return DimensionDataChildIpAddressList( + id=element.get('id'), + name=element.get('name') + ) + + def _to_ip_address(self, element): + return DimensionDataIpAddress( + begin=element.get('begin'), + end=element.get('end'), + prefix_size=element.get('prefixSize') + ) + + def _to_port_lists(self, object): + port_lists = [] + for element in findall(object, 'portList', TYPES_URN): + port_lists.append(self._to_port_list(element)) + + return port_lists + + def _to_port_list(self, element): + ports = [] + for port in findall(element, 'port', TYPES_URN): + ports.append(self._to_port(element=port)) + + child_port_list_lists = [] + for child in findall(element, 'childPortList', TYPES_URN): + child_port_list_lists.append( + self._to_child_port_list(element=child)) + + return DimensionDataPortList( + id=element.get('id'), + name=findtext(element, 'name', TYPES_URN), + description=findtext(element, 'description', TYPES_URN), + port_collection=ports, + child_port_list_lists=child_port_list_lists, + state=findtext(element, 'state', TYPES_URN), + create_time=findtext(element, 'createTime', TYPES_URN) + ) + def _image_needs_auth(self, image): if not isinstance(image, NodeImage): image = self.ex_get_image_by_id(image) @@ -2836,6 +3440,20 @@ class DimensionDataNodeDriver(NodeDriver): return True @staticmethod + def _to_port(element): + return DimensionDataPort( + begin=element.get('begin'), + end=element.get('end') + ) + + @staticmethod + def _to_child_port_list(element): + return DimensionDataChildPortList( + id=element.get('id'), + name=element.get('name') + ) + + @staticmethod def _get_node_state(state, started, action): try: return NODE_STATE_MAP[(state, started, action)] @@ -2882,5 +3500,27 @@ class DimensionDataNodeDriver(NodeDriver): return dd_object_to_id(tag_key, DimensionDataTagKey, id_value='name') @staticmethod + def _ip_address_list_to_ip_address_list_id(ip_addr_list): + return dd_object_to_id(ip_addr_list, DimensionDataIpAddressList, + id_value='id') + + @staticmethod + def _child_ip_address_list_to_child_ip_address_list_id(child_ip_addr_list): + return dd_object_to_id(child_ip_addr_list, + DimensionDataChildIpAddressList, + id_value='id') + + @staticmethod + def _port_list_to_port_list_id(port_list): + return dd_object_to_id(port_list, DimensionDataPortList, + id_value='id') + + @staticmethod + def _child_port_list_to_child_port_list_id(child_port_list): + return dd_object_to_id(child_port_list, + DimensionDataChildPortList, + id_value='id') + + @staticmethod def _str2bool(string): return string.lower() in ("true")