Source code for smc.core.contact_address

"""
A ContactAddress is used by elements to provide an alternate
address for communication between engine and management/log server.
This is typically used when the SMC sits behind a NAT address and 
the SMC needs to contact the engine directly (this is a default behavior).
In this case, you would add the public IP in front of the engine as a 
contact address to the engine interface.

Obtain all eligible interfaces for contact addressess::
        
    >>> engine = Engine('dingo')
    >>> for ca in engine.contact_addresses:
    ...   ca
    ... 
    ContactAddressNode(interface_id=11, interface_ip=10.10.10.20)
    ContactAddressNode(interface_id=120, interface_ip=120.120.120.100)
    ContactAddressNode(interface_id=0, interface_ip=1.1.1.1)
    ContactAddressNode(interface_id=12, interface_ip=3.3.3.3)
    ContactAddressNode(interface_id=12, interface_ip=17.17.17.17)
        
Retrieve a specific contact address interface for modification::

    >>> ca = engine.contact_addresses.get(interface_id=12, interface_ip='3.3.3.3')
    >>> ca
    ContactAddressNode(interface_id=12, interface_ip=3.3.3.3)
    >>> list(ca)
    [InterfaceContactAddress(location=Default,address=4.4.4.4), InterfaceContactAddress(location=Foo,address=3.4.5.6)]

Add a new contact address to the fetched interface::
    
    >>> ca.add_contact_address('23.23.23.23', location='mynewlocation')
    >>> list(ca)
    [InterfaceContactAddress(location=Default,address=4.4.4.4), InterfaceContactAddress(location=Foo,address=3.4.5.6),
     InterfaceContactAddress(location=mynewlocation,address=23.23.23.23)]

Remove a contact address::

    >>> ca.remove_contact_address('23.23.23.23')
    >>> list(ca)
    [InterfaceContactAddress(location=Default,address=4.4.4.4), InterfaceContactAddress(location=Foo,address=3.4.5.6)]
    
.. note:: Contact Addresses for servers (Management/Log Server) do not use
          this same object definition
"""
from smc.base.model import SubElement
from smc.elements.helpers import location_helper
from smc.elements.other import ContactAddress
from smc.base.collection import SubElementCollection


[docs]class InterfaceContactAddress(ContactAddress): """ An interface contact address is used on engine interfaces to provide an alternative location to address mapping. This is frequently used when the engine sits behind a NAT and you need a public NAT mapping, as might be the case with site to site VPN. """ @property def addresses(self): return self.get('address') @property def dynamic(self): return self.get('dynamic', 'false') == 'true'
[docs]class ContactAddressNode(SubElement): """ A mapping of contact address to interface. This is specific to assigning the contact address on the engine. """ def __init__(self, **meta): meta.update(type='contact_addresses') super(ContactAddressNode, self).__init__(**meta) self._name, self._address = self.name.split('_') @property def _cas(self): return self.data.get('contact_addresses', []) def __iter__(self): for addr in self._cas: yield InterfaceContactAddress(addr) def __contains__(self, location_href): for location in self._cas: if location.get('location_ref') == location_href: return True return False
[docs] def delete(self, location_name): """ Remove a given location by location name. This operation is performed only if the given location is valid, and if so, `update` is called automatically. :param str location: location name or location ref :raises UpdateElementFailed: failed to update element with reason :rtype: bool """ updated = False location_ref = location_helper(location_name, search_only=True) if location_ref in self: self._cas[:] = [loc for loc in self if loc.location_ref != location_ref] self.update() updated = True return updated
[docs] def update_or_create(self, location, contact_address, with_status=False, **kw): """ Update an existing contact address or create if the location does not exist. :param str location: name of the location, the location will be added if it doesn't exist :param str contact_address: contact address IP. Can be the string 'dynamic' if this should be a dynamic contact address (i.e. on DHCP interface) :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 UpdateElementFailed: failed to update element with reason :rtype: ContactAddressNode """ updated, created = False, False location_ref = location_helper(location) if location_ref in self: for ca in self: if ca.location_ref == location_ref: ca.update( address=contact_address if 'dynamic' not in contact_address\ else 'First DHCP Interface ip', dynamic='true' if 'dynamic' in contact_address else 'false') updated = True else: self.data.setdefault('contact_addresses', []).append( dict(address=contact_address if 'dynamic' not in contact_address\ else 'First DHCP Interface ip', dynamic='true' if 'dynamic' in contact_address else 'false', location_ref=location_ref)) created = True if updated or created: self.update() if with_status: return self, updated, created return self
[docs] def add_contact_address(self, contact_address, location='Default'): """ Add a contact address to this specified interface. A contact address is an alternative address which is typically applied when NAT is used between the NGFW and another component (such as management server). Adding a contact address operation is committed immediately. :param str contact_address: IP address for this contact address. :raises EngineCommandFailed: invalid contact address :return: ContactAddressNode """ return self.update_or_create(location, contact_address)
[docs] def remove_contact_address(self, location): """ Remove a contact address from an interface by the location name. There is a one to one relationship between a contact address and :param str contact_address: ip for contact address :raises EngineCommandFailed: problem removing address :return: status of delete as boolean :rtype: bool """ return self.delete(location)
@property def interface_id(self): """ The interface ID for this contact address interface :rtype: str """ return self._name.split(' ')[-1] @property def interface_ip(self): """ The IP address for this contact address interface :rtype: str """ return self._address def __str__(self): return '{}(interface_id={}, interface_ip={})'.format( self.__class__.__name__, self.interface_id, self.interface_ip)
[docs]class ContactAddressCollection(SubElementCollection): """ A contact address collection provides all available interfaces that can be used to configure a contact address. An eligible interface is one that is a layer 3 interface with an address assigned (including VLANs):: for ca in engine.contact_addresses: ... .. note:: All eligible interfaces are returned, regardless of whether a contact address is assigned or not. """ def __init__(self, resource): super(ContactAddressCollection, self).__init__( resource, ContactAddressNode)
[docs] def get(self, interface_id, interface_ip=None): """ Get will return a list of interface references based on the specified interface id. Multiple references can be returned if a single interface has multiple IP addresses assigned. :return: If interface_ip is provided, a single ContactAddressNode element is returned if found. Otherwise a list will be returned with all contact address nodes for the given interface_id. """ interfaces = [] for interface in iter(self): if interface.interface_id == str(interface_id): if interface_ip: if interface.interface_ip == interface_ip: return interface else: interfaces.append(interface) return interfaces