Source code for smc.elements.network

"""
Module representing network elements used within the SMC
"""
from smc.base.model import Element, ElementCreator
from smc.api.exceptions import MissingRequiredInput, CreateElementFailed,\
    ElementNotFound, FetchElementFailed
from smc.base.util import element_resolver


[docs]class Host(Element): """ Class representing a Host object used in access rules Create a host element with ipv4:: Host.create(name='myhost', address='1.1.1.1', secondary=['1.1.1.2'], comment='some comment for my host') Create a host element with ipv6 and secondary ipv4 address:: Host.create(name='mixedhost', ipv6_address='2001:cdba::3257:9652', secondary=['1.1.1.1']) Available attributes: :ivar str address: IPv4 address for this element :ivar str ipv6_address: IPv6 address for this host element :ivar list secondary: secondary IP addresses for this host """ typeof = 'host'
[docs] @classmethod def create(cls, name, address=None, ipv6_address=None, secondary=None, comment=None): """ Create the host element :param str name: Name of element :param str address: ipv4 address of host object (optional if ipv6) :param str ipv6_address: ipv6 address (optional if ipv4) :param list secondary: secondary ip addresses (optional) :param str comment: comment (optional) :raises CreateElementFailed: element creation failed with reason :return: instance with meta :rtype: Host .. note:: Either ipv4 or ipv6 address is required """ address = address if address else None ipv6_address = ipv6_address if ipv6_address else None secondaries = [] if secondary is None else secondary json = {'name': name, 'address': address, 'ipv6_address': ipv6_address, 'secondary': secondaries, 'comment': comment} return ElementCreator(cls, json)
[docs] def add_secondary(self, address, append_lists=False): """ Add secondary IP addresses to this host element. If append_list is True, then add to existing list. Otherwise overwrite. :param list address: ip addresses to add in IPv4 or IPv6 format :param bool append_list: add to existing or overwrite (default: append) :return: None """ self.update( secondary=address, append_lists=append_lists)
[docs]class AddressRange(Element): """ Class representing a IpRange object used in access rules Create an address range element:: IpRange.create('myrange', '1.1.1.1-1.1.1.5') Available attributes: :ivar str ip_range: IP range for element. In format: '10.10.10.1-10.10.10.10' """ typeof = 'address_range'
[docs] @classmethod def create(cls, name, ip_range, comment=None): """ Create an AddressRange element :param str name: Name of element :param str iprange: iprange of element :param str comment: comment (optional) :raises CreateElementFailed: element creation failed with reason :return: instance with meta :rtype: AddressRange """ json = {'name': name, 'ip_range': ip_range, 'comment': comment} return ElementCreator(cls, json)
[docs]class Router(Element): """ Class representing a Router object used in access rules Create a router element with ipv4 address:: Router.create('myrouter', '1.2.3.4', comment='my router comment') Create a router element with ipv6 address:: Router.create(name='mixedhost', ipv6_address='2001:cdba::3257:9652') Available attributes: :ivar str address: IPv4 address for this router :ivar str ipv6_address: IPv6 address for this router :ivar list secondary: list of additional IP's for this router """ typeof = 'router'
[docs] @classmethod def create(cls, name, address=None, ipv6_address=None, secondary=None, comment=None): """ Create the router element :param str name: Name of element :param str address: ip address of host object (optional if ipv6) :param str ipv6_address: ipv6 address (optional if ipv4) :param list secondary: secondary ip address (optional) :param str comment: comment (optional) :raises CreateElementFailed: element creation failed with reason :return: instance with meta :rtype: Router .. note:: either ipv4 or ipv6 address is required """ address = address if address else None ipv6_address = ipv6_address if ipv6_address else None secondary = [] if secondary is None else secondary json = {'name': name, 'address': address, 'ipv6_address': ipv6_address, 'secondary': secondary, 'comment': comment} return ElementCreator(cls, json)
[docs]class Network(Element): """ Class representing a Network object used in access rules Network format should be CIDR based. It is recommended that when creating the network element, you use a naming convention that includes the network cidr in the name, such as 'network-1.1.1.0/24'. This will simplify searches later and workaround the restriction that searches with '/' and '-' only match on the name field and not an actual attribute value. Create an ipv4 network element:: Network.create('mynetwork', '2.2.2.0/24') Create an ipv6 network element:: Network.create(name='mixednetwork', ipv6_network='fc00::/7') Available attributes: :ivar str ipv4_network: IPv4 network, in format: 10.10.10.0/24 :ivar str ipv6_network: IPv6 network """ typeof = 'network'
[docs] @classmethod def create(cls, name, ipv4_network=None, ipv6_network=None, comment=None): """ Create the network element :param str name: Name of element :param str ipv4_network: network cidr (optional if ipv6) :param str ipv6_network: network cidr (optional if ipv4) :param str comment: comment (optional) :raises CreateElementFailed: element creation failed with reason :return: instance with meta :rtype: Network .. note:: Either an ipv4_network or ipv6_network must be specified """ ipv4_network = ipv4_network if ipv4_network else None ipv6_network = ipv6_network if ipv6_network else None json = {'name': name, 'ipv4_network': ipv4_network, 'ipv6_network': ipv6_network, 'comment': comment} return ElementCreator(cls, json)
[docs]class DomainName(Element): """ Represents a domain name used as FQDN in policy Use this object to reference a DNS resolvable FQDN or partial domain name to be used in policy. Create a domain based network element:: DomainName.create('mydomain.net') """ typeof = 'domain_name'
[docs] @classmethod def create(cls, name, comment=None): """ Create domain name element :param str name: name of domain, i.e. lepages.net, www.lepages.net :raises CreateElementFailed: element creation failed with reason :return: instance with meta :rtype: DomainName """ json = {'name': name, 'comment': comment} return ElementCreator(cls, json)
[docs]class Expression(Element): """ Expressions are used to build boolean like objects used in policy. For example, if you wanted to create an expression that negates a specific set of network elements to use in a "NOT" rule, an expression would be the element type. For example, adding a rule that negates (network A or network B):: sub_expression = Expression.build_sub_expression( name='mytestexporession', ne_ref=['http://172.18.1.150:8082/6.0/elements/host/3999', 'http://172.18.1.150:8082/6.0/elements/host/4325'], operator='union') Expression.create(name='apiexpression', ne_ref=[], sub_expression=sub_expression) .. note:: The sub-expression creates the json for the expression (network A or network B) and is then used as an parameter to create. """ typeof = 'expression'
[docs] @staticmethod def build_sub_expression(name, ne_ref=None, operator='union'): """ Static method to build and return the proper json for a sub-expression. A sub-expression would be the grouping of network elements used as a target match. For example, (network A or network B) would be considered a sub-expression. This can be used to compound sub-expressions before calling create. :param str name: name of sub-expression :param list ne_ref: network elements references :param str operator: exclusion (negation), union, intersection (default: union) :return: JSON of subexpression. Use in :func:`~create` constructor """ ne_ref = [] if ne_ref is None else ne_ref json = {'name': name, 'ne_ref': ne_ref, 'operator': operator} return json
[docs] @classmethod def create(cls, name, ne_ref=None, operator='exclusion', sub_expression=None, comment=None): """ Create the expression :param str name: name of expression :param list ne_ref: network element references for expression :param str operator: 'exclusion' (negation), 'union', 'intersection' (default: exclusion) :param dict sub_expression: sub expression used :param str comment: optional comment :raises CreateElementFailed: element creation failed with reason :return: instance with meta :rtype: Expression """ sub_expression = [] if sub_expression is None else [sub_expression] json = {'name': name, 'operator': operator, 'ne_ref': ne_ref, 'sub_expression': sub_expression, 'comment': comment} return ElementCreator(cls, json)
[docs]class URLListApplication(Element): """ URL List Application represents a list of URL's (typically by domain) that allow for easy grouping for performing whitelist and blacklisting Creating a URL List:: URLListApplication.create( name='whitelist', url_entry=['www.google.com', 'www.cnn.com']) .. note:: URLListApplication requires SMC API version >= 6.1 Available attributes: :ivar list url_entry: URL entries as strings """ typeof = 'url_list_application'
[docs] @classmethod def create(cls, name, url_entry, comment=None): """ Create the custom URL list :param str name: name of url list :param list url_entry: list of url's :param str comment: optional comment :raises CreateElementFailed: element creation failed with reason :return: instance with meta :rtype: URLListApplication """ json = {'name': name, 'url_entry': url_entry, 'comment': comment} return ElementCreator(cls, json)
class IPListGroup(Element): """ .. note:: IPListGroup requires SMC API version >= 6.1 """ pass
[docs]class IPList(Element): """ IPList represent a custom list of IP addresses, networks or ip ranges (IPv4 or IPv6). These are used in source/destination fields of a rule for policy enforcement. .. note:: IPList requires SMC API version >= 6.1 Create an empty IPList:: IPList.create(name='mylist') Create an IPList with initial content:: IPList.create(name='mylist', iplist=['1.1.1.1','1.1.1.2', '1.2.3.4']) Example of downloading the IPList in text format:: >>> iplist = list(IPList.objects.filter('mylist')) >>> print(iplist) [IPList(name=mylist)] >>> iplist[0].download(filename='iplist.txt', as_type='txt') Example of uploading an IPList as a zip file:: >>> iplist = list(IPList.objects.filter('mylist')) >>> print(iplist) [IPList(name=mylist)] iplist[0].upload(filename='/path/to/iplist.zip') Upload an IPList using json format:: >>> iplist = IPList('mylist') >>> iplist.upload(json={'ip': ['4.4.4.4']}, as_type='json') """ typeof = 'ip_list'
[docs] def download(self, filename=None, as_type='zip'): """ Download the IPList. List format can be either zip, text or json. For large lists, it is recommended to use zip encoding. Filename is required for zip downloads. :param str filename: Name of file to save to (required for zip) :param str as_type: type of format to download in: txt,json,zip (default: zip) :raises IOError: problem writing to destination filename :return: None """ headers = None if as_type in ['zip', 'txt', 'json']: if as_type == 'zip': if filename is None: raise MissingRequiredInput('Filename must be specified when ' 'downloading IPList as a zip file.') filename = '{}'.format(filename) elif as_type == 'txt': headers = {'accept': 'text/plain'} elif as_type == 'json': headers = {'accept': 'application/json'} result = self.make_request( FetchElementFailed, raw_result=True, resource='ip_address_list', filename=filename, headers=headers) return result.json if as_type == 'json' else result.content
[docs] def upload(self, filename=None, json=None, as_type='zip'): """ Upload an IPList to the SMC. The contents of the upload are not incremental to what is in the existing IPList. So if the intent is to add new entries, you should first retrieve the existing and append to the content, then upload. The only upload type that can be done without loading a file as the source is as_type='json'. :param str filename: required for zip/txt uploads :param str json: required for json uploads :param str as_type: type of format to upload in: txt|json|zip (default) :raises IOError: filename specified cannot be loaded :raises CreateElementFailed: element creation failed with reason :return: None """ headers = {'content-type': 'multipart/form-data'} params = None files = None if filename: files = {'ip_addresses': open(filename, 'rb')} if as_type == 'json': headers = {'accept': 'application/json', 'content-type': 'application/json'} elif as_type == 'txt': params = {'format': 'txt'} self.make_request( CreateElementFailed, method='create', resource='ip_address_list', headers=headers, files=files, json=json, params=params)
[docs] @classmethod def update_or_create(cls, append_lists=True, with_status=False, **kwargs): """ Update or create an IPList. :param bool append_lists: append to existing IP List :param dict kwargs: provide at minimum the name attribute and optionally match the create constructor values :raises FetchElementFailed: Reason for retrieval failure """ was_created, was_modified = False, False element = None try: element = cls.get(kwargs.get('name')) if append_lists: iplist = element.iplist diff = [i for i in kwargs.get('iplist', []) if i not in iplist] if diff: iplist.extend(diff) else: iplist = [] else: iplist = kwargs.get('iplist', []) if iplist: element.upload(json={'ip': iplist}, as_type='json') was_modified = True except ElementNotFound: element = cls.create( kwargs.get('name'), iplist = kwargs.get('iplist', [])) was_created = True if with_status: return element, was_modified, was_created return element
@property def iplist(self): """ Return a list representation of this IPList. This is not a recommended function if the list is extremely large. In that case use the download function in zip format. :raises FetchElementFailed: Reason for retrieval failure :rtype: list """ return self.download(as_type='json').get('ip', [])
[docs] @classmethod def create(cls, name, iplist=None, comment=None): """ Create an IP List. It is also possible to add entries by supplying a list of IPs/networks, although this is optional. You can also use upload/download to add to the iplist. :param str name: name of ip list :param list iplist: list of ipaddress :param str comment: optional comment :raises CreateElementFailed: element creation failed with reason :return: instance with meta :rtype: IPList """ json = {'name': name, 'comment': comment} result = ElementCreator(cls, json) if result and iplist is not None: element = IPList(name) element.make_request( CreateElementFailed, method='create', resource='ip_address_list', json={'ip': iplist}) return result
[docs]class Zone(Element): """ Class representing a zone used on physical interfaces and used in access control policy rules, typically in source and destination fields. Zones can be applied on multiple interfaces which would allow logical grouping in policy. Create a zone:: Zone.create('myzone') """ typeof = 'interface_zone'
[docs] @classmethod def create(cls, name, comment=None): """ Create the zone element :param str zone: name of zone :param str comment: optional comment :raises CreateElementFailed: element creation failed with reason :return: instance with meta :rtype: Zone """ json = {'name': name, 'comment': comment} return ElementCreator(cls, json)
class Country(Element): """ Country elements cannot be created, only viewed .. note:: Country requires SMC API version >= 6.1 """ typeof = 'country' class IPCountryGroup(Element): """ IP Country Group .. note:: IP Country Group requires SMC API version >= 6.1 """ typeof = 'ip_country_group'
[docs]class Alias(Element): """ Aliases are adaptive objects that represent a single element having different values based on the engine applied on. There are many default aliases in SMC and new ones can also be created. Finding aliases can be achieved by using collections or loading directly if you know the alias name: :: >>> from smc.elements.network import Alias >>> list(Alias.objects.all()) [Alias(name=$$ Interface ID 46.net), Alias(name=$$ Interface ID 45.net), etc] Resolve an alias to a specific engine:: >>> alias = Alias('$$ Interface ID 0.ip') >>> alias.resolve('myfirewall') [u'10.10.0.1'] Create an alias and assign values specific to an engine:: >>> alias = Alias.update_or_create( name='fooalias', engine=Layer3Firewall('vm'), translation_values=[Host('foo')]) >>> alias Alias(name=fooalias) """ typeof = 'alias' def __init__(self, name, **meta): super(Alias, self).__init__(name, **meta) self.resolved_value = [] #: resolved value for alias
[docs] @classmethod def create(cls, name, comment=None): """ Create an alias. :param str name: name of alias :param str comment: comment for this alias :raises CreateElementFailed: create failed with reason :rtype: Alias """ return ElementCreator(cls, json={'name': name, 'comment': comment})
[docs] @classmethod def update_or_create(cls, name, engine, translation_values=None, with_status=False): """ Update or create an Alias and it's mappings. :param str name: name of alias :param Engine engine: engine to modify alias translation values :param list(str,Element) translation_values: translation values as elements. Can be None if you want to unset any existing values :param bool with_status: if set to True, a 3-tuple is returned with (Element, modified, created), where the second and third tuple items are booleans indicating the status :raises ElementNotFound: specified engine or translation values are not found in the SMC :raises UpdateElementFailed: update failed with reason :raises CreateElementFailed: create failed with reason :rtype: Element """ updated, created = False, False alias = cls.get(name, raise_exc=False) if not alias: alias = cls.create(name) created = True elements = element_resolver(translation_values) if translation_values \ else [] if not created: # possible update # Does alias already exist with a value alias_value = [_alias for _alias in engine.data.get('alias_value', []) if _alias.get('alias_ref') == alias.href] if alias_value: if not elements: alias_value[0].update(translated_element=None) updated = True else: t_values = alias_value[0].get('translated_element') if set(t_values) ^ set(elements): t_values[:] = elements updated = True if elements and (created or not updated): engine.data.setdefault('alias_value', []).append( {'alias_ref': alias.href, 'translated_element': elements}) updated = True if updated: engine.update() if with_status: return alias, updated, created return alias
@classmethod def _from_engine(cls, data, alias_list): """ Return an alias for the engine. The data is dict provided when calling engine.alias_resolving(). The alias list is the list of aliases pre-fetched from Alias.objects.all(). This will return an Alias element by taking the alias_ref and finding the name in the alias list. :rtype: Alias """ for alias in alias_list: href = data.get('alias_ref') if alias.href == href: _alias = Alias(alias.name, href=href) _alias.resolved_value = data.get('resolved_value') _alias.typeof = alias._meta.type return _alias
[docs] def resolve(self, engine): """ Resolve this Alias to a specific value. Specify the engine by name to find it's value. :: alias = Alias('$$ Interface ID 0.ip') alias.resolve('smcpython-fw') :param str engine: name of engine to resolve value :raises ElementNotFound: if alias not found on engine :return: alias resolving values :rtype: list """ if not self.resolved_value: result = self.make_request( ElementNotFound, href=self.get_relation('resolve'), params={'for': engine}) self.resolved_value = result.get('resolved_value') return self.resolved_value