def _load(cls, engine): nodes = [] for node in engine.data.get('nodes', []): for typeof, data in node.items(): cache = ElementCache(data) node = Node(name=cache.get('name'), href=cache.get_link('self'), type=typeof) node.data = cache node._engine = engine nodes.append(node) return nodes
class MultilinkMember(object): """ A multilink member represents an netlink member used on a multilink configuration. Multilink uses netlinks to specify settings specific to a connection, network, whether it should be active or standby and optionally QoS. Use this class to create mutlilink members that are required for creating a Multilink element. """ def __init__(self, kwargs): self.data = ElementCache(kwargs) @property def ip_range(self): """ Specifies the IP address range for dynamic source address translation (NAT) for the internal source IP addresses on the NetLink. Can also be set. :rtype: str """ return self.data.get('ip_range') @ip_range.setter def ip_range(self, value): if '-' in value: self.data.update(ip_range=value) @property def netlink_role(self): """ Shows whether the Netlink is active or standby. Active - traffic is routed through the NetLink according to the method you specify in the Outbound Multi-Link element properties. Standby - traffic is only routed through the netlink if all primary (active) netlinks are unavailable. :rtype: str """ return self.data.get('netlink_role') @netlink_role.setter def netlink_role(self, value): if value in ('standby', 'active'): self.data.update(netlink_role=value) @property def network(self): """ Specifies the Network element that represents the IP address space in the directly connected external network of the network link. Can also be set. :rtype: Network """ return Element.from_href(self.data.get('network_ref')) @network.setter def network(self, value): self.data.update(network_ref=element_resolver(value)) @property def netlink(self): """ The static netlink referenced in this multilink member :rtype: StaticNetlink """ return Element.from_href(self.data.get('netlink_ref')) @classmethod def create(cls, netlink, ip_range=None, netlink_role='active'): """ Create a multilink member. Multilink members are added to an Outbound Multilink configuration and define the ip range, static netlink to use, and the role. This element can be passed to the Multilink constructor to simplify creation of the outbound multilink. :param StaticNetlink,DynamicNetlink netlink: static netlink element to use as member :param str ip_range: the IP range for source NAT for this member. The IP range should be part of the defined network range used by this netlink. Not required for dynamic netlink :param str netlink_role: role of this netlink, 'active' or 'standby' :raises ElementNotFound: Specified netlink could not be found :rtype: MultilinkMember """ member_def = dict( netlink_ref=netlink.href, netlink_role=netlink_role, ip_range=ip_range if netlink.typeof == 'netlink' else '0.0.0.0') if netlink.typeof == 'netlink': # static netlink vs dynamic netlink member_def.update(network_ref=netlink.network[0].href) return cls(member_def) def __repr__(self): return 'MultilinkMember(netlink={},netlink_role={},ip_range={})'.format( self.netlink, self.netlink_role, self.ip_range)
class MultilinkMember(object): """ A multilink member represents an netlink member used on a multilink configuration. Multilink uses netlinks to specify settings specific to a connection, network, whether it should be active or standby and optionally QoS. Use this class to create mutlilink members that are required for creating a Multilink element. :ivar Network network: network element reference specifying netlink subnet :ivar StaticNetlink,DynamicNetlink netlink: netlink element reference """ network = ElementRef('network_ref') netlink = ElementRef('netlink_ref') def __init__(self, kwargs): self.data = ElementCache(kwargs) def __eq__(self, other): return all([ self.ip_range == other.ip_range, self.netlink_role == other.netlink_role, self.data.get('network_ref') == other.data.get('network_ref'), self.data.get('netlink_ref') == other.data.get('netlink_ref') ]) def __ne__(self, other): return not self == other def __hash__(self): return hash((self.ip_range, self.netlink_role, self.data.get('network_ref'), self.data.get('netlink_ref'))) @property def ip_range(self): """ Specifies the IP address range for dynamic source address translation (NAT) for the internal source IP addresses on the NetLink. Can also be set. :rtype: str """ return self.data.get('ip_range') @ip_range.setter def ip_range(self, value): if '-' in value: self.data.update(ip_range=value) @property def netlink_role(self): """ Shows whether the Netlink is active or standby. Active - traffic is routed through the NetLink according to the method you specify in the Outbound Multi-Link element properties. Standby - traffic is only routed through the netlink if all primary (active) netlinks are unavailable. :rtype: str """ return self.data.get('netlink_role') @netlink_role.setter def netlink_role(self, value): if value in ('standby', 'active'): self.data.update(netlink_role=value) @classmethod def create(cls, netlink, ip_range=None, netlink_role='active'): """ Create a multilink member. Multilink members are added to an Outbound Multilink configuration and define the ip range, static netlink to use, and the role. This element can be passed to the Multilink constructor to simplify creation of the outbound multilink. :param StaticNetlink,DynamicNetlink netlink: static netlink element to use as member :param str ip_range: the IP range for source NAT for this member. The IP range should be part of the defined network range used by this netlink. Not required for dynamic netlink :param str netlink_role: role of this netlink, 'active' or 'standby' :raises ElementNotFound: Specified netlink could not be found :rtype: MultilinkMember """ member_def = dict( netlink_ref=netlink.href, netlink_role=netlink_role, ip_range=ip_range if netlink.typeof == 'netlink' else '0.0.0.0') if netlink.typeof == 'netlink': # static netlink vs dynamic netlink member_def.update(network_ref=netlink.network[0].href) return cls(member_def) def __repr__(self): return 'MultilinkMember(netlink={},netlink_role={},ip_range={})'.format( self.netlink, self.netlink_role, self.ip_range)
class Task(SubElement): """ Task representation. This is generic and the format is used for any calls to SMC that return an asynchronous follower link to check the status of the task. :param str last_message: Last message received on this task :param bool in_progress: Whether the task is in progress or finished :param bool success: Whether the task succeeded or not :param str follower: Fully qualified path to the follower link to track this task. """ def __init__(self, task): super(Task, self).__init__(href=task.get("follower", None), name=task.get("type", None)) self.data = ElementCache(task) @property def resource(self): """ The resource/s associated with this task :rtype: list(Element) """ return [ Element.from_href(resource) for resource in self.data.get("resource", []) ] @property def progress(self): """ Percentage of completion :rtype: int """ return self.data.get("progress", 0) @property def success(self): """ the task has succeed :rtype: boolean """ return self.data.get("success", 0) @property def last_message(self): """ the last message returned by the task :rtype: string """ return self.data.get("last_message", 0) @property def start_time(self): """ Task start time in UTC datetime format :rtype: datetime """ start_time = self.data.get("start_time") if start_time: return millis_to_utc(start_time) @property def end_time(self): """ Task end time in UTC datetime format :rtype: datetime """ end_time = self.data.get("end_time") if end_time: return millis_to_utc(end_time) def abort(self): """ Abort existing task. :raises ActionCommandFailed: aborting task failed with reason :return: None """ try: self.make_request(method="delete", resource="abort") except ResourceNotFound: pass except ActionCommandFailed: pass @property def result_url(self): """ Link to result (this task) :rtype: str """ return self.get_relation("result") def update_status(self): """ Gets the current status of this task and returns a new task object. :raises TaskRunFailed: fail to update task status """ task = self.make_request(TaskRunFailed, href=self.href) return Task(task) def __getattr__(self, key): return self.data.get(key) @staticmethod def execute(self, resource, **kw): """ Execute the task and return a TaskOperationPoller. :rtype: TaskOperationPoller """ params = kw.pop("params", {}) json = kw.pop("json", None) task = self.make_request(TaskRunFailed, method="create", params=params, json=json, resource=resource) timeout = kw.pop("timeout", 5) wait_for_finish = kw.pop("wait_for_finish", True) return TaskOperationPoller(task=task, timeout=timeout, wait_for_finish=wait_for_finish, **kw) @staticmethod def download(self, resource, filename, timeout=5, max_tries=36, **kw): """ Start and return a Download Task :rtype: DownloadTask(TaskOperationPoller) """ params = kw.pop("params", {}) task = self.make_request(TaskRunFailed, method="create", resource=resource, params=params) return DownloadTask(timeout=timeout, max_tries=max_tries, filename=filename, task=task)
class RoutingTree(SubElement): """ RoutingTree is the base class for both Routing and Antispoofing nodes. This provides a commmon API for operations that affect how routing table and antispoofing operate. """ def __init__(self, data=None, **meta): super(RoutingTree, self).__init__(**meta) if data is not None: self.data = ElementCache(data) def __iter__(self): for node in self.data[self.typeof]: data = ElementCache(node) yield (self.__class__(href=data.get_link('self'), type=self.__class__.__name__, data=node, parent=self)) @property def name(self): """ Interface name / ID for routing level :return: name of routing node :rtype: str """ return self.data.get('name') @property def nicid(self): """ NIC id for this interface :return: nic identifier :rtype: str """ return self.data.get('nic_id') @property def dynamic_nicid(self): """ NIC id for this dynamic interface :return: nic identifier, if this is a DHCP interface :rtype: str or None """ return self.data.get('dynamic_nicid') @property def ip(self): """ IP network / host for this route :return: IP address of this routing level :rtype: str """ return self.data.get('ip') @property def level(self): """ Routing nodes have multiple 'levels' where routes can be nested. Most routes are placed at the interface level. This setting can mostly be ignored, but provides an informative view of how the route is nested. :return: routing node level (interface,network,gateway,any) :rtype: str """ return self.data.get('level') @property def related_element_type(self): """ .. versionadded:: 0.6.0 Requires SMC version >= 6.4 Related element type defines the 'type' of element at this routing or antispoofing node level. :rtype: str """ return self.data.get('related_element_type') def as_tree(self, level=0): """ Display the routing tree representation in string format :rtype: str """ ret = '--' * level + repr(self) + '\n' for routing_node in self: ret += routing_node.as_tree(level + 1) return ret def get(self, interface_id): """ Obtain routing configuration for a specific interface by ID. .. note:: If interface is a VLAN, you must use a str to specify the interface id, such as '3.13' (interface 3, VLAN 13) :param str,int interface_id: interface identifier :raises InterfaceNotFound: invalid interface for engine :return: Routing element, or None if not found :rtype: Routing """ for interface in self: if interface.nicid == str(interface_id) or \ interface.dynamic_nicid == str(interface_id): return interface raise InterfaceNotFound('Specified interface {} does not exist on ' 'this engine.'.format(interface_id)) def delete(self): super(RoutingTree, self).delete() flush_parent_cache(self._parent) def update(self): super(RoutingTree, self).update() flush_parent_cache(self._parent) def all(self): """ Return all routes for this engine. :return: current route entries as :class:`.Routing` element :rtype: list """ return [node for node in self] def __str__(self): return '{}(name={},level={},type={})'.format(self.__class__.__name__, self.name, self.level, self.related_element_type) def __repr__(self): return str(self)
class Routing(SubElement): """ Routing represents the Engine routing configuration and provides the ability to view and add features to routing nodes such as OSPF. """ def __init__(self, data=None, **meta): super(Routing, self).__init__(**meta) if data is not None: self.data = ElementCache(**data) def __iter__(self): for node in self.data['routing_node']: data = ElementCache(**node) yield (Routing(href=data.get_link('self'), data=node)) @property def name(self): """ Interface name / ID for routing level :return: name of routing node :rtype: str """ return self.data.get('name') @property def nicid(self): """ NIC id for this interface :return str nic identifier """ return self.data.get('nic_id') @property def dynamic_nicid(self): """ NIC id for this dynamic interface :return str nic identifier """ return self.data.get('dynamic_nicid') @property def ip(self): """ IP network / host for this route :return: IP address of this routing level :rtype: str """ return self.data.get('ip') @property def level(self): """ Routing nodes have multiple 'levels' where routes can be nested. Most routes are placed at the interface level. This setting can mostly be ignored, but provides an informative view of how the route is nested. :return: routing node level (interface,network,gateway,any) :rtype: str """ return self.data.get('level') def get(self, interface_id): """ Obtain routing configuration for a specific interface by ID. .. note:: If interface is a VLAN, you must use a str to specify the interface id, such as '3.13' (interface 3, VLAN 13) :param str,int interface_id: interface identifier :return: Routing element, or None if not found :rtype: Routing """ for interface in iter(self): if interface.nicid == str(interface_id) or \ interface.dynamic_nicid == str(interface_id): return interface def add_traffic_handler(self, netlink, netlink_gw=None, network=None): """ Add a traffic handler to a routing node. A traffic handler can be either a static netlink or a multilink traffic handler. If ``network`` is not specified and the interface has multiple IP addresses, the traffic handler will be added to all ipv4 addresses. Add a pre-defined netlink to the route table of interface 0:: engine = Engine('vm') rnode = engine.routing.get(0) rnode.add_traffic_handler(StaticNetlink('mynetlink')) Add a pre-defined netlink only to a specific network on an interface with multiple addresses. Specify a netlink_gw for the netlink:: rnode = engine.routing.get(0) rnode.add_traffic_handler( StaticNetlink('mynetlink'), netlink_gw=Router('myrtr'), network='172.18.1.0/24') :param StaticNetlink,Multilink netlink: netlink element :param Element netlink_gw: gateway for the netlink element. Can be None if no gateway is needed. Element type is typically of type :class:`smc.elements.network.Router`. :param str network: if network specified, only add OSPF to this network on interface :raises EngineCommandFailed: failure updating routing :raises ElementNotFound: ospf area not found :return: None """ netlink = { 'href': netlink.href, 'level': 'gateway', 'routing_node': [], 'name': netlink.name } if netlink_gw: netlink_gateway = { 'level': 'any', 'href': netlink_gw.href, 'name': netlink_gw.name } netlink['routing_node'].append(netlink_gateway) self._bind_to_ipv4_network(network, netlink) self.update() def add_ospf_area(self, ospf_area, communication_mode='NOT_FORCED', unicast_ref=None, network=None): """ Add OSPF Area to this routing node. Communication mode specifies how the interface will interact with the adjacent OSPF environment. Please see SMC API documentation for more in depth information on each option. If the interface has multiple networks nested below, all networks will receive the OSPF area by default unless the ``network`` parameter is specified. OSPF cannot be applied to IPv6 networks. Example of adding an area to interface routing node:: area = OSPFArea('area0') #obtain area resource #Set on routing interface 0 interface = engine.routing.get(0) interface.add_ospf_area(area) .. note:: If UNICAST is specified, you must also provide a unicast_ref to identify the remote host :param OSPFArea ospf_area: OSPF area instance or href :param str communication_mode: NOT_FORCED|POINT_TO_POINT|PASSIVE|UNICAST :param Element unicast_ref: Element used as unicast gw (required for UNICAST) :param str network: if network specified, only add OSPF to this network on interface :raises EngineCommandFailed: failure updating routing :raises ElementNotFound: ospf area not found :return: None """ communication_mode = communication_mode.upper() node = { 'href': ospf_area.href, 'communication_mode': communication_mode, 'level': 'gateway', 'routing_node': [], 'name': ospf_area.name } if communication_mode == 'UNICAST': # Need a destination ref, add to sub routing_node node['routing_node'].append({ 'href': unicast_ref.href, 'level': 'any', 'name': unicast_ref.name }) self._bind_to_ipv4_network(network, node) self.update() def add_bgp_peering(self, bgp_peering, external_bgp_peer, network=None): """ Add a BGP configuration to this routing interface. If the interface has multiple ipaddresses, all networks will receive the BGP peering by default unless the ``network`` parameter is specified. Example of adding BGP to an interface by ID:: interface = engine.routing.get(0) interface.add_bgp_peering( BGPPeering('mypeer'), ExternalBGPPeer('neighbor')) :param BGPPeering bgp_peering: BGP Peer element :param ExternalBGPPeer external_bgp_peer: peer element or href :param str network: if network specified, only add OSPF to this network on interface :raises UpdateElementFailed: failed to add BGP :return: None """ bgp = { 'href': bgp_peering.href, 'level': 'gateway', 'routing_node': [], 'name': bgp_peering.name } external_peer = { 'href': external_bgp_peer.href, 'level': 'any', 'name': external_bgp_peer.name } bgp['routing_node'].append(external_peer) self._bind_to_ipv4_network(network, bgp) self.update() def add_static_route(self, gateway, destination, network=None): """ Add a static route to this route table. Destination can be any element type supported in the routing table such as a Group of network members. :: >>> engine = Engine('ve-1') >>> itf = engine.routing.get(0) >>> itf.add_static_route( gateway=Router('tmprouter'), destination=[Group('routegroup')]) :param Element gateway: gateway for this route (Router, Host) :param Element destination: destination network/s for this route. :type destination: list(Host, Router, ..) :raises UpdateElementFailed: failure to update routing table :return: None """ route = { 'href': gateway.href, 'level': 'gateway', 'routing_node': [], 'name': gateway.name } for dest in destination: route['routing_node'].append({ 'href': dest.href, 'level': 'any', 'name': dest.name }) self._bind_to_ipv4_network(network, route) self.update() def add_dynamic_gateway(self, networks): """ A dynamic gateway object is a router that is attached to a DHCP interface. You can associate networks with this gateway address to identify networks on this interface. :: route = engine.routing.get(0) route.add_dynamic_gateway([Network('mynetwork')]) :param list Network: list of network elements to add to this gateway :return: None """ route = { 'dynamic_classid': 'gateway', 'level': 'gateway', 'routing_node': [] } for network in networks: route['routing_node'].append({ 'href': network.href, 'level': 'any', 'name': network.name }) for networks in iter(self): networks.data['routing_node'].append(route) self.update() def _bind_to_ipv4_network(self, network, element): for networks in iter(self): if len(networks.ip.split(':')) == 1: # Skip IPv6 if network is not None: # Only place on specific network if networks.ip == network: networks.data['routing_node'].append(element) else: networks.data['routing_node'].append(element) def remove_route_element(self, element, network=None): """ Remove a route element by href or Element. Use this if you want to remove a netlink or a routing element such as BGP or OSPF. Removing is done from within the routing interface context. :: rnode = engine.routing.get(0) rnode.remove_route_element(StaticNetlink('mynetlink')) Only from a specific network on a multi-address interface:: rnode.remove_route_element( StaticNetlink('mynetlink'), network='172.18.1.0/24') :param str,Element element: element to remove from this routing node :param str network: if network specified, only add OSPF to this network on interface :raises UpdateElementFailed: failed to remove route element :return: None """ element = element_resolver(element) routing_node = [] for networks in iter(self): if network is not None: if networks.ip != network: routing_node.append(networks.data) else: rnode = [ gw for gw in networks.data['routing_node'] if gw.get('href') != element ] networks.data['routing_node'] = rnode routing_node.append(networks.data) else: rnode = [ gw for gw in networks.data['routing_node'] if gw.get('href') != element ] networks.data['routing_node'] = rnode routing_node.append(networks.data) self.data['routing_node'] = routing_node self.update() def all(self): """ Return all routes for this engine. :return: current route entries as :class:`.Routing` element :rtype: list """ return [node for node in iter(self)] def __str__(self): return '{0}(name={1},level={2})'.format(self.__class__.__name__, self.name, self.level) def __repr__(self): return str(self)
class Antispoofing(SubElement): """ Anti-spoofing is configured by default based on interface networks directly attached. It is possible to override these settings by adding additional networks as valid source networks on a given interface. Antispoofing is nested similar to routes. Iterate the antispoofing configuration:: for entry in engine.antispoofing.all(): print(entry) """ def __init__(self, data=None, **meta): super(Antispoofing, self).__init__(**meta) if data is not None: self.data = ElementCache(**data) def __iter__(self): for node in self.data['antispoofing_node']: data = ElementCache(**node) yield (Antispoofing(href=data.get_link('self'), data=node)) @property def name(self): """ Name on this node level """ return self.data.get('name') @property def ip(self): """ IP network / address / host of this antispoofing entry :return: IP Address of this antispoofing node :rtype: str """ return self.data.get('ip') @property def level(self): """ Routing nodes have multiple 'levels' where routes can be nested. Most routes are placed at the interface level. This setting can mostly be ignored, but provides an informative view of how the route is nested. :return: routing node level (interface,network,gateway,any) :rtype: str """ return self.data.get('level') @property def validity(self): """ Enabled or disabled antispoofing entry :return: validity of this entry (enable,disable,absolute) :rtype: str """ return self.data.get('validity') @property def nicid(self): """ NIC id for this interface :return str nic identifier """ return self.data.get('nic_id') def add(self, entry): """ Add an entry to this antispoofing node level. Entry can be either href or network elements specified in :py:class:`smc.elements.network` :: for entry in engine.antispoofing.all(): if entry.name == 'Interface 0': entry.add(Network('network-10.1.2.0/24')) :param Element entry: entry to add, i.e. Network('mynetwork'), Host(..) :return: None :raises CreateElementFailed: failed adding entry :raises ElementNotFound: element entry specified not in SMC """ node = { 'antispoofing_node': [], 'auto_generated': 'false', 'href': entry.href, 'level': self.level, 'validity': 'enable', 'name': entry.name } self.data['antispoofing_node'].append(node) self.update() def all(self): return [node for node in iter(self)] def __str__(self): return '{0}(name={1},level={2})'.format(self.__class__.__name__, self.name, self.level) def __repr__(self): return str(self)
class RoutingTree(SubElement): """ RoutingTree is the base class for both Routing and Antispoofing nodes. This provides a commmon API for operations that affect how routing table and antispoofing operate. """ def __init__(self, data=None, **meta): super(RoutingTree, self).__init__(**meta) if data is not None: self.data = ElementCache(data) def __iter__(self): for node in self.data[self.typeof]: data = ElementCache(node) yield(self.__class__( href=data.get_link('self'), type=self.__class__.__name__, data=node, parent=self)) @property def name(self): """ Interface name / ID for routing level :return: name of routing node :rtype: str """ return self.data.get('name') @property def nicid(self): """ NIC id for this interface :return: nic identifier :rtype: str """ return self.data.get('nic_id') @property def dynamic_nicid(self): """ NIC id for this dynamic interface :return: nic identifier, if this is a DHCP interface :rtype: str or None """ return self.data.get('dynamic_nicid') @property def ip(self): """ IP network / host for this route :return: IP address of this routing level :rtype: str """ return self.data.get('ip') @property def level(self): """ Routing nodes have multiple 'levels' where routes can be nested. Most routes are placed at the interface level. This setting can mostly be ignored, but provides an informative view of how the route is nested. :return: routing node level (interface,network,gateway,any) :rtype: str """ return self.data.get('level') @property def related_element_type(self): """ .. versionadded:: 0.6.0 Requires SMC version >= 6.4 Related element type defines the 'type' of element at this routing or antispoofing node level. :rtype: str """ if 'related_element_type' in self.data: return self.data.get('related_element_type') return None if self.dynamic_nicid or (self.nicid and '.' in self.nicid) else \ Element.from_href(self.data.get('href')).typeof # pre-6.4 def as_tree(self, level=0): """ Display the routing tree representation in string format :rtype: str """ ret = '--' * level + repr(self) + '\n' for routing_node in self: ret += routing_node.as_tree(level+1) return ret def get(self, interface_id): """ Obtain routing configuration for a specific interface by ID. .. note:: If interface is a VLAN, you must use a str to specify the interface id, such as '3.13' (interface 3, VLAN 13) :param str,int interface_id: interface identifier :raises InterfaceNotFound: invalid interface for engine :return: Routing element, or None if not found :rtype: Routing """ for interface in self: if interface.nicid == str(interface_id) or \ interface.dynamic_nicid == str(interface_id): return interface raise InterfaceNotFound('Specified interface {} does not exist on ' 'this engine.'.format(interface_id)) def delete(self): super(RoutingTree, self).delete() flush_parent_cache(self._parent) def update(self): super(RoutingTree, self).update() flush_parent_cache(self._parent) def all(self): """ Return all routes for this engine. :return: current route entries as :class:`.Routing` element :rtype: list """ return [node for node in self] def __str__(self): return '{}(name={},level={},type={})'.format( self.__class__.__name__, self.name, self.level, self.related_element_type) def __repr__(self): return str(self)
class MultilinkMember(object): """ A multilink member represents an netlink member used on a multilink configuration. Multilink uses netlinks to specify settings specific to a connection, network, whether it should be active or standby and optionally QoS. Use this class to create mutlilink members that are required for creating a Multilink element. :ivar Network network: network element reference specifying netlink subnet :ivar StaticNetlink,DynamicNetlink netlink: netlink element reference """ network = ElementRef("network_ref") netlink = ElementRef("netlink_ref") def __init__(self, kwargs): self.data = ElementCache(kwargs) def __eq__(self, other): return all([ self.ip_range == other.ip_range, self.netlink_role == other.netlink_role, self.data.get("network_ref") == other.data.get("network_ref"), self.data.get("netlink_ref") == other.data.get("netlink_ref"), ]) def __ne__(self, other): return not self == other def __hash__(self): return hash(( self.ip_range, self.netlink_role, self.data.get("network_ref"), self.data.get("netlink_ref"), )) @property def ip_range(self): """ Specifies the IP address range for dynamic source address translation (NAT) for the internal source IP addresses on the NetLink. Can also be set. :rtype: str """ return self.data.get("ip_range") @ip_range.setter def ip_range(self, value): if "-" in value: self.data.update(ip_range=value) @property def netlink_role(self): """ Shows whether the Netlink is active or standby. Active - traffic is routed through the NetLink according to the method you specify in the Outbound Multi-Link element properties. Standby - traffic is only routed through the netlink if all primary (active) netlinks are unavailable. :rtype: str """ return self.data.get("netlink_role") @netlink_role.setter def netlink_role(self, value): if value in ("standby", "active"): self.data.update(netlink_role=value) @classmethod def create(cls, netlink, ip_range=None, netlink_role="active"): """ Create a multilink member. Multilink members are added to an Outbound Multilink configuration and define the ip range, static netlink to use, and the role. This element can be passed to the Multilink constructor to simplify creation of the outbound multilink. :param StaticNetlink,DynamicNetlink netlink: static netlink element to use as member :param str ip_range: the IP range for source NAT for this member. The IP range should be part of the defined network range used by this netlink. Not required for dynamic netlink :param str netlink_role: role of this netlink, 'active' or 'standby' :raises ElementNotFound: Specified netlink could not be found :rtype: MultilinkMember """ member_def = dict( netlink_ref=netlink.href, netlink_role=netlink_role, ip_range=ip_range if netlink.typeof == "netlink" else "0.0.0.0", ) if netlink.typeof == "netlink": # static netlink vs dynamic netlink member_def.update(network_ref=netlink.network[0].href) return cls(member_def) def __repr__(self): return "MultilinkMember(netlink={},netlink_role={},ip_range={})".format( self.netlink, self.netlink_role, self.ip_range)