Example #1
0
class VSSContext(Engine):
    typeof = 'vss_context'
    virtual_resource = ElementRef('virtual_resource')
    
    def __init__(self, name, **meta):
        super(VSSContext, self).__init__(name, **meta)

    @classmethod
    def create(cls, isc_name, isc_policy_id, isc_traffic_tag, vss_container):
        """
        Create the VSS Context within the VSSContainer

        :param str name: ISC name, possibly append policy name??
        :param str isc_policy_id: Policy ID in SMC (the 'key' attribute)
        :param str isc_traffic_tag: NSX groupId (serviceprofile-145)
        :param VSSContainer vss_container: VSS Container to get create
            context
        :raises CreateElementFailed
        :rtype: VSSContext
         """
        container = element_resolver(vss_container)
        return ElementCreator(cls,
            href=container + '/vss_context',
            json = {
                'vc_isc': {
                    'isc_name': isc_name,
                    'isc_policy_id': isc_policy_id,
                    'isc_traffic_tag': isc_traffic_tag
                }
            })
    
    def remove_from_master_node(self, wait_for_finish=False, timeout=20, **kw):
        """
        Remove this VSS Context from it's parent VSS Container.
        This is required before calling VSSContext.delete(). It preps
        the engine for removal.
        
        :param bool wait_for_finish: wait for the task to finish
        :param int timeout: how long to wait if delay
        :type: TaskOperationPoller
        """
        return Task.execute(self, 'remove_from_master_node',
            timeout=timeout, wait_for_finish=wait_for_finish, **kw)
    
    def move_to_master_node(self, master):
        pass
    
    @property
    def isc_settings(self):
        """
        ISC Settings are used to provide information about the
        security policy mapping for the engine.

        :return dict of engine vc_isc attribute

        'vc_isc': {'isc_name': 'isc_VMPolicy',
                   'isc_policy_id': 17,
                   'isc_traffic_tag': 'serviceprofile-145'},
        """
        return self.data.get('vc_isc')
Example #2
0
class ExternalBGPPeer(Element):
    """
    An External BGP represents the AS and IP settings for a remote
    BGP peer. Creating a BGP peer requires that you also pre-create
    an :class:`~AutonomousSystem` element::

        AutonomousSystem.create(name='neighborA', as_number=500)
        ExternalBGPPeer.create(name='name', 
                               neighbor_as_ref=AutonomousSystem('neighborA'),
                               neighbor_ip='1.1.1.1')
    
    :ivar AutonomousSystem neighbor_as: AS for this external BGP peer
    """
    typeof = 'external_bgp_peer'
    neighbor_as = ElementRef('neighbor_as')

    @classmethod
    def create(cls, name, neighbor_as, neighbor_ip,
               neighbor_port=179, comment=None):
        """
        Create an external BGP Peer. 

        :param str name: name of peer
        :param str,AutonomousSystem neighbor_as_ref: AutonomousSystem
            element or href. 
        :param str neighbor_ip: ip address of BGP peer
        :param int neighbor_port: port for BGP, default 179.
        :raises CreateElementFailed: failed creating
        :return: instance with meta
        :rtype: ExternalBGPPeer
        """
        json = {'name': name,
                'neighbor_ip': neighbor_ip,
                'neighbor_port': neighbor_port,
                'comment': comment}

        neighbor_as_ref = element_resolver(neighbor_as)
        json.update(neighbor_as=neighbor_as_ref)

        return ElementCreator(cls, json)

    @property
    def neighbor_ip(self):
        """
        IP address of the external BGP Peer

        :return: ipaddress of external bgp peer
        :rtype: str
        """
        return self.data.get('neighbor_ip')

    @property
    def neighbor_port(self):
        """
        Port used for neighbor AS

        :return: neighbor port
        :rtype: int
        """
        return self.data.get('neighbor_port')
Example #3
0
class GatewayCertificate(SubElement):
    """
    A Gateway Certificate repesents a certificate assigned to a
    NGFW certificate used for VPN endpoints. Gateway certificates
    are typically renewed automatically when the auto renew option
    is set on the engine. However you can also optionally force
    renew a gateway certificate, export, check the expiration, or
    find the certificate authority that signed this gateway certificate.

    :ivar certificate_authority: CA for this GatewayCertificate
    """

    typeof = "gateway_certificate"
    certificate_authority = ElementRef("certificate_authority")

    @staticmethod
    def _create(
        self,
        common_name,
        public_key_algorithm="rsa",
        signature_algorithm="rsa_sha_512",
        key_length=2048,
        signing_ca=None,
    ):
        """
        Internal method called as a reference from the engine.vpn
        node
        """
        if signing_ca is None:
            signing_ca = VPNCertificateCA.objects.filter("Internal RSA").first()

        cert_auth = element_resolver(signing_ca)

        return ElementCreator(
            GatewayCertificate,
            exception=CertificateError,
            href=self.internal_gateway.get_relation("generate_certificate"),
            json={
                "common_name": common_name,
                "public_key_algorithm": public_key_algorithm,
                "signature_algorithm": signature_algorithm,
                "public_key_length": key_length,
                "certificate_authority_href": cert_auth,
            },
        )

    @property
    def certificate(self):
        return self.certificate_base64

    def renew(self):
        pass

    @property
    def expiration(self):
        pass

    def export_certificate(self):
        pass
class ExternalLdapUserDomain(Browseable, Element):
    """
    External User Domain represents an external LDAP service configured to
    retrieve identity information. Identities are synchronized into SMC and
    then can be used as source objects within a policy.
    
    :ivar list(ActiveDirectoryServer) ldap_server: LDAP server/s used by this
        external domain
    :ivar AuthenticationMethod auth_method: default authentication method for
        this domain. Can also be set as attribute.
    """
    typeof = 'external_ldap_user_domain'
    ldap_server = ElementList('ldap_server')
    auth_method = ElementRef('auth_method')

    @classmethod
    def create(cls,
               name,
               ldap_server,
               isdefault=False,
               auth_method=None,
               comment=None):
        """
        Create an External LDAP user domain. These are used as containers for
        retrieving user and groups from the configured LDAP server/s. If you
        have multiple authentication methods supported for your LDAP server,
        or have none configured, you can set the `auth_method` to
        a supported AuthenticationMethod.
        
        :param str name: name of external LDAP domain
        :param list(str,ActiveDirectoryServer) ldap_server: list of existing
            authentication servers in href or element format
        :param bool isdefault: set this to 'Default LDAP domain'
        :param str,AuthenticationMethod auth_method: authentication method to
            use. Usually set when multiple are defined in LDAP service or
            none are defined.
        :param str comment: optional comment
        :raises CreateElementFailed: failed to create
        :rtype: ExternalLdapUserDomain
        """
        return ElementCreator(cls,
                              json={
                                  'name': name,
                                  'ldap_server': element_resolver(ldap_server),
                                  'auth_method': element_resolver(auth_method),
                                  'isdefault': isdefault,
                                  'comment': comment
                              })
class ValidatePolicyTask(ScheduledTaskMixin, Element):
    """
    Run a policy validation task. This does not perform a policy push.
    This may be useful if you want to validate any pending changes before
    a future policy push.

    :ivar Element policy: The policy associated with this task
    """

    typeof = "validate_policy_task"
    policy = ElementRef("policy")

    @classmethod
    def create(cls, name, engines, policy=None, comment=None, **kwargs):
        """
        Create a new validate policy task.
        If a policy is not specified, the engines existing policy will
        be validated. Override default validation settings as kwargs.

        :param str name: name of task
        :param engines: list of engines to validate
        :type engines: list(Engine)
        :param Policy policy: policy to validate. Uses the engines assigned
            policy if none specified.
        :param kwargs: see :func:`~policy_validation_settings` for keyword
            arguments and default values.
        :raises ElementNotFound: engine or policy specified does not exist
        :raises CreateElementFailed: failure to create the task
        :return: the task
        :rtype: ValidatePolicyTask
        """
        json = {
            "name": name,
            "resources": [eng.href for eng in engines],
            "policy": policy.href if policy is not None else policy,
            "comment": comment,
        }

        if kwargs:
            json.update(policy_validation_settings(**kwargs))

        return ElementCreator(cls, json)
Example #6
0
class DHCPServer(Element):
    """
    A DHCP Server based element. Used in various areas to define External DHCP Server.
    """

    typeof = "dhcp_server"
    location = ElementRef("location_ref")

    @classmethod
    def create(cls,
               name,
               address,
               ipv6_address=None,
               location=None,
               comment=None):
        """
        Create a DHCP Server element.

        :param str name: Name of DHCP Server
        :param str address: IP address for DHCP Server element
        :param str ipv6_address: IPv6 addres fir DHCP Server
        :param str location: Specifies the location for the server if there is a NAT
            device between the server and other SMC components.
        :param str comment: Comment for DHCP Server element
        :raises CreateElementFailed: Failed to create with reason
        :rtype: DHCPServer
        """
        json = {
            "name": name,
            "address": address,
            "ipv6_address": ipv6_address,
            "comment": comment
        }

        if location:
            json.update(location_ref=element_resolver(location))

        return ElementCreator(cls, json)
Example #7
0
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)  
Example #8
0
class Policy(Element):
    """
    Policy is the base class for all policy types managed by the SMC.
    This base class is not intended to be instantiated directly.

    Subclasses should implement create(....) individually as each subclass will likely
    have different input requirements.

    All generic methods that are policy level, such as 'open', 'save', 'force_unlock',
    'export', and 'upload' are encapsulated into this base class.
    
    :ivar Element template: The template associated with this policy. Can be None
    :ivar InspectionPolicy inspection_policy: related inspection policy
    :ivar FileFilteringPolicy file_filtering_policy: related file policy
    """
    template = ElementRef('template')
    inspection_policy = ElementRef('inspection_policy')
    file_filtering_policy = ElementRef('file_filtering_policy')

    def upload(self, engine, timeout=5, wait_for_finish=False, **kw):
        """
        Upload policy to specific device. Using wait for finish
        returns a poller thread for monitoring progress::

            policy = FirewallPolicy('_NSX_Master_Default')
            poller = policy.upload('myfirewall', wait_for_finish=True)
            while not poller.done():
                poller.wait(3)
                print(poller.task.progress)
            print("Task finished: %s" % poller.message())

        :param str engine: name of device to upload policy to
        :raises: TaskRunFailed
        :return: TaskOperationPoller
        """
        return Task.execute(self,
                            'upload',
                            params={'filter': engine},
                            timeout=timeout,
                            wait_for_finish=wait_for_finish,
                            **kw)

    def force_unlock(self):
        """
        Forcibly unlock a locked policy

        :return: None
        """
        self.make_request(PolicyCommandFailed,
                          method='create',
                          resource='force_unlock')

    def search_rule(self, search):
        """
        Search a rule for a rule tag or name value
        Result will be the meta data for rule (name, href, type)

        Searching for a rule in specific policy::

            f = FirewallPolicy(policy)
            search = f.search_rule(searchable)

        :param str search: search string
        :return: rule elements matching criteria
        :rtype: list(Element)
        """
        result = self.make_request(resource='search_rule',
                                   params={'filter': search})

        if result:
            results = []
            for data in result:
                typeof = data.get('type')
                if 'ethernet' in typeof:
                    klazz = lookup_class('ethernet_rule')
                elif typeof in [
                        'ips_ipv4_access_rule', 'l2_interface_ipv4_access_rule'
                ]:
                    klazz = lookup_class('layer2_ipv4_access_rule')
                else:
                    klazz = lookup_class(typeof)
                results.append(klazz(**data))
            return results
        return []

    def rule_counters(self,
                      engine,
                      duration_type='one_week',
                      duration=0,
                      start_time=0):
        """
        .. versionadded:: 0.5.6
            Obtain rule counters for this policy. Requires SMC >= 6.2
        
        Rule counters can be obtained for a given policy and duration for
        those counters can be provided in duration_type. A custom start
        range can also be provided.

        :param Engine engine: the target engine to obtain rule counters from
        :param str duration_type: duration for obtaining rule counters. Valid
            options are: one_day, one_week, one_month, six_months, one_year,
            custom, since_last_upload
        :param int duration: if custom set for duration type, specify the
            duration in seconds (Default: 0)
        :param int start_time: start time in milliseconds (Default: 0)
        :raises: ActionCommandFailed
        :return: list of rule counter objects
        :rtype: RuleCounter
        """
        json = {'target_ref': engine.href, 'duration_type': duration_type}
        return [
            RuleCounter(**rule) for rule in self.make_request(
                method='create', resource='rule_counter', json=json)
        ]
Example #9
0
class ActiveDirectoryServer(ContactAddressMixin, Element):
    """
    Create an Active Directory Element.
    
    At a minimum you must provide the name, address and base_dn for the connection.
    If you do not provide bind_user_id and bind_password, the connection type will
    use anonymous authentication (not recommended).
    
    You can also pass kwargs to customize aspects of the AD server configuration.
    Valid kwargs are:
    
    :param TLSProfile tls_profile: TLS profile used when ldaps or start_tls specified
    :param str user_id_attr: The name that the server uses for the UserID Attribute
        (default: sAMAccountName)
    :param str user_principal_name: The name of the attribute for storing the users UPN
        (default: userPrincipalName)
    :param str display_name_attr_name: The attribute storing the users friendly name
        (default: displayName)
    :param str email: The attribute storing the users email address (default: email)
    :param str group_member_attr: The attribute storing group membership details
        (default: member)
    :param str job_title_attr_name: The attribute storing users job title (default: title)
    :param str frame_ip_attr_name: The attribute storing the users IP address when user
        is authenticated via RADIUS (default: msRADIUSFramedIPAddress)
    :param str mobile_attr_name: The attribute storing the users mobile (default: mobile)
    :param str office_location_attr: The attribute storing the users office location
        (default: physicalDeliveryOfficeName)
    :param str photo: The attribute with users photo used for display (default: photo)
    :param list group_object_class: If your Active Directory or LDAP server has LDAP object
        classes that are not defined in the SMC by default, you must add those object classes
        to the LDAP Object classes in the server properties (default: ['group', 'organizationUnit',
        'organization', 'country', 'groupOfNames', 'sggroup']
    :param list user_object_class: LDAP classes used for user identification (default:
        ['inetOrgPerson','organizationalPerson', 'person', 'sguser'])
    :param str client_cert_based_user_search: Not implemented
    :param int auth_port: required when internet authentication service is enabled (default: 1812)
    :param str auth_ipaddress: required when internet authentication service is enabled
    :param str shared_secret: required when internet authentication service is enabled
    :param int retries: Used with IAS. Number of times firewall will try to connect to the
        RADIUS or TACACS+ authentication server if the connection fails (default: 2)
    """
    typeof = 'active_directory_server'
    tls_profile = ElementRef('tls_profile_ref')
    supported_method = ElementList('supported_method')

    @classmethod
    def create(cls,
               name,
               address,
               base_dn,
               bind_user_id=None,
               bind_password=None,
               port=389,
               protocol='ldap',
               tls_profile=None,
               tls_identity=None,
               domain_controller=None,
               supported_method=None,
               timeout=10,
               max_search_result=0,
               page_size=0,
               internet_auth_service_enabled=False,
               retries=3,
               **kwargs):
        """
        Create an AD server element using basic settings. You can also provide additional
        kwargs documented in the class description::
        
            ActiveDirectoryServer.create(name='somedirectory',
                address='10.10.10.10',
                base_dn='dc=domain,dc=net',
                bind_user_id='cn=admin,cn=users,dc=domain,dc=net',
                bind_password='******')
        
        Configure NPS along with Active Directory::
        
            ActiveDirectoryServer.create(name='somedirectory5',
                address='10.10.10.10',
                base_dn='dc=du,dc=net',
                internet_auth_service_enabled=True,
                retries=3,
                auth_ipaddress='10.10.10.15',
                auth_port=1900,
                shared_secret='123456')
                
        :param str name: name of AD element for display
        :param str address: address of AD server
        :param str base_dn: base DN for which to retrieve users, format is 'dc=domain,dc=com'
        :param str bind_user_id: bind user ID credentials, fully qualified. Format is
            'cn=admin,cn=users,dc=domain,dc=com'. If not provided, anonymous bind is used
        :param str bind_password: bind password, required if bind_user_id set
        :param int port: LDAP bind port, (default: 389)
        :param str protocol: Which LDAP protocol to use, options 'ldap/ldaps/ldap_tls'. If
            ldaps or ldap_tls is used, you must provide a tls_profile element (default: ldap)
        :param str,TLSProfile tls_profile by element of str href. Used when protocol is set
            to ldaps or ldap_tls
        :param str,TLSIdentity tls_identity: check server identity when establishing TLS connection
        :param list(DomainController) domain_controller: list of domain controller objects to
            add an additional domain controllers for AD communication
        :param list(AuthenticationMethod) supported_method: authentication services allowed
            for this resource
        :param int timeout: The time (in seconds) that components wait for the server to reply
        :param int max_search_result: The maximum number of LDAP entries that are returned in
            an LDAP response (default: 0 for no limit)
        :param int page_size: The maximum number of LDAP entries that are returned on each page
            of the LDAP response. (default: 0 for no limit)
        :param bool internet_auth_service_enabled: whether to attach an NPS service to this
            AD controller (default: False). If setting to true, provide kwargs values for
            auth_ipaddress, auth_port and shared_secret
        :raises CreateElementFailed: failed creating element
        :rtype: ActiveDirectoryServer
        """
        json = {
            'name': name,
            'address': address,
            'base_dn': base_dn,
            'bind_user_id': bind_user_id,
            'bind_password': bind_password,
            'port': port,
            'protocol': protocol,
            'timeout': timeout,
            'domain_controller': domain_controller or [],
            'retries': retries,
            'max_search_result': max_search_result,
            'page_size': page_size,
            'internet_auth_service_enabled': internet_auth_service_enabled,
            'supported_method': element_resolver(supported_method) or []
        }

        for obj_class in ('group_object_class', 'user_object_class'):
            json[obj_class] = kwargs.pop(obj_class, [])

        if protocol in ('ldaps', 'ldap_tls'):
            if not tls_profile:
                raise CreateElementFailed(
                    'You must provide a TLS Profile when TLS '
                    'connections are configured to the AD controller.')
            json.update(tls_profile_ref=element_resolver(tls_profile),
                        tls_identity=tls_identity)

        if internet_auth_service_enabled:
            ias = {
                'auth_port': kwargs.pop('auth_port', 1812),
                'auth_ipaddress': kwargs.pop('auth_ipaddress', ''),
                'shared_secret': kwargs.pop('shared_secret')
            }
            json.update(ias)

        json.update(kwargs)
        return ElementCreator(cls, json)

    @classmethod
    def update_or_create(cls, with_status=False, **kwargs):
        """
        Update or create active directory configuration.
        
        :param dict kwargs: kwargs to satisfy the `create` constructor arguments
            if the element doesn't exist or attributes to change
        :raises CreateElementFailed: failed creating element
        :return: element instance by type or 3-tuple if with_status set
        """
        element, updated, created = super(
            ActiveDirectoryServer, cls).update_or_create(defer_update=True,
                                                         **kwargs)

        if not created:
            domain_controller = kwargs.pop('domain_controller', [])
            if domain_controller:
                current_dc_list = element.domain_controller
            for dc in domain_controller:
                if dc not in current_dc_list:
                    element.data.setdefault('domain_controller',
                                            []).append(dc.data)
                    updated = True

        if updated:
            element.update()
        if with_status:
            return element, updated, created
        return element

    @property
    def domain_controller(self):
        """
        List of optional domain controllers specified for this AD resource.
        When adding domain controllers through update_or_create, only domain
        controllers that do not already exist are added.
        
        :rtype: list(DomainController)
        """
        return [
            DomainController(**dc)
            for dc in self.data.get('domain_controller', [])
        ]

    def check_connectivity(self):
        """
        Return a status for this active directory controller
        
        :raises ActionCommandFailed: failed to check connectivity with reason
        :rtype: bool
        """
        return self.make_request(
            href=self.get_relation('check_connectivity')) is None
Example #10
0
class OSPFProfile(Element):
    """
    An OSPF Profile contains administrative distance and redistribution 
    settings. An OSPF Profile is set on the engine element when enabling
    OSPF.

    These settings are always in effect:

    * No autosummary

    Example of creating an OSPFProfile with the default domain profile::

        OSPFProfile.create(name='myospf')

    .. note:: Enable OSPF on engine using engine.ospf.enable()
    
    :ivar int external_distance: external distance metric
    :ivar int inter_distance: inter distance metric
    :ivar int intra_distance: intra distance metric
    :ivar int default_metric: set a default metric for all unset areas
    :ivar list redistribution_entry: settings for static, connected, etc
    :ivar OSPFDomainSetting domain_settings_ref: OSPF Domain Settings profile
        used for this OSPF Profile
    """
    typeof = 'ospfv2_profile'
    domain_settings_ref = ElementRef('domain_settings_ref')

    @classmethod
    def create(cls, name, domain_settings_ref=None, external_distance=110,
               inter_distance=110, intra_distance=110, redistribution_entry=None,
               default_metric=None, comment=None):
        """
        Create an OSPF Profile.
        
        If providing a list of redistribution entries, provide in the following
        dict format: 
        
        {'enabled': boolean, 'metric_type': 'external_1' or 'external_2',
         'metric': 2, 'type': 'kernel'}
        
        Valid types for redistribution entries are: kernel, static, connected, bgp,
        and default_originate.
        
        You can also provide a 'filter' key with either an IPAccessList or RouteMap
        element to use for further access control on the redistributed route type.
        If metric_type is not provided, external_1 (E1) will be used. 
        
        An example of a redistribution_entry would be::
        
            {u'enabled': True,
             u'metric': 123,
             u'metric_type': u'external_2',
             u'filter': RouteMap('myroutemap'),
             u'type': u'static'}

        :param str name: name of profile
        :param str,OSPFDomainSetting domain_settings_ref: OSPFDomainSetting 
            element or href
        :param int external_distance: route metric (E1-E2)
        :param int inter_distance: routes learned from different areas (O IA)
        :param int intra_distance: routes learned from same area (O)
        :param list redistribution_entry: how to redistribute the OSPF routes.
        :raises CreateElementFailed: create failed with reason
        :rtype: OSPFProfile
        """
        json = {'name': name,
                'external_distance': external_distance,
                'inter_distance': inter_distance,
                'intra_distance': intra_distance,
                'default_metric': default_metric,
                'comment': comment}
        
        if redistribution_entry:
            json.update(redistribution_entry=
                _format_redist_entry(redistribution_entry))

        domain_settings_ref = element_resolver(domain_settings_ref) or \
            OSPFDomainSetting('Default OSPFv2 Domain Settings').href

        json.update(domain_settings_ref=domain_settings_ref)

        return ElementCreator(cls, json)
    
    @classmethod
    def update_or_create(cls, filter_key=None, with_status=False, **kwargs):
        if 'redistribution_entry' in kwargs:
            kwargs.update(redistribution_entry=_format_redist_entry(
                kwargs.pop('redistribution_entry', [])))
        
        element, updated, created = super(OSPFProfile, cls).update_or_create(
            filter_key, defer_update=True, **kwargs)
        if not created:
            # Deferred update, update redistribution settings
            if 'redistribution_entry' in kwargs:
                element.data['redistribution_entry'] = kwargs.get('redistribution_entry')
                element.update()
                updated = True
        
        if with_status:
            return element, updated, created
        return element
Example #11
0
class OSPF(object):
    """
    OSPF configuration on the engine. Access through an engine reference::
        
        engine.dynamic_routing.ospf.status
        engine.dynamic_rotuing.ospf.enable(....)
        
    When making changes to the OSPF configuration, any methods
    called that change the configuration also require that
    engine.update() is called once changes are complete. This way
    you can make multiple changes without refreshing the engine cache.
    
    :ivar OSPFProfile profile: OSPFProfile reference for this engine
    """
    profile = ElementRef('ospfv2_profile_ref')
    
    def __init__(self, data=None):
        self.data = data if data else ElementCache()
    
    @property
    def router_id(self):
        """
        Get the router ID for this OSPF configuration. If None, then
        the ID will use the interface IP.
        
        :return: str or None
        """
        return self.data.get('router_id')
    
    @property
    def status(self):
        """
        Is OSPF enabled on this engine.
        
        :rtype: bool
        """
        return self.data.get('enabled')
    
    def disable(self):
        """
        Disable OSPF on this engine.

        :return: None
        """
        self.data.update(
            enabled=False)

    def enable(self, ospf_profile=None, router_id=None):
        """
        Enable OSPF on this engine. For master engines, enable
        OSPF on the virtual firewall.

        Once enabled on the engine, add an OSPF area to an interface::

            engine.dynamic_routing.ospf.enable()
            interface = engine.routing.get(0)
            interface.add_ospf_area(OSPFArea('myarea'))

        :param str,OSPFProfile ospf_profile: OSPFProfile element or str
            href; if None, use default profile
        :param str router_id: single IP address router ID
        :raises ElementNotFound: OSPF profile not found
        :return: None
        """
        ospf_profile = element_resolver(ospf_profile) if ospf_profile \
            else OSPFProfile('Default OSPFv2 Profile').href

        self.data.update(
            enabled=True,
            ospfv2_profile_ref=ospf_profile,
            router_id=router_id)
    
    def update_configuration(self, **kwargs):
        """
        Update the OSPF configuration using kwargs that match the
        `enable` constructor.
        
        :param dict kwargs: keyword arguments matching enable constructor.
        :return: whether change was made
        :rtype: bool
        """
        updated = False
        if 'ospf_profile' in kwargs:
            kwargs.update(ospfv2_profile_ref=kwargs.pop('ospf_profile'))
        for name, value in kwargs.items():
            _value = element_resolver(value)
            if self.data.get(name) != _value:
                self.data[name] = _value
                updated = True
        return updated
Example #12
0
class OSPFArea(Element):
    """
    OSPF Area is an element that identifies general settings for an
    OSPF configuration applied to an engine routing node. The OSPFArea
    has a reference to an OSPFInterfaceSetting and is required when
    creating.

    Create a basic OSPFArea with just area id::

        OSPFArea.create(name='myarea', area_id=0)

    Create an OSPFArea and use a custom OSPFInterfaceSetting element::

        OSPFArea.create(
            name='customOSPFArea', 
            interface_settings_ref=OSPFInterfaceSetting('myospf'), 
            area_id=3)

    **Advanced example:**

    Adding ospf_virtual_links_endpoints::

        OSPFArea.create(
            name='ospf', 
            interface_settings_ref=intf, 
            area_id=3,
            ospfv2_virtual_links_endpoints_container=
                [{'interface_settings_ref': 
                    'http://172.18.1.150:8082/6.1/elements/ospfv2_interface_settings/8',
                  'router_id_endpoint_A': '192.168.1.1',
                  'router_id_endpoint_B': '192.168.1.254'},
                 {'router_id_endpoint_A': '172.18.1.254',
                  'router_id_endpoint_B': '172.18.1.200'}])

    When using ABR substitute rules, there are 3 actions, 'aggregate', 'not_advertise'
    and 'substitute_with'. All references required are of type 
    :py:class:`smc.elements.network.Network`. These elements can either be created or
    retrieved using collections, or by getting the resource directly.

    Example of creating an OSPF area and using ABR settings::

        OSPFArea.create(
                name='area_with_abr', 
                interface_settings_ref=intf, 
                area_id=1, 
                ospf_abr_substitute_container=[
                    {'subnet_ref': 'http://172.18.1.150:8082/6.1/elements/network/143',
                     'substitute_ref': 'http://172.18.1.150:8082/6.1/elements/network/1547',
                     'substitute_type': 'substitute_with'},
                    {'subnet_ref': 'http://172.18.1.150:8082/6.1/elements/network/979',
                     'substitute_type': 'aggregate'}])
    
    :ivar OSPFInterfaceSetting interface_settings_ref: reference to the OSPFInterfaceSetting
        element
    """
    typeof = 'ospfv2_area'
    interface_settings_ref = ElementRef('interface_settings_ref')

    @classmethod
    def create(cls, name, interface_settings_ref=None, area_id=1,
               area_type='normal', outbound_filters=None,
               inbound_filters=None, shortcut_capable_area=False,
               ospfv2_virtual_links_endpoints_container=None,
               ospf_abr_substitute_container=None, comment=None, **kwargs):
        """
        Create a new OSPF Area

        :param str name: name of OSPFArea configuration
        :param str,OSPFInterfaceSetting interface_settings_ref: an OSPFInterfaceSetting
            element or href. If None, uses the default system profile
        :param str name: area id
        :param str area_type: \|normal\|stub\|not_so_stubby\|totally_stubby\|
               totally_not_so_stubby
        :param list outbound_filters: reference to an IPAccessList and or IPPrefixList.
            You can only have one outbound prefix or access list
        :param list inbound_filters: reference to an IPAccessList and or IPPrefixList.
            You can only have one outbound prefix or access list
        :param shortcut_capable_area: True|False
        :param list ospfv2_virtual_links_endpoints_container: virtual link endpoints
        :param list ospf_abr_substitute_container: substitute types: 
               \|aggregate\|not_advertise\|substitute_with
        :param str comment: optional comment
        :raises CreateElementFailed: failed to create with reason
        :rtype: OSPFArea
        """
        interface_settings_ref = element_resolver(interface_settings_ref) or \
            OSPFInterfaceSetting('Default OSPFv2 Interface Settings').href
        
        if 'inbound_filters_ref' in kwargs:
            inbound_filters = kwargs.get('inbound_filters_ref')
        
        if 'outbound_filters_ref' in kwargs:
            outbound_filters = kwargs.get('outbound_filters_ref')
        
        json = {'name': name,
                'area_id': area_id,
                'area_type': area_type,
                'comment': comment,
                'inbound_filters_ref': element_resolver(inbound_filters),
                'interface_settings_ref': interface_settings_ref,
                'ospf_abr_substitute_container': ospf_abr_substitute_container,
                'ospfv2_virtual_links_endpoints_container':
                    ospfv2_virtual_links_endpoints_container,
                'outbound_filters_ref': element_resolver(outbound_filters),
                'shortcut_capable_area': shortcut_capable_area}

        return ElementCreator(cls, json)

    @classmethod
    def update_or_create(cls, filter_key=None, with_status=False, **kwargs):
        if 'inbound_filters' in kwargs:
            kwargs.update(inbound_filters_ref=
                element_resolver(kwargs.pop('inbound_filters')))
        if 'outbound_filters' in kwargs:
            kwargs.update(outbound_filters_ref=
                element_resolver(kwargs.pop('outbound_filters')))
        return super(OSPFArea, cls).update_or_create(filter_key, with_status, **kwargs)

    @property
    def inbound_filters(self):
        """
        Inbound filters attached to this OSPF Area. Filters can be type
        IPPrefixList or IPAccessList
        
        :rtype: list
        """
        return [Element.from_href(filt)
            for filt in self.data.get('inbound_filters_ref', [])]
    
    @property
    def outbound_filters(self):
        """
        Outbound filters attached to this OSPF Area. Filters can be type
        IPPrefixList or IPAccessList
        
        :rtype: list
        """
        return [Element.from_href(filt)
            for filt in self.data.get('outbound_filters_ref', [])]
Example #13
0
class OSPFInterfaceSetting(Element):
    """
    OSPF Interface Setting indicate specific configurations that are
    applied to the interface and OSPF Area configuration, including
    authentication.

    If you require non-default settings applied to your interface
    OSPF instance, you can create a custom interface profile::

        OSPFInterfaceSetting.create(
            name='myprofile', 
            dead_interval=30, 
            hello_interval=5)

    When using authentication on interface settings, there are two types,
    password authentication (plain text) or message digest. 

    When specifying an authentication_type='password', the password parameter
    must be provided. 

    When specifying authentication_type='message_digest', the key_chain_ref
    parameter must be specified.
    """
    typeof = 'ospfv2_interface_settings'
    key_chain_ref = ElementRef('key_chain_ref')

    @classmethod
    def create(cls, name, dead_interval=40, hello_interval=10,
               hello_interval_type='normal', dead_multiplier=1,
               mtu_mismatch_detection=True, retransmit_interval=5,
               router_priority=1, transmit_delay=1,
               authentication_type=None, password=None,
               key_chain_ref=None):
        """
        Create custom OSPF interface settings profile

        :param str name: name of interface settings
        :param int dead_interval: in seconds
        :param str hello_interval: in seconds
        :param str hello_interval_type: \|normal\|fast_hello
        :param int dead_multipler: fast hello packet multipler
        :param bool mtu_mismatch_detection: True|False
        :param int retransmit_interval: in seconds
        :param int router_priority: set priority
        :param int transmit_delay: in seconds
        :param str authentication_type: \|password\|message_digest
        :param str password: max 8 chars (required when 
               authentication_type='password')
        :param str,Element key_chain_ref: OSPFKeyChain (required when 
               authentication_type='message_digest')
        :raises CreateElementFailed: create failed with reason
        :return: instance with meta
        :rtype: OSPFInterfaceSetting
        """
        json = {'name': name,
                'authentication_type': authentication_type,
                'password': password,
                'key_chain_ref': element_resolver(key_chain_ref),
                'dead_interval': dead_interval,
                'dead_multiplier': dead_multiplier,
                'hello_interval': hello_interval,
                'hello_interval_type': hello_interval_type,
                'mtu_mismatch_detection': mtu_mismatch_detection,
                'retransmit_interval': retransmit_interval,
                'router_priority': router_priority,
                'transmit_delay': transmit_delay}

        return ElementCreator(cls, json)
Example #14
0
class ElasticsearchCluster(ContactAddressMixin, Element):
    """
    An ElasticsearchCluster server type element.
    """

    typeof = "elasticsearch_cluster"
    location = ElementRef("location_ref")

    @classmethod
    def create(
        cls,
        name,
        addresses,
        port=9200,
        es_retention_period=30,
        es_shard_number=0,
        es_replica_number=0,
        enable_cluster_sniffer=False,
        location=None,
        comment=None,
        tls_profile=None,
        use_internal_credentials=True,
        tls_credentials=None,
    ):
        """
        Create a Elasticsearch Cluster Server element.

        :param str name: Name of Elasticsearch Cluster
        :param list addresses: comma-separated list of one or more FQDNs or IP addresses
        :param int port: Default port is 9200
        :param int es_retention_period: How much time logs will be kept
        30days default
        :param int es_shard_number: Auto by default, number of shards
        :param int es_replica_number : number of ES replicas
        :param bool enable_cluster_sniffer : Enable cluster sniffer (False
        default)
        :param str location: Specifies the location for the server if there
        is a NAT device between the server and other SMC components.
        :param str comment: Comment for Elasticsearch cluster Server element
        :param str tls_profile: tls profile name to use
        :param bool use_internal_credentials: use internal credentials
        :param str tls_credentials: tls credentials name to use

        :raises CreateElementFailed: Failed to create with reason
        :rtype: ElasticsearchCluster
        """
        json = {
            "name": name,
            "port": port,
            "es_retention_period": es_retention_period,
            "es_shard_number": es_shard_number,
            "es_replica_number": es_replica_number,
            "es_enable_cluster_sniffer": enable_cluster_sniffer,
            "comment": comment,
        }

        addresses_lst = addresses.split(",")
        json.update(addresses=addresses_lst)
        if location:
            location_href = Location(location).href
            json.update(location_ref=location_href)
        if tls_profile:
            tls_profile_ref = tls.TLSProfile(tls_profile).href
            json.update(tls_profile=tls_profile_ref)
        if tls_credentials:
            tls_credentials_ref = tls.TLSServerCredential(tls_credentials).href
            json.update(
                es_tls_settings={
                    "use_internal_credentials": use_internal_credentials,
                    "tls_credentials": tls_credentials_ref,
                })
        else:
            json.update(es_tls_settings={
                "use_internal_credentials": use_internal_credentials
            })

        return ElementCreator(cls, json)
Example #15
0
class TunnelEndpoint(object):
    """
    A Tunnel Endpoint represents one side of a route based VPN.
    Based on the RBVPN type required, you must create the local
    and remote endpoints and pass them into the RouteVPN create
    classmethods.

    :ivar InternalGateway,ExternalGateway gateway: reference to the element
        that is used by this tunnel endpoint
    :ivar TunnelInterface tunnel_interface: Tunnel interface used by this tunnel
        endpoint
    """

    gateway = ElementRef("gateway_ref")

    def __init__(self,
                 gateway_ref=None,
                 tunnel_interface_ref=None,
                 endpoint_ref=None,
                 ip_address=None):
        self.gateway_ref = gateway_ref
        self.tunnel_interface_ref = tunnel_interface_ref
        self.endpoint_ref = endpoint_ref
        self.ip_address = ip_address

    @property
    def endpoint(self):
        """
        Endpoint is used to specify which interface is enabled for
        VPN. This is the InternalEndpoint property of the
        InternalGateway.

        .. note:: This will only return a value if the tunnel type is GRE

        :return: internal endpoint where VPN is enabled
        :rtype: InternalEndpoint,ExternalGateway
        """
        if self.endpoint_ref and self.tunnel_interface_ref:
            return InternalEndpoint(href=self.endpoint_ref)
        return Element.from_href(self.endpoint_ref)

    @property
    def tunnel_interface(self):
        """
        Show the tunnel interface for this TunnelEndpoint.

        :return: interface for this endpoint
        :rtype: TunnelInterface
        """
        if self.tunnel_interface_ref:
            return TunnelInterface(href=self.tunnel_interface_ref)

    @classmethod
    def create_gre_tunnel_endpoint(cls,
                                   endpoint=None,
                                   tunnel_interface=None,
                                   remote_address=None):
        """
        Create the GRE tunnel mode or no encryption mode endpoint.
        If the GRE tunnel mode endpoint is an SMC managed device,
        both an endpoint and a tunnel interface is required. If the
        endpoint is externally managed, only an IP address is required.

        :param InternalEndpoint,ExternalEndpoint endpoint: the endpoint
            element for this tunnel endpoint.
        :param TunnelInterface tunnel_interface: the tunnel interface for
            this tunnel endpoint. Required for SMC managed devices.
        :param str remote_address: IP address, only required if the tunnel
            endpoint is a remote gateway.
        :rtype: TunnelEndpoint
        """
        tunnel_interface = tunnel_interface.href if tunnel_interface else None
        endpoint = endpoint.href if endpoint else None
        return TunnelEndpoint(tunnel_interface_ref=tunnel_interface,
                              endpoint_ref=endpoint,
                              ip_address=remote_address)

    @classmethod
    def create_gre_transport_endpoint(cls, endpoint, tunnel_interface=None):
        """
        Create the GRE transport mode endpoint. If the GRE transport mode
        endpoint is an SMC managed device, both an endpoint and a tunnel
        interface is required. If the GRE endpoint is an externally managed
        device, only an endpoint is required.

        :param InternalEndpoint,ExternalEndpoint endpoint: the endpoint
            element for this tunnel endpoint.
        :param TunnelInterface tunnel_interface: the tunnel interface for
            this tunnel endpoint. Required for SMC managed devices.
        :rtype: TunnelEndpoint
        """
        tunnel_interface = tunnel_interface.href if tunnel_interface else None
        return TunnelEndpoint(endpoint_ref=endpoint.href,
                              tunnel_interface_ref=tunnel_interface)

    @classmethod
    def create_ipsec_endpoint(cls, gateway, tunnel_interface=None):
        """
        Create the VPN tunnel endpoint. If the VPN tunnel endpoint
        is an SMC managed device, both a gateway and a tunnel interface
        is required. If the VPN endpoint is an externally managed
        device, only a gateway is required.

        :param InternalGateway,ExternalGateway gateway: the gateway
            for this tunnel endpoint
        :param TunnelInterface tunnel_interface: Tunnel interface for
            this RBVPN. This can be None if the gateway is a non-SMC
            managed gateway.
        :rtype: TunnelEndpoint
        """
        tunnel_interface = tunnel_interface.href if tunnel_interface else None
        return TunnelEndpoint(gateway_ref=gateway.href,
                              tunnel_interface_ref=tunnel_interface)

    @property
    def data(self):
        return {k: v for k, v in vars(self).items() if v}

    @property
    def remote_address(self):
        """
        Show the remote IP address configured for a GRE RBVPN using Tunnel
        or No Encryption Mode configurations.
        """
        return self.ip_address
Example #16
0
class PolicyVPN(Element):
    """
    Create a new VPN Policy.
    ::

        >>> PolicyVPN.create(name='myvpn')
        PolicyVPN(name=myvpn)
        >>> v = PolicyVPN('myvpn')
        >>> print(v.vpn_profile)
        VPNProfile(name=VPN-A Suite)

    When making VPN Policy modifications, you must first call :func:`open`,
    make your modifications and then call :func:`save` followed by
    :func:`close`.

    :ivar VPNProfile vpn_profile: VPN Profile used by this Policy VPN

    """

    typeof = "vpn"
    vpn_profile = ElementRef("vpn_profile")

    @classmethod
    def create(cls, name, nat=False, mobile_vpn_toplogy_mode=None, vpn_profile=None):
        """
        Create a new policy based VPN

        :param name: name of vpn policy
        :param bool nat: whether to apply NAT to the VPN (default False)
        :param mobile_vpn_toplogy_mode: whether to allow remote vpn
        :param VPNProfile vpn_profile: reference to VPN profile, or uses default
        :rtype: PolicyVPN
        """
        vpn_profile = element_resolver(vpn_profile) or VPNProfile("VPN-A Suite").href

        json = {
            "mobile_vpn_topology_mode": mobile_vpn_toplogy_mode,
            "name": name,
            "nat": nat,
            "vpn_profile": vpn_profile,
        }

        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreatePolicyFailed(err)

    @property
    def nat(self):
        """
        Is NAT enabled on this vpn policy

        :return: NAT enabled
        :rtype: bool
        """
        return self.data.get("nat")

    def enable_disable_nat(self):
        """
        Enable or disable NAT on this policy. If NAT is disabled, it
        will be enabled and vice versa.

        :return: None
        """
        if self.nat:
            self.data["nat"] = False
        else:
            self.data["nat"] = True

    @property
    def central_gateway_node(self):
        """
        Central Gateway Node acts as the hub of a hub-spoke VPN.

        :rtype: SubElementCollection(GatewayNode)
        """
        return sub_collection(
            self.get_relation("central_gateway_node"),
            type("CentralGatewayNode", (GatewayNode,), {}),
        )

    @property
    def satellite_gateway_node(self):
        """
        Node level settings for configured satellite gateways

        :rtype: SubElementCollection(GatewayNode)
        """
        return sub_collection(
            self.get_relation("satellite_gateway_node"),
            type("SatelliteGatewayNode", (GatewayNode,), {}),
        )

    @property
    def mobile_gateway_node(self):
        """
        Mobile Gateway's are represented by client endpoints connecting
        to the policy based VPN.

        :rtype: SubElementCollection(GatewayNode)
        """
        return sub_collection(
            self.get_relation("mobile_gateway_node"), type("MobileGatewayNode", (GatewayNode,), {})
        )

    @property
    def tunnels(self):
        """
        Return all tunnels for this VPN. A tunnel is defined as two end
        points within the VPN topology. Endpoints are automatically
        configureed based on whether they are a central gateway or
        satellite gateway. This provides access to enabling/disabling
        and setting the preshared key for the linked endpoints.
        List all tunnel mappings for this policy vpn::

            for tunnel in policy.tunnels:
                tunnela = tunnel.tunnel_side_a
                tunnelb = tunnel.tunnel_side_b
                print(tunnela.gateway)
                print(tunnelb.gateway)

        :rtype: SubElementCollection(GatewayTunnel)
        """
        return sub_collection(self.get_relation("gateway_tunnel"), GatewayTunnel)

    def open(self):
        """
        Open the policy for editing. This is only a valid method for
        SMC version <= 6.1

        :raises PolicyCommandFailed: couldn't open policy with reason
        :return: None
        """
        self.make_request(PolicyCommandFailed, method="create", resource="open")

    def save(self):
        """
        Save the policy after editing. This is only a valid method for
        SMC version <= 6.1

        :raises PolicyCommandFailed: save failed with reason
        :return: None
        """
        self.make_request(PolicyCommandFailed, method="create", resource="save")
        self._del_cache()

    def close(self):
        """
        Close the policy. This is only a valid method for
        SMC version <= 6.1

        :raises PolicyCommandFailed: close failed with reason
        :return: None
        """
        self.make_request(PolicyCommandFailed, method="create", resource="close")

    def validate(self):
        """
        Return a validation string from the SMC after running validate on
        this VPN policy.

        :return: status as dict
        :rtype: dict
        """
        return self.make_request(resource="validate")

    @property
    def mobile_vpn_topology(self):
        """
        Is the policy VPN configured for mobile VPN gateways.
        Valid modes: 'Selected Gateways below', 'Only central Gateways from overall topology',
        'All Gateways from overall topology', 'None'
        """
        return self.data.get("mobile_vpn_topology_mode")

    def add_mobile_gateway(self, all_central_gateways=False, all_gateways=False, gateways=None):
        """
        Add a mobile VPN gateway to this policy VPN. You can select all central
        gateways, all gateways in overall topology or specify a list of gateways
        to allow for mobile VPN.

        Example of adding or removing a mobile VPN gateway::

            policy_vpn = PolicyVPN('myvpn')
            policy_vpn.update(mobile_vpn_topology_mode='Selected Gateways below')
            policy_vpn.open()

            policy_vpn.add_mobile_vpn_gateway(gateways=Engine('azure'))

            policy_vpn.save()
            policy_vpn.close()

        :param Engine,ExternalGateway gateway: An external gateway, engine or
            href for the mobile gateway
        :raises PolicyCommandFailed: could not add gateway
        :rtype: None
        """
        if all_central_gateways:
            self.update(mobile_vpn_topology_mode="Only central Gateways from overall topology")
        elif all_gateways:
            self.update(mobile_vpn_topology_mode="All Gateways from overall topology")

        if gateways and self.mobile_vpn_topology != "Selected Gateways below":
            raise PolicyCommandFailed(
                "You must first update the policy VPN with "
                "the Selected Gateways below setting before adding members"
            )

        if gateways:
            try:
                gateway = gateways.vpn.internal_gateway.href  # Engine
            except AttributeError:
                raise PolicyCommandFailed(
                    "VPN endpoint does not appear to " "be a managed engine: %s" % gateways
                )

            self.make_request(
                PolicyCommandFailed,
                method="create",
                resource="mobile_gateway_node",
                json={"gateway": gateway, "node_usage": "mobile"},
            )

    def add_central_gateway(self, gateway):
        """
        Add SMC managed internal gateway to the Central Gateways of this VPN

        :param Engine,ExternalGateway gateway: An external gateway, engine or
            href for the central gateway
        :raises PolicyCommandFailed: could not add gateway
        :return: None
        """
        try:
            gateway = gateway.vpn.internal_gateway.href
        except AttributeError:
            gateway = element_resolver(gateway)
        self.make_request(
            PolicyCommandFailed,
            method="create",
            resource="central_gateway_node",
            json={"gateway": gateway, "node_usage": "central"},
        )

    def add_satellite_gateway(self, gateway):
        """
        Add gateway node as a satellite gateway for this VPN. You must first
        have the gateway object created. This is typically used when you either
        want a hub-and-spoke topology or the test_external gateway is a non-SMC
        managed device.

        :param Engine,ExternalGateway gateway: An external gateway, engine or
            href for the central gateway
        :raises PolicyCommandFailed: could not add gateway
        :return: None
        """
        try:
            gateway = gateway.vpn.internal_gateway.href
        except AttributeError:
            gateway = element_resolver(gateway)
        self.make_request(
            PolicyCommandFailed,
            method="create",
            resource="satellite_gateway_node",
            json={"gateway": gateway, "node_usage": "satellite"},
        )

    @staticmethod
    def add_internal_gateway_to_vpn(internal_gateway_href, vpn_policy, vpn_role="central"):
        """
        Add an internal gateway (managed engine node) to a VPN policy
        based on the internal gateway href.

        :param str internal_gateway_href: href for engine internal gw
        :param str vpn_policy: name of vpn policy
        :param str vpn_role: central|satellite
        :return: True for success
        :rtype: bool
        """
        try:
            vpn = PolicyVPN(vpn_policy)
            vpn.open()
            if vpn_role == "central":
                vpn.add_central_gateway(internal_gateway_href)
            else:
                vpn.add_satellite_gateway(internal_gateway_href)
            vpn.save()
            vpn.close()
        except ElementNotFound:
            return False
        return True
Example #17
0
class VPNSite(SubElement):
    """
    VPN Site information for an internal or test_external gateway
    Sites are used to encapsulate hosts or networks as 'protected' for VPN
    policy configuration.

    Create a new vpn site for an engine::

        engine = Engine('myengine')
        network = Network('network-192.168.5.0/25') #get resource
        engine.vpn.sites.create('newsite', [network.href])

    Sites can also be added to ExternalGateway's as well::

        extgw = ExternalGateway('mygw')
        extgw.vpn_site.create('newsite', [Network('foo')])

    This class is a property of :py:class:`smc.core.engine.InternalGateway`
    or :py:class:`smc.vpn.elements.ExternalGateway` and should not be accessed
    directly.

    :ivar InternalGateway,ExternalGateway gateway: gateway referenced
    """

    typeof = "vpn_site"
    gateway = ElementRef("gateway")

    def create(self, name, site_element):
        """
        Create a VPN site for an internal or external gateway

        :param str name: name of site
        :param list site_element: list of protected networks/hosts
        :type site_element: list[str,Element]
        :raises CreateElementFailed: create element failed with reason
        :return: href of new element
        :rtype: str
        """
        site_element = element_resolver(site_element)
        json = {"name": name, "site_element": site_element}

        return ElementCreator(self.__class__, href=self.href, json=json)

    @classmethod
    def update_or_create(cls,
                         external_gateway,
                         name,
                         site_element=None,
                         with_status=False):
        """
        Update or create a VPN Site elements or modify an existing VPN
        site based on value of provided site_element list. The resultant
        VPN site end result will be what is provided in the site_element
        argument (can also be an empty list to clear existing).

        :param ExternalGateway external_gateway: The external gateway for
            this VPN site
        :param str name: name of the VPN site
        :param list(str,Element) site_element: list of resolved Elements to
            add to the VPN site
        :param bool with_status: If set to True, returns a 3-tuple of
            (VPNSite, modified, created), where modified and created
            is the boolean status for operations performed.
        :raises ElementNotFound: ExternalGateway or unresolved site_element
        """
        site_element = [] if not site_element else site_element
        site_elements = [element_resolver(element) for element in site_element]
        vpn_site = external_gateway.vpn_site.get_exact(name)
        updated = False
        created = False
        if vpn_site:  # If difference, reset
            if set(site_elements) != set(vpn_site.data.get("site_element",
                                                           [])):
                vpn_site.data["site_element"] = site_elements
                vpn_site.update()
                updated = True

        else:
            vpn_site = external_gateway.vpn_site.create(
                name=name, site_element=site_elements)
            created = True

        if with_status:
            return vpn_site, updated, created
        return vpn_site

    @property
    def name(self):
        name = super(VPNSite, self).name
        if not name:
            return self.data.get("name")
        return name

    @property
    def site_element(self):
        """
        Site elements for this VPN Site.

        :return: Elements used in this VPN site
        :rtype: list(Element)
        """
        return [
            Element.from_href(element)
            for element in self.data.get("site_element")
        ]

    def add_site_element(self, element):
        """
        Add a site element or list of elements to this VPN.

        :param list element: list of Elements or href's of vpn site
            elements
        :type element: list(str,Network)
        :raises UpdateElementFailed: fails due to reason
        :return: None
        """
        element = element_resolver(element)
        self.data["site_element"].extend(element)
        self.update()
Example #18
0
class BGPPeering(Element):
    """
    BGP Peering is applied directly to an interface and defines
    basic connection settings. A BGPConnectionProfile is required
    to create a BGPPeering and if not provided, the default profile
    will be used.

    The most basic peering can simply specify the name of the peering
    and leverage the default BGPConnectionProfile::

        BGPPeering.create(name='my-aws-peer')

    :ivar BGPConnectionProfile connection_profile: BGP connection profile for this
        peering
    """
    typeof = 'bgp_peering'
    connection_profile = ElementRef('connection_profile')

    @classmethod
    def create(cls,
               name,
               connection_profile_ref=None,
               md5_password=None,
               local_as_option='not_set',
               max_prefix_option='not_enabled',
               send_community='no',
               connected_check='disabled',
               orf_option='disabled',
               next_hop_self=True,
               override_capability=False,
               dont_capability_negotiate=False,
               remote_private_as=False,
               route_reflector_client=False,
               soft_reconfiguration=True,
               ttl_option='disabled',
               comment=None):
        """
        Create a new BGPPeering configuration.

        :param str name: name of peering
        :param str,BGPConnectionProfile connection_profile_ref: required BGP
            connection profile. System default used if not provided.
        :param str md5_password: optional md5_password
        :param str local_as_option: the local AS mode. Valid options are:
            'not_set', 'prepend', 'no_prepend', 'replace_as'
        :param str max_prefix_option: The max prefix mode. Valid options are:
            'not_enabled', 'enabled', 'warning_only'
        :param str send_community: the send community mode. Valid options are:
            'no', 'standard', 'extended', 'standard_and_extended'
        :param str connected_check: the connected check mode. Valid options are:
            'disabled', 'enabled', 'automatic'
        :param str orf_option: outbound route filtering mode. Valid options are:
            'disabled', 'send', 'receive', 'both'
        :param bool next_hop_self: next hop self setting
        :param bool override_capability: is override received capabilities
        :param bool dont_capability_negotiate: do not send capabilities
        :param bool remote_private_as: is remote a private AS
        :param bool route_reflector_client: Route Reflector Client (iBGP only)
        :param bool soft_reconfiguration: do soft reconfiguration inbound
        :param str ttl_option: ttl check mode. Valid options are: 'disabled', 
            'ttl-security'
        :raises CreateElementFailed: failed creating profile
        :return: instance with meta
        :rtype: BGPPeering
        """
        json = {
            'name': name,
            'local_as_option': local_as_option,
            'max_prefix_option': max_prefix_option,
            'send_community': send_community,
            'connected_check': connected_check,
            'orf_option': orf_option,
            'next_hop_self': next_hop_self,
            'override_capability': override_capability,
            'dont_capability_negotiate': dont_capability_negotiate,
            'soft_reconfiguration': soft_reconfiguration,
            'remove_private_as': remote_private_as,
            'route_reflector_client': route_reflector_client,
            'ttl_option': ttl_option,
            'comment': comment
        }

        if md5_password:
            json.update(md5_password=md5_password)

        connection_profile_ref = element_resolver(connection_profile_ref) or \
            BGPConnectionProfile('Default BGP Connection Profile').href

        json.update(connection_profile=connection_profile_ref)

        return ElementCreator(cls, json)
Example #19
0
class BGP(object):
    """
    BGP represents the BGP configuration on a given engine. An
    instance is returned from an engine reference::
    
        engine = Engine('myengine')
        engine.dynamic_routing.bgp.status
        engine.dynamic_routing.bgp.announced_networks
        ...
    
    When making changes to the BGP configuration, any methods
    called that change the configuration also require that
    engine.update() is called once changes are complete. This way
    you can make multiple changes without refreshing the engine cache.
    
    For example, adding advertised networks to the configuration::
    
        engine.dynamic_routing.bgp.update_configuration(announced_network=[Network('foo')])
        engine.update()
    
    :ivar AutonomousSystem autonomous_system: AS reference for this BGP configuration
    :ivar BGPProfile profile: BGP profile reference for this configuration
    """
    autonomous_system = ElementRef('bgp_as_ref')
    profile = ElementRef('bgp_profile_ref')

    def __init__(self, data=None):
        self.data = data if data else ElementCache()

    @property
    def router_id(self):
        """
        Get the router ID for this BGP configuration. If None, then
        the ID will use the interface IP.
        
        :return: str or None
        """
        return self.data.get('router_id')

    @property
    def status(self):
        """
        Is BGP enabled on this engine.
        
        :rtype: bool
        """
        return self.data.get('enabled')

    def disable(self):
        """
        Disable BGP on this engine.

        :return: None
        """
        self.data.update(enabled=False, announced_ne_setting=[])

    def enable(self,
               autonomous_system,
               announced_networks,
               router_id=None,
               bgp_profile=None):
        """
        Enable BGP on this engine. On master engine, enable BGP on the
        virtual firewall. When adding networks to `announced_networks`, the
        element types can be of type :class:`smc.elements.network.Host`,
        :class:`smc.elements.network.Network` or :class:`smc.elements.group.Group`.
        If passing a Group, it must have element types of host or network.
        
        Within announced_networks, you can pass a 2-tuple that provides an optional
        :class:`smc.routing.route_map.RouteMap` if additional policy is required
        for a given network.
        ::

            engine.dynamic_routing.bgp.enable(
                autonomous_system=AutonomousSystem('aws_as'),
                announced_networks=[Network('bgpnet'),Network('inside')],
                router_id='10.10.10.10')

        :param str,AutonomousSystem autonomous_system: provide the AS element
            or str href for the element
        :param str,BGPProfile bgp_profile: provide the BGPProfile element or
            str href for the element; if None, use system default
        :param list announced_networks: list of networks to advertise via BGP
            Announced networks can be single networks,host or group elements or
            a 2-tuple with the second tuple item being a routemap element
        :param str router_id: router id for BGP, should be an IP address. If not
            set, automatic discovery will use default bound interface as ID.
        :raises ElementNotFound: OSPF, AS or Networks not found
        :return: None
        
        .. note:: For arguments that take str or Element, the str value should be
            the href of the element.
        """
        autonomous_system = element_resolver(autonomous_system)

        bgp_profile = element_resolver(bgp_profile) or \
            BGPProfile('Default BGP Profile').href

        announced = self._unwrap(announced_networks)

        self.data.update(enabled=True,
                         bgp_as_ref=autonomous_system,
                         bgp_profile_ref=bgp_profile,
                         announced_ne_setting=announced,
                         router_id=router_id)

    def update_configuration(self, **kwargs):
        """
        Update configuration using valid kwargs as defined in
        the enable constructor.
        
        :param dict kwargs: kwargs to satisfy valid args from `enable`
        :rtype: bool
        """
        updated = False
        if 'announced_networks' in kwargs:
            kwargs.update(
                announced_ne_setting=kwargs.pop('announced_networks'))
        if 'bgp_profile' in kwargs:
            kwargs.update(bgp_profile_ref=kwargs.pop('bgp_profile'))
        if 'autonomous_system' in kwargs:
            kwargs.update(bgp_as_ref=kwargs.pop('autonomous_system'))

        announced_ne = kwargs.pop('announced_ne_setting', None)

        for name, value in kwargs.items():
            _value = element_resolver(value)
            if self.data.get(name) != _value:
                self.data[name] = _value
                updated = True

        if announced_ne is not None:
            s = self.data.get('announced_ne_setting')
            ne = self._unwrap(announced_ne)

            if len(announced_ne) != len(s) or not self._equal(ne, s):
                self.data.update(announced_ne_setting=ne)
                updated = True

        return updated

    def _equal(self, dict1, dict2):
        _s = {
            entry.get('announced_ne_ref'): entry.get('announced_rm_ref')
            for entry in dict1
        }
        _d = {
            entry.get('announced_ne_ref'): entry.get('announced_rm_ref')
            for entry in dict2
        }
        return len({k: _s[k]
                    for k in _s if k not in _d or _d.get(k) != _s[k]}) == 0

    def _unwrap(self, network):
        _announced = []
        for net in network:
            d = dict()
            if isinstance(net, tuple):
                _network, _routemap = net
                d.update(announced_ne_ref=_network.href)
                if _routemap:
                    d.update(announced_rm_ref=_routemap.href)
                _announced.append(d)
                continue
            d.update(announced_ne_ref=net.href)
            _announced.append(d)
        return _announced

    @property
    def announced_networks(self):
        """
        Show all announced networks for the BGP configuration.
        Returns tuple of advertised network, routemap. Route
        map may be None.
        ::
        
            for advertised in engine.bgp.advertisements:
                net, route_map = advertised
        
        :return: list of tuples (advertised_network, route_map).
        """
        return [(Element.from_href(ne.get('announced_ne_ref')),
                 Element.from_href(ne.get('announced_rm_ref')))
                for ne in self.data.get('announced_ne_setting')]
Example #20
0
class RouteVPN(Element):
    """
    Route based VPN in NGFW.
    
    :ivar VPNProfile vpn_profile: VPNProfile reference for this RouteVPN
    :ivar TunnelMonitoringGroup monitoring_group: tunnel monitoring group reference
    """
    typeof = 'rbvpn_tunnel'
    vpn_profile = ElementRef('vpn_profile_ref')
    monitoring_group = ElementRef('monitoring_group_ref')
    
    @classmethod
    def create_ipsec_tunnel(cls, name, local_endpoint, remote_endpoint,
                            preshared_key=None, monitoring_group=None,
                            vpn_profile=None, mtu=0, pmtu_discovery=True,
                            ttl=0, enabled=True, comment=None):
        """
        The VPN tunnel type negotiates IPsec tunnels in the same way
        as policy-based VPNs, but traffic is selected to be sent into
        the tunnel based on routing.
        
        :param str name: name of VPN
        :param TunnelEndpoint local_endpoint: the local side endpoint for
            this VPN.
        :param TunnelEndpoint remote_endpoint: the remote side endpoint for
            this VPN.
        :param str preshared_key: required if remote endpoint is an ExternalGateway
        :param TunnelMonitoringGroup monitoring_group: the group to place
            this VPN in for monitoring. Default: 'Uncategorized'.
        :param VPNProfile vpn_profile: VPN profile for this VPN.
            (default: VPN-A Suite)
        :param int mtu: Set MTU for this VPN tunnel (default: 0)
        :param boolean pmtu_discovery: enable pmtu discovery (default: True)
        :param int ttl: ttl for connections on the VPN (default: 0)
        :param bool enabled: enable the RBVPN or leave it disabled
        :param str comment: optional comment
        :raises CreateVPNFailed: failed to create the VPN with reason
        :rtype: RouteVPN
        """
        group = monitoring_group or TunnelMonitoringGroup('Uncategorized')
        profile = vpn_profile or VPNProfile('VPN-A Suite')
        
        json = {
            'name': name,
            'mtu': mtu,
            'ttl': ttl,
            'enabled': enabled,
            'monitoring_group_ref': group.href,
            'pmtu_discovery': pmtu_discovery,
            'preshared_key': preshared_key,
            'rbvpn_tunnel_side_a': local_endpoint.data,
            'rbvpn_tunnel_side_b': remote_endpoint.data,
            'tunnel_mode': 'vpn',
            'comment': comment,
            'vpn_profile_ref': profile.href
        }
        
        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreateVPNFailed(err)
        
    @classmethod
    def create_gre_tunnel_mode(cls, name, local_endpoint, remote_endpoint,
                          policy_vpn, mtu=0, pmtu_discovery=True, ttl=0,
                          enabled=True, comment=None):
        """
        Create a GRE based tunnel mode route VPN. Tunnel mode GRE wraps the
        GRE tunnel in an IPSEC tunnel to provide encrypted end-to-end
        security. Therefore a policy based VPN is required to 'wrap' the
        GRE into IPSEC. 
        
        :param str name: name of VPN
        :param TunnelEndpoint local_endpoint: the local side endpoint for
            this VPN.
        :param TunnelEndpoint remote_endpoint: the remote side endpoint for
            this VPN.
        :param PolicyVPN policy_vpn: reference to a policy VPN
        :param TunnelMonitoringGroup monitoring_group: the group to place
            this VPN in for monitoring. (default: 'Uncategorized')
        :param int mtu: Set MTU for this VPN tunnel (default: 0)
        :param boolean pmtu_discovery: enable pmtu discovery (default: True)
        :param int ttl: ttl for connections on the VPN (default: 0)
        :param str comment: optional comment
        :raises CreateVPNFailed: failed to create the VPN with reason
        :rtype: RouteVPN
        """
        json = {
            'name': name,
            'ttl': ttl,
            'mtu': mtu,
            'pmtu_discovery': pmtu_discovery,
            'tunnel_encryption': 'tunnel_mode',
            'tunnel_mode': 'gre',
            'enabled': enabled,
            'comment': comment,
            'rbvpn_tunnel_side_a': local_endpoint.data,
            'rbvpn_tunnel_side_b': remote_endpoint.data
        }
        if policy_vpn is None:
            json['tunnel_encryption'] = 'no_encryption'
        else:
            json['tunnel_mode_vpn_ref'] = policy_vpn.href
          
        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreateVPNFailed(err)
    
    @classmethod
    def create_gre_tunnel_no_encryption(cls, name, local_endpoint, remote_endpoint,
                          mtu=0, pmtu_discovery=True, ttl=0,
                          enabled=True, comment=None):
        """
        Create a GRE Tunnel with no encryption. See `create_gre_tunnel_mode` for
        constructor descriptions.
        """
        return cls.create_gre_tunnel_mode(
            name, local_endpoint, remote_endpoint, policy_vpn=None,
            mtu=mtu, pmtu_discovery=pmtu_discovery, ttl=ttl,
            enabled=enabled, comment=comment)
               
    @classmethod
    def create_gre_transport_mode(cls, name, local_endpoint, remote_endpoint,
                             preshared_key, monitoring_group=None,
                             vpn_profile=None, mtu=0, ttl=0,
                             pmtu_discovery=True, enabled=True, comment=None):
        """
        Create a transport based route VPN. This VPN type uses IPSEC
        for protecting the payload, therefore a VPN Profile is specified.
        
        :param str name: name of VPN
        :param TunnelEndpoint local_endpoint: the local side endpoint for
            this VPN.
        :param TunnelEndpoint remote_endpoint: the remote side endpoint for
            this VPN.
        :param str preshared_key: preshared key for RBVPN
        :param TunnelMonitoringGroup monitoring_group: the group to place
            this VPN in for monitoring. (default: 'Uncategorized')
        :param VPNProfile vpn_profile: VPN profile for this VPN.
            (default: VPN-A Suite)
        :param int mtu: Set MTU for this VPN tunnel (default: 0)
        :param boolean pmtu_discovery: enable pmtu discovery (default: True)
        :param int ttl: ttl for connections on the VPN (default: 0)
        :param str comment: optional comment
        :raises CreateVPNFailed: failed to create the VPN with reason
        :rtype: RouteVPN
        """
        group = monitoring_group or TunnelMonitoringGroup('Uncategorized')
        profile = vpn_profile or VPNProfile('VPN-A Suite')
        
        json = {
            'name': name,
            'mtu': mtu,
            'ttl': ttl,
            'preshared_key': preshared_key,
            'pmtu_discovery': pmtu_discovery,
            'monitoring_group_ref': group.href,
            'rbvpn_tunnel_side_a': local_endpoint.data,
            'rbvpn_tunnel_side_b': remote_endpoint.data,
            'tunnel_encryption': 'transport_mode',
            'vpn_profile_ref': profile.href,
            'tunnel_mode': 'gre',
            'enabled': enabled,
            'comment': comment
        }
        
        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreateVPNFailed(err)
    
    def enable(self):
        """
        Enable this route based VPN
        
        :return: None
        """
        if not self.enabled:
            self.update(enabled=True)
    
    def disable(self):
        """
        Disable this route based VPN
        
        :return: None
        """
        if self.enabled:
            self.update(enabled=False)
    
    def set_preshared_key(self, new_key):
        """
        Set the preshared key for this VPN. A pre-shared key is only
        present when the tunnel type is 'VPN' or the encryption mode
        is 'transport'.
        
        :return: None
        """
        if self.data.get('preshared_key'):
            self.update(preshared_key=new_key)
      
    @property
    def local_endpoint(self):
        """
        The local endpoint for this RBVPN
        
        :rtype: TunnelEndpoint
        """
        return TunnelEndpoint(**self.rbvpn_tunnel_side_a)
    
    @property
    def remote_endpoint(self):
        """
        The remote endpoint for this RBVPN
        
        :rtype: TunnelEndpoint
        """
        return TunnelEndpoint(**self.rbvpn_tunnel_side_b)
    
    @property
    def tunnel_mode(self):
        """
        The tunnel mode for this RBVPN
        
        :rtype: str
        """
        return self.data.get('tunnel_mode')
Example #21
0
class PolicyVPN(Element):
    """
    Create a new VPN Policy.
    ::
    
        >>> PolicyVPN.create(name='myvpn')
        PolicyVPN(name=myvpn)
        >>> v = PolicyVPN('myvpn')
        >>> print(v.vpn_profile)
        VPNProfile(name=VPN-A Suite)

    When making VPN Policy modifications, you must first call :func:`open`, 
    make your modifications and then call :func:`save` followed by 
    :func:`close`.
    
    :ivar VPNProfile vpn_profile: VPN Profile used by this Policy VPN
    
    """
    typeof = 'vpn'
    vpn_profile = ElementRef('vpn_profile')

    @classmethod
    def create(cls,
               name,
               nat=False,
               mobile_vpn_toplogy_mode=None,
               vpn_profile=None):
        """
        Create a new policy based VPN

        :param name: name of vpn policy
        :param bool nat: whether to apply NAT to the VPN (default False)
        :param mobile_vpn_toplogy_mode: whether to allow remote vpn
        :param VPNProfile vpn_profile: reference to VPN profile, or uses default
        :rtype: PolicyVPN
        """
        vpn_profile = element_resolver(vpn_profile) or \
            VPNProfile('VPN-A Suite').href

        json = {
            'mobile_vpn_topology_mode': mobile_vpn_toplogy_mode,
            'name': name,
            'nat': nat,
            'vpn_profile': vpn_profile
        }

        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreatePolicyFailed(err)

    @property
    def nat(self):
        """ 
        Is NAT enabled on this vpn policy

        :return: NAT enabled
        :rtype: bool
        """
        return self.data.get('nat')

    def enable_disable_nat(self):
        """
        Enable or disable NAT on this policy. If NAT is disabled, it
        will be enabled and vice versa.

        :return: None
        """
        if self.nat:
            self.data['nat'] = False
        else:
            self.data['nat'] = True

    @property
    def central_gateway_node(self):
        """
        Central Gateway Node acts as the hub of a hub-spoke VPN. 

        :rtype: SubElementCollection(GatewayNode)
        """
        return sub_collection(self.get_relation('central_gateway_node'),
                              type('CentralGatewayNode', (GatewayNode, ), {}))

    @property
    def satellite_gateway_node(self):
        """
        Node level settings for configured satellite gateways

        :rtype: SubElementCollection(GatewayNode)
        """
        return sub_collection(
            self.get_relation('satellite_gateway_node'),
            type('SatelliteGatewayNode', (GatewayNode, ), {}))

    @property
    def mobile_gateway_node(self):
        """
        Mobile Gateway's are represented by client endpoints connecting
        to the policy based VPN.

        :rtype: SubElementCollection(GatewayNode)
        """
        return sub_collection(self.get_relation('mobile_gateway_node'),
                              type('MobileGatewayNode', (GatewayNode, ), {}))

    @property
    def tunnels(self):
        """
        Return all tunnels for this VPN. A tunnel is defined as two end
        points within the VPN topology. Endpoints are automatically
        configureed based on whether they are a central gateway or 
        satellite gateway. This provides access to enabling/disabling
        and setting the preshared key for the linked endpoints.
        List all tunnel mappings for this policy vpn::
        
            for tunnel in policy.tunnels:    
                tunnela = tunnel.tunnel_side_a
                tunnelb = tunnel.tunnel_side_b
                print(tunnela.gateway)
                print(tunnelb.gateway)
    
        :rtype: SubElementCollection(GatewayTunnel)
        """
        return sub_collection(self.get_relation('gateway_tunnel'),
                              GatewayTunnel)

    def open(self):
        """
        Open the policy for editing. This is only a valid method for
        SMC version <= 6.1

        :raises PolicyCommandFailed: couldn't open policy with reason
        :return: None
        """
        self.make_request(PolicyCommandFailed,
                          method='create',
                          resource='open')

    def save(self):
        """
        Save the policy after editing. This is only a valid method for
        SMC version <= 6.1

        :raises PolicyCommandFailed: save failed with reason
        :return: None
        """
        self.make_request(PolicyCommandFailed,
                          method='create',
                          resource='save')

    def close(self):
        """
        Close the policy. This is only a valid method for
        SMC version <= 6.1

        :raises PolicyCommandFailed: close failed with reason
        :return: None
        """
        self.make_request(PolicyCommandFailed,
                          method='create',
                          resource='close')

    def validate(self):
        """
        Return a validation string from the SMC after running validate on
        this VPN policy.
        
        :return: status as string
        :rtype: str
        """
        return self.make_request(resource='validate').get('value')

    @property
    def mobile_vpn_topology(self):
        """
        Is the policy VPN configured for mobile VPN gateways.
        Valid modes: 'Selected Gateways below', 'Only central Gateways from overall topology',
        'All Gateways from overall topology', 'None'
        """
        return self.data.get('mobile_vpn_topology_mode')


#     @mobile_vpn_topology.setter
#     def mobile_vpn_topology(self, value):
#         self.data.update(mobile_vpn_topology_mode=value)

#     @mobile_vpn_topology.setter
#     def mobile_vpn_topology(self, value):
#         self.update(mobile_vpn_topology_mode=value)
#         if self.mobile_vpn_topology == 'None' and value.startswith('Selected'):
#             self.update(mobile_vpn_topology='Selected Gateways below')
#             self.make_request(
#                 PolicyCommandFailed,
#                 method='create',
#                 resource='mobile_gateway_node',
#                 json={'gateway': ClientGateway('VPN Client').href,
#                       'node_usage': 'mobile'})
#         elif self.mobile_vpn_topology != value:
#             self.update(mobile_vpn_topology_mode=value)

    def add_mobile_gateway(self, gateway):
        """
        Add a mobile VPN gateway to this policy VPN. 
        Example of adding or removing a mobile VPN gateway::
        
            policy_vpn = PolicyVPN('myvpn')
            policy_vpn.open()
            policy_vpn.add_mobile_vpn_gateway(ExternalGateway('extgw3'))
            
            for mobile_gateway in policy_vpn.mobile_gateway_node:
                if mobile_gateway.gateway == ExternalGateway('extgw3'):
                    mobile_gateway.delete()
            policy_vpn.save()
            policy_vpn.close()
        
        :param Engine,ExternalGateway gateway: An external gateway, engine or
            href for the mobile gateway
        :raises PolicyCommandFailed: could not add gateway
        """
        try:
            gateway = gateway.vpn.internal_gateway.href  # Engine
        except AttributeError:
            gateway = element_resolver(gateway)  # External Gateway

        self.make_request(PolicyCommandFailed,
                          method='create',
                          resource='mobile_gateway_node',
                          json={
                              'gateway': gateway,
                              'node_usage': 'mobile'
                          })

    def add_central_gateway(self, gateway):
        """ 
        Add SMC managed internal gateway to the Central Gateways of this VPN

        :param Engine,ExternalGateway gateway: An external gateway, engine or
            href for the central gateway
        :raises PolicyCommandFailed: could not add gateway
        :return: None
        """
        try:
            gateway = gateway.vpn.internal_gateway.href
        except AttributeError:
            gateway = element_resolver(gateway)
        self.make_request(PolicyCommandFailed,
                          method='create',
                          resource='central_gateway_node',
                          json={
                              'gateway': gateway,
                              'node_usage': 'central'
                          })

    def add_satellite_gateway(self, gateway):
        """
        Add gateway node as a satellite gateway for this VPN. You must first
        have the gateway object created. This is typically used when you either 
        want a hub-and-spoke topology or the test_external gateway is a non-SMC 
        managed device.

        :param Engine,ExternalGateway gateway: An external gateway, engine or
            href for the central gateway
        :raises PolicyCommandFailed: could not add gateway
        :return: None
        """
        try:
            gateway = gateway.vpn.internal_gateway.href
        except AttributeError:
            gateway = element_resolver(gateway)
        self.make_request(PolicyCommandFailed,
                          method='create',
                          resource='satellite_gateway_node',
                          json={
                              'gateway': gateway,
                              'node_usage': 'satellite'
                          })

    @staticmethod
    def add_internal_gateway_to_vpn(internal_gateway_href,
                                    vpn_policy,
                                    vpn_role='central'):
        """
        Add an internal gateway (managed engine node) to a VPN policy
        based on the internal gateway href.

        :param str internal_gateway_href: href for engine internal gw
        :param str vpn_policy: name of vpn policy
        :param str vpn_role: central|satellite
        :return: True for success
        :rtype: bool
        """
        try:
            vpn = PolicyVPN(vpn_policy)
            vpn.open()
            if vpn_role == 'central':
                vpn.add_central_gateway(internal_gateway_href)
            else:
                vpn.add_satellite_gateway(internal_gateway_href)
            vpn.save()
            vpn.close()
        except ElementNotFound:
            return False
        return True
Example #22
0
class Situation(Element):
    """
    Situation defines a common interface for inspection and correlated
    situations.
    """
    situation_context = ElementRef('situation_context_ref')

    @property
    def severity(self):
        """
        The severity of this inspection situation, critical, high,
        low, information
        
        :rtype: int
        """
        return SEVERITY.get(self.data.get('severity'))

    @property
    def description(self):
        """
        The description for this situation
        
        :rtype: str
        """
        return self.data.get('description', '')

    @property
    def attacker(self):
        """
        How the Attacker is determined when the Situation matches. This
        information is used for blacklisting and in log entries and may
        be None
        
        :rtype: str or None
        """
        return self.data.get('attacker')

    @property
    def target(self):
        """
        How the Target is determined when the Situation matches. This
        information is used for blacklisting and in log entries and may
        be None
        
        :rtype: str or None
        """
        return self.data.get('target')

    @property
    def parameter_values(self):
        """
        Parameter values for this inspection situation. This correlate to
        the the situation_context.
        
        :rtype: list(SituationParameterValue)
        """
        for param in self.data.get('parameter_values', []):
            cache = ElementCache(data=self.make_request(href=param))
            name = '{}'.format(cache.type.title()).replace('_', '')
            yield type(name, (SituationParameterValue, ),
                       {'data': cache})(name=cache.name,
                                        type=cache.type,
                                        href=param)
Example #23
0
class ExternalGateway(Element):
    """
    External Gateway defines an VPN Gateway for a non-SMC managed device.
    This will specify details such as the endpoint IP, and VPN site
    protected networks. Example of manually provisioning each step::

        Network.create(name='mynetwork', ipv4_network='172.18.1.0/24')
        gw = ExternalGateway.create(name='mygw')
        gw.external_endpoint.create(name='myendpoint', address='10.10.10.10')
        gw.vpn_site.create(name='mysite', site_element=[Network('mynetwork')])

    :ivar GatewayProfile gateway_profile: A gateway profile will define the
        capabilities (i.e. crypto) allowed for this VPN.
    """

    typeof = "external_gateway"
    gateway_profile = ElementRef("gateway_profile")

    @classmethod
    def create(cls, name, trust_all_cas=True, gateway_profile=None, **kwargs):
        """
        Create new External Gateway

        :param str name: name of test_external gateway
        :param bool trust_all_cas: whether to trust all internal CA's
               (default: True)
        :param GatewayProfile,href gateway_profile: optional gateway profile, otherwise
            default
        :return: instance with meta
        :rtype: ExternalGateway
        """
        json = {
            "name":
            name,
            "trust_all_cas":
            trust_all_cas,
            "gateway_profile":
            element_resolver(gateway_profile)
            if gateway_profile else element_default(GatewayProfile, "Default"),
        }
        json.update(kwargs)

        return ElementCreator(cls, json)

    @classmethod
    def update_or_create(cls,
                         name,
                         external_endpoint=None,
                         vpn_site=None,
                         trust_all_cas=True,
                         with_status=False):
        """
        Update or create an ExternalGateway. The ``external_endpoint`` and
        ``vpn_site`` parameters are expected to be a list of dicts with key/value
        pairs to satisfy the respective elements create constructor. VPN Sites will
        represent the final state of the VPN site list. ExternalEndpoint that are
        pre-existing will not be deleted if not provided in the ``external_endpoint``
        parameter, however existing elements will be updated as specified.

        :param str name: name of external gateway
        :param list(dict) external_endpoint: list of dict items with key/value
            to satisfy ExternalEndpoint.create constructor
        :param list(dict) vpn_site: list of dict items with key/value to satisfy
            VPNSite.create constructor
        :param bool with_status: If set to True, returns a 3-tuple of
            (ExternalGateway, modified, created), where modified and created
            is the boolean status for operations performed.
        :raises ValueError: missing required argument/s for constructor argument
        :rtype: ExternalGateway
        """
        if external_endpoint:
            for endpoint in external_endpoint:
                if "name" not in endpoint:
                    raise ValueError("External endpoints are configured "
                                     "but missing the name parameter.")

        if vpn_site:
            for site in vpn_site:
                if "name" not in site:
                    raise ValueError("VPN sites are configured but missing "
                                     "the name parameter.")
                # Make sure VPN sites are resolvable before continuing
                sites = [
                    element_resolver(element, do_raise=True)
                    for element in site.get("site_element", [])
                ]
                site.update(site_element=sites)

        updated = False
        created = False
        try:
            extgw = ExternalGateway.get(name)
        except ElementNotFound:
            extgw = ExternalGateway.create(name, trust_all_cas)
            created = True

        if external_endpoint:
            for endpoint in external_endpoint:
                _, modified, was_created = ExternalEndpoint.update_or_create(
                    extgw, with_status=True, **endpoint)
                if was_created or modified:
                    updated = True

        if vpn_site:
            for site in vpn_site:
                _, modified, was_created = VPNSite.update_or_create(
                    extgw,
                    name=site["name"],
                    site_element=site.get("site_element"),
                    with_status=True,
                )
                if was_created or modified:
                    updated = True

        if with_status:
            return extgw, updated, created
        return extgw

    @property
    def vpn_site(self):
        """
        A VPN site defines a collection of IP's or networks that
        identify address space that is defined on the other end of
        the VPN tunnel.

        :rtype: CreateCollection(VPNSite)
        """
        return create_collection(self.get_relation("vpn_site"), VPNSite)

    @property
    def external_endpoint(self):
        """
        An External Endpoint is the IP based definition for the destination
        VPN peers. There may be multiple per External Gateway.  Add a new
        endpoint to an existing test_external gateway::

            >>> list(ExternalGateway.objects.all())
            [ExternalGateway(name=cisco-remote-side), ExternalGateway(name=remoteside)]
            >>> gateway.external_endpoint.create('someendpoint', '12.12.12.12')
            'http://1.1.1.1:8082/6.1/elements/external_gateway/22961/external_endpoint/27467'

        :rtype: CreateCollection(ExternalEndpoint)
        """
        return create_collection(self.get_relation("external_endpoint"),
                                 ExternalEndpoint)

    @property
    def trust_all_cas(self):
        """
        Gateway setting identifying whether all CA's specified in the
        profile are supported or only specific ones.

        :rtype: bool
        """
        return self.data.get("trust_all_cas")
Example #24
0
class StaticNetlink(Element):
    """
    A Static Netlink is applied to an interface to provide an alternate
    route to a destination. It is typically used when you have fixed IP
    interfaces versus using DHCP (use a Dynamic NetLink).
    
    :ivar Router,Engine gateway: gateway for this netlink. Should be
        the 'next hop' element associated with the netlink
    :ivar list(Network) network: list of networks associated with this
        netlink
    :ivar int input_speed: input speed in Kbps, used for ratio-based
            load-balancing
    :ivar int output_speed: output speed in Kbps,  used for ratio-based
        load-balancing
    :ivar list probe_address: list of IP addresses to use as probing
        addresses to validate connectivity
    :ivar int standby_mode_period: Specifies the probe period when
        standby mode is used (in seconds)
    :ivar int standby_mode_timeout: probe timeout in seconds
    :ivar int active_mode_period: Specifies the probe period when active
        mode is used (in seconds)
    :ivar int active_mode_timeout: probe timeout in seconds
    """
    typeof = 'netlink'
    gateway = ElementRef('gateway_ref')
    network = ElementList(('6.5', 'ref'),
                          ('6.6', 'network_ref'))

    @classmethod
    def create(cls, name, gateway, network, input_speed=None,
               output_speed=None, domain_server_address=None,
               provider_name=None, probe_address=None,
               standby_mode_period=3600, standby_mode_timeout=30,
               active_mode_period=5, active_mode_timeout=1, comment=None):
        """
        Create a new StaticNetlink to be used as a traffic handler.

        :param str name: name of netlink Element
        :param gateway_ref: gateway to map this netlink to. This can be an element
            or str href.
        :type gateway_ref: Router,Engine
        :param list ref: network/s associated with this netlink.
        :type ref: list(str,Element)
        :param int input_speed: input speed in Kbps, used for ratio-based
            load-balancing
        :param int output_speed: output speed in Kbps,  used for ratio-based
            load-balancing
        :param list domain_server_address: dns addresses for netlink. Engine
            DNS can override this field
        :type dns_addresses: list(str,Element)
        :param str provider_name: optional name to identify provider for this
            netlink
        :param list probe_address: list of IP addresses to use as probing
            addresses to validate connectivity
        :type probe_ip_address: list(str)
        :param int standby_mode_period: Specifies the probe period when
            standby mode is used (in seconds)
        :param int standby_mode_timeout: probe timeout in seconds
        :param int active_mode_period: Specifies the probe period when active
            mode is used (in seconds)
        :param int active_mode_timeout: probe timeout in seconds
        :raises ElementNotFound: if using type Element parameters that are
            not found.
        :raises CreateElementFailed: failure to create netlink with reason
        :rtype: StaticNetlink

        .. note:: To monitor the status of the network links, you must define
                  at least one probe IP address.
        """
        json = {'name': name,
                'gateway_ref': element_resolver(gateway),
                'input_speed': input_speed,
                'output_speed': output_speed,
                'probe_address': probe_address,
                'nsp_name': provider_name,
                'comment': comment,
                'standby_mode_period': standby_mode_period,
                'standby_mode_timeout': standby_mode_timeout,
                'active_mode_period': active_mode_period,
                'active_mode_timeout': active_mode_timeout}

        if is_version_less_than_or_equal('6.5'):
            json.update(ref=element_resolver(network))
        else:
            json.update(network_ref=element_resolver(network))

        if domain_server_address:
            r = RankedDNSAddress([])
            r.add(domain_server_address)
            json.update(domain_server_address=r.entries)
        
        return ElementCreator(cls, json)
    
    @classmethod
    def update_or_create(cls, with_status=False, **kwargs):
        """
        Update or create static netlink. DNS entry differences are not
        resolved, instead any entries provided will be the final state
        for this netlink. If the intent is to add/remove DNS entries
        you can use the :meth:`~domain_server_address` method to add
        or remove.
        
        :raises CreateElementFailed: failed creating element
        :return: element instance by type or 3-tuple if with_status set
        """
        dns_address = kwargs.pop('domain_server_address', [])
        element, updated, created = super(StaticNetlink, cls).update_or_create(
            with_status=True, defer_update=True, **kwargs)
        if not created:
            if dns_address:
                new_entries = RankedDNSAddress([])
                new_entries.add(dns_address)
                element.data.update(domain_server_address=new_entries.entries)
                updated = True
        if updated:
            element.update()
        if with_status:
            return element, updated, created
        return element

    @property
    def domain_server_address(self):
        """
        Configured DNS servers for this netlink

        :return: list of DNS servers; if elements are specifed, they will
            be returned as type Element
        :rtype: RankedDNSAddress
        """
        return RankedDNSAddress(self.data.get('domain_server_address'))
    
    @property
    def networks(self):
        return self.network
Example #25
0
class ProxyServer(ContactAddressMixin, Element):
    """
    A ProxyServer element is used in the firewall policy to provide the ability to
    send HTTP, HTTPS, FTP or SMTP traffic to a next hop proxy.
    There are two types of next hop proxies, 'Generic' and 'Forcepoint AP Web".
    
    Example of creating a configuration for a Forcepoint AP-Web proxy redirect::
    
        server = ProxyServer.update_or_create(name='myproxy',
            address='1.1.1.1', proxy_service='forcepoint_ap-web_cloud',
            fp_proxy_key='mypassword', fp_proxy_key_id=3, fp_proxy_user_id=1234,
            inspected_service=[{'service_type': 'HTTP', 'port': '80'}])
    
    Create a Generic Proxy forward service::
    
        server = ProxyServer.update_or_create(name='generic', address='1.1.1.1,1.1.1.2',
            inspected_service=[{'service_type': 'HTTP', 'port': 80}, {'service_type': 'HTTPS', 'port': 8080}])
    
    Inspected services take a list of keys `service_type` and `port`. Service type key values
    are 'HTTP', 'HTTPS', 'FTP' and 'SMTP'. Port value is the port for the respective protocol.
    
    :param str http_proxy: type of proxy configuration, either generic or forcepoint_ap-web_cloud
    """
    typeof = 'proxy_server'
    location = ElementRef('location_ref')

    @classmethod
    def create(cls,
               name,
               address,
               inspected_service,
               secondary=None,
               balancing_mode='ha',
               proxy_service='generic',
               location=None,
               comment=None,
               add_x_forwarded_for=False,
               trust_host_header=False,
               **kw):
        """
        Create a Proxy Server element
        
        :param str name: name of proxy server element
        :param str address: address of element. Can be a single FQDN or comma separated
            list of IP addresses
        :param list secondary: list of secondary IP addresses
        :param str balancing_mode: how to balance traffic, valid options are
            ha (first available server), src, dst, srcdst (default: ha)
        :param str proxy_service: which proxy service to use for next hop, options
            are generic or forcepoint_ap-web_cloud
        :param str,Element location: location for this proxy server
        :param bool add_x_forwarded_for: add X-Forwarded-For header when using the
            Generic Proxy forwarding method (default: False)
        :param bool trust_host_header: trust the host header when using the Generic
            Proxy forwarding method (default: False)
        :param dict inspected_service: inspection services dict. Valid keys are
            service_type and port. Service type valid values are HTTP, HTTPS, FTP or SMTP
            and are case sensitive
        :param str comment: optional comment
        :param kw: keyword arguments are used to collect settings when the proxy_service
            value is forcepoint_ap-web_cloud. Valid keys are `fp_proxy_key`, 
            `fp_proxy_key_id`, `fp_proxy_user_id`. The fp_proxy_key is the password value.
            All other values are of type int
        """
        json = {
            'name': name,
            'comment': comment,
            'secondary': secondary or [],
            'http_proxy': proxy_service,
            'balancing_mode': balancing_mode,
            'inspected_service': inspected_service,
            'trust_host_header': trust_host_header,
            'add_x_forwarded_for': add_x_forwarded_for,
            'location_ref': element_resolver(location)
        }
        addresses = address.split(',')
        json.update(address=addresses.pop(0))
        json.update(ip_address=addresses if 'ip_address' not in
                    kw else kw['ip_address'])

        if proxy_service == 'forcepoint_ap-web_cloud':
            for key in ('fp_proxy_key', 'fp_proxy_key_id', 'fp_proxy_user_id'):
                if key not in kw:
                    raise CreateElementFailed(
                        'Missing required fp key when adding a '
                        'proxy server to forward to forcepoint. Missing key: %s'
                        % key)
                json[key] = kw.get(key)

        return ElementCreator(cls, json)

    @property
    def proxy_service(self):
        """
        The proxy service for this proxy server configuration
        
        :rtype: str
        """
        return self.data.get('http_proxy')

    @classmethod
    def update_or_create(cls, with_status=False, **kwargs):
        element, updated, created = super(ProxyServer, cls).update_or_create(
            defer_update=True, **kwargs)

        if not created:
            if 'proxy_service' in element.data and element.http_proxy != element.data[
                    'proxy_service']:
                element.data['http_proxy'] = element.data.pop('proxy_service')
                updated = True
            if 'address' in kwargs:
                if ',' in element.data.get('address'):
                    addresses = element.data.pop('address').split(',')
                    element.data['address'] = addresses.pop(0)
                    # Remainder is ip_address attribute
                    if set(addresses) ^ set(element.data.get('ip_address',
                                                             [])):
                        element.data['ip_address'] = addresses
                    updated = True

            inspected_service = kwargs.pop('inspected_service', None)
            if inspected_service is not None:
                service_keys = set(
                    [k.get('service_type') for k in inspected_service])
                element_keys = set([
                    k.get('service_type')
                    for k in element.data.get('inspected_service', [])
                ])
                if service_keys ^ element_keys:
                    element.data['inspected_service'] = inspected_service
                    updated = True
            if updated:
                element.update()

        if with_status:
            return element, updated, created
        return element

    @property
    def inspected_services(self):
        """
        The specified services for inspection. An inspected service is a
        reference to a protocol that can be forwarded for inspection, such
        as HTTP, HTTPS, FTP and SMTP.
        
        :rtype: list(InspectedService)
        """
        return [
            InspectedService(**service)
            for service in self.make_request(resource='inspected_services')
        ]
Example #26
0
class RouteVPN(Element):
    """
    Route based VPN in NGFW.

    :ivar VPNProfile vpn_profile: VPNProfile reference for this RouteVPN
    :ivar TunnelMonitoringGroup monitoring_group: tunnel monitoring group reference
    """

    typeof = "rbvpn_tunnel"
    vpn_profile = ElementRef("vpn_profile_ref")
    monitoring_group = ElementRef(("6.5", "monitoring_group_ref"),
                                  ("6.6", "tunnel_group_ref"))

    @classmethod
    def create_ipsec_tunnel(cls, *args, **kwargs):
        """
        The VPN tunnel type negotiates IPsec tunnels in the same way
        as policy-based VPNs, but traffic is selected to be sent into
        the tunnel based on routing.

        :param str name: name of VPN
        :param TunnelEndpoint local_endpoint: the local side endpoint for
            this VPN.
        :param TunnelEndpoint remote_endpoint: the remote side endpoint for
            this VPN.
        :param str preshared_key: required if remote endpoint is an ExternalGateway
        :param TunnelMonitoringGroup monitoring_group: the group to place
            this VPN in for monitoring. Default: 'Uncategorized'.
        :param VPNProfile vpn_profile: VPN profile for this VPN.
            (default: VPN-A Suite)
        :param int mtu: Set MTU for this VPN tunnel (default: 0)
        :param boolean pmtu_discovery: enable pmtu discovery (default: True)
        :param int ttl: ttl for connections on the VPN (default: 0)
        :param bool enabled: enable the RBVPN or leave it disabled
        :param str comment: optional comment
        :raises CreateVPNFailed: failed to create the VPN with reason
        :rtype: RouteVPN
        """
        versioned_method = get_best_version(
            ("6.5", cls._create_ipsec_tunnel_65),
            ("6.6", cls._create_ipsec_tunnel_66))
        return versioned_method(*args, **kwargs)

    @classmethod
    def _create_ipsec_tunnel_65(
        cls,
        name,
        local_endpoint,
        remote_endpoint,
        preshared_key=None,
        monitoring_group=None,
        vpn_profile=None,
        mtu=0,
        pmtu_discovery=True,
        ttl=0,
        enabled=True,
        comment=None,
    ):
        group = monitoring_group or RouteVPNTunnelMonitoringGroup(
            "Uncategorized")
        profile = vpn_profile or VPNProfile("VPN-A Suite")

        json = {
            "name": name,
            "mtu": mtu,
            "ttl": ttl,
            "enabled": enabled,
            "monitoring_group_ref": group.href,
            "pmtu_discovery": pmtu_discovery,
            "preshared_key": preshared_key,
            "rbvpn_tunnel_side_a": local_endpoint.data,
            "rbvpn_tunnel_side_b": remote_endpoint.data,
            "tunnel_mode": "vpn",
            "comment": comment,
            "vpn_profile_ref": profile.href,
        }

        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreateVPNFailed(err)

    @classmethod
    def _create_ipsec_tunnel_66(
        cls,
        name,
        local_endpoint,
        remote_endpoint,
        preshared_key=None,
        monitoring_group=None,
        vpn_profile=None,
        mtu=0,
        pmtu_discovery=True,
        ttl=0,
        enabled=True,
        comment=None,
    ):
        group = monitoring_group or TunnelMonitoringGroup("Uncategorized")
        profile = vpn_profile or VPNProfile("VPN-A Suite")

        json = {
            "name": name,
            "mtu": mtu,
            "ttl": ttl,
            "enabled": enabled,
            "tunnel_group_ref": group.href,
            "pmtu_discovery": pmtu_discovery,
            "preshared_key": preshared_key,
            "rbvpn_tunnel_side_a": local_endpoint.data,
            "rbvpn_tunnel_side_b": remote_endpoint.data,
            "tunnel_mode": "vpn",
            "comment": comment,
            "vpn_profile_ref": profile.href,
        }

        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreateVPNFailed(err)

    @classmethod
    def create_gre_tunnel_mode(
        cls,
        name,
        local_endpoint,
        remote_endpoint,
        policy_vpn,
        mtu=0,
        pmtu_discovery=True,
        ttl=0,
        enabled=True,
        comment=None,
    ):
        """
        Create a GRE based tunnel mode route VPN. Tunnel mode GRE wraps the
        GRE tunnel in an IPSEC tunnel to provide encrypted end-to-end
        security. Therefore a policy based VPN is required to 'wrap' the
        GRE into IPSEC.

        :param str name: name of VPN
        :param TunnelEndpoint local_endpoint: the local side endpoint for
            this VPN.
        :param TunnelEndpoint remote_endpoint: the remote side endpoint for
            this VPN.
        :param PolicyVPN policy_vpn: reference to a policy VPN
        :param TunnelMonitoringGroup monitoring_group: the group to place
            this VPN in for monitoring. (default: 'Uncategorized')
        :param int mtu: Set MTU for this VPN tunnel (default: 0)
        :param boolean pmtu_discovery: enable pmtu discovery (default: True)
        :param int ttl: ttl for connections on the VPN (default: 0)
        :param str comment: optional comment
        :raises CreateVPNFailed: failed to create the VPN with reason
        :rtype: RouteVPN
        """
        json = {
            "name": name,
            "ttl": ttl,
            "mtu": mtu,
            "pmtu_discovery": pmtu_discovery,
            "tunnel_encryption": "tunnel_mode",
            "tunnel_mode": "gre",
            "enabled": enabled,
            "comment": comment,
            "rbvpn_tunnel_side_a": local_endpoint.data,
            "rbvpn_tunnel_side_b": remote_endpoint.data,
        }
        if policy_vpn is None:
            json["tunnel_encryption"] = "no_encryption"
        else:
            json["tunnel_mode_vpn_ref"] = policy_vpn.href

        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreateVPNFailed(err)

    @classmethod
    def create_gre_tunnel_no_encryption(
        cls,
        name,
        local_endpoint,
        remote_endpoint,
        mtu=0,
        pmtu_discovery=True,
        ttl=0,
        enabled=True,
        comment=None,
    ):
        """
        Create a GRE Tunnel with no encryption. See `create_gre_tunnel_mode` for
        constructor descriptions.
        """
        return cls.create_gre_tunnel_mode(
            name,
            local_endpoint,
            remote_endpoint,
            policy_vpn=None,
            mtu=mtu,
            pmtu_discovery=pmtu_discovery,
            ttl=ttl,
            enabled=enabled,
            comment=comment,
        )

    @classmethod
    def create_gre_transport_mode(cls, *args, **kwargs):
        """
        Create a transport based route VPN. This VPN type uses IPSEC
        for protecting the payload, therefore a VPN Profile is specified.

        :param str name: name of VPN
        :param TunnelEndpoint local_endpoint: the local side endpoint for
            this VPN.
        :param TunnelEndpoint remote_endpoint: the remote side endpoint for
            this VPN.
        :param str preshared_key: preshared key for RBVPN
        :param TunnelMonitoringGroup monitoring_group: the group to place
            this VPN in for monitoring. (default: 'Uncategorized')
        :param VPNProfile vpn_profile: VPN profile for this VPN.
            (default: VPN-A Suite)
        :param int mtu: Set MTU for this VPN tunnel (default: 0)
        :param boolean pmtu_discovery: enable pmtu discovery (default: True)
        :param int ttl: ttl for connections on the VPN (default: 0)
        :param str comment: optional comment
        :raises CreateVPNFailed: failed to create the VPN with reason
        :rtype: RouteVPN
        """
        versioned_method = get_best_version(
            ("6.5", cls._create_gre_transport_mode_65),
            ("6.6", cls._create_gre_transport_mode_66))
        return versioned_method(*args, **kwargs)

    @classmethod
    def _create_gre_transport_mode_65(
        cls,
        name,
        local_endpoint,
        remote_endpoint,
        preshared_key,
        monitoring_group=None,
        vpn_profile=None,
        mtu=0,
        ttl=0,
        pmtu_discovery=True,
        enabled=True,
        comment=None,
    ):
        group = monitoring_group or RouteVPNTunnelMonitoringGroup(
            "Uncategorized")
        profile = vpn_profile or VPNProfile("VPN-A Suite")

        json = {
            "name": name,
            "mtu": mtu,
            "ttl": ttl,
            "preshared_key": preshared_key,
            "pmtu_discovery": pmtu_discovery,
            "monitoring_group_ref": group.href,
            "rbvpn_tunnel_side_a": local_endpoint.data,
            "rbvpn_tunnel_side_b": remote_endpoint.data,
            "tunnel_encryption": "transport_mode",
            "vpn_profile_ref": profile.href,
            "tunnel_mode": "gre",
            "enabled": enabled,
            "comment": comment,
        }

        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreateVPNFailed(err)

    @classmethod
    def _create_gre_transport_mode_66(
        cls,
        name,
        local_endpoint,
        remote_endpoint,
        preshared_key,
        monitoring_group=None,
        vpn_profile=None,
        mtu=0,
        ttl=0,
        pmtu_discovery=True,
        enabled=True,
        comment=None,
    ):
        group = monitoring_group or TunnelMonitoringGroup("Uncategorized")
        profile = vpn_profile or VPNProfile("VPN-A Suite")

        json = {
            "name": name,
            "mtu": mtu,
            "ttl": ttl,
            "preshared_key": preshared_key,
            "pmtu_discovery": pmtu_discovery,
            "tunnel_group_ref": group.href,
            "rbvpn_tunnel_side_a": local_endpoint.data,
            "rbvpn_tunnel_side_b": remote_endpoint.data,
            "tunnel_encryption": "transport_mode",
            "vpn_profile_ref": profile.href,
            "tunnel_mode": "gre",
            "enabled": enabled,
            "comment": comment,
        }

        try:
            return ElementCreator(cls, json)
        except CreateElementFailed as err:
            raise CreateVPNFailed(err)

    def enable(self):
        """
        Enable this route based VPN

        :return: None
        """
        if not self.enabled:
            self.update(enabled=True)

    def disable(self):
        """
        Disable this route based VPN

        :return: None
        """
        if self.enabled:
            self.update(enabled=False)

    def set_preshared_key(self, new_key):
        """
        Set the preshared key for this VPN. A pre-shared key is only
        present when the tunnel type is 'VPN' or the encryption mode
        is 'transport'.

        :return: None
        """
        if self.data.get("preshared_key"):
            self.update(preshared_key=new_key)

    @property
    def local_endpoint(self):
        """
        The local endpoint for this RBVPN

        :rtype: TunnelEndpoint
        """
        return TunnelEndpoint(**self.rbvpn_tunnel_side_a)

    @property
    def remote_endpoint(self):
        """
        The remote endpoint for this RBVPN

        :rtype: TunnelEndpoint
        """
        return TunnelEndpoint(**self.rbvpn_tunnel_side_b)

    @property
    def tunnel_mode(self):
        """
        The tunnel mode for this RBVPN

        :rtype: str
        """
        return self.data.get("tunnel_mode")

    @property
    def tunnels(self):
        """
        Return all tunnels for this RBVPN in case of Tunnel Type 'VPN'.
        This provides access to enabling/disabling for the linked endpoints.
        List all tunnel mappings for this route vpn::

            for tunnel in rb_vpn.tunnels:
                print(tunnel.enabled)

        :rtype: SubElementCollection(EndpointTunnel)
        """
        return sub_collection(self.get_relation("gateway_endpoint_tunnel"),
                              EndpointTunnel)