예제 #1
0
    def exec_module(self, **kwargs):
        state = kwargs.pop('state', 'present')
        for name, value in kwargs.items():
            setattr(self, name, value)
        
        changed = False
        engine = self.fetch_element(FirewallCluster)
        
        if state == 'present':
            # Resolve dependencies
            if not engine:
                if not self.interfaces:
                    self.fail(msg='You must provide at least one interface '
                        'configuration to create a cluster.')
                
                if not self.primary_mgt:
                    self.fail(msg='You must provide a primary_mgt interface to create '
                        'an engine.')
                
                if not self.cluster_mode:
                    self.fail(msg='You must define a cluster mode to create an engine')
                
                itf = self.check_interfaces()
                
                # Management interface
                if not self.primary_mgt in itf:
                    self.fail(msg='Management interface is not defined. Management was '
                        'specified on interface: %s' % self.primary_mgt)
            
            if engine and self.interfaces and not self.skip_interfaces:
                itf = self.check_interfaces()
            
            cache = Cache()
            
            # SNMP settings
            if self.snmp and self.snmp.get('enabled', True):
                cache._add_entry('snmp_agent', self.snmp.get('snmp_agent', None))
                if cache.missing:
                    self.fail(msg='SNMP configured but the SNMP Agent specified is not '
                        'found: %s' % cache.missing)

            # Only validate BGP if it's specifically set to enabled    
            if self.bgp and self.bgp.get('enabled', True):
                # BGP Profile if specified
                if self.bgp.get('bgp_profile', None):
                    cache._add_entry('bgp_profile', self.bgp['bgp_profile'])
                    if cache.missing:
                        self.fail(msg='A BGP Profile was specified that does not exist: '
                            '%s' % self.bgp['bgp_profile'])
                
                # Get external bgp peers, can be type 'engine' or 'external_bgp_peer'
                # Can also be empty if you don't want to attach a peer.
                peerings = self.bgp.get('bgp_peering', [])
                for peer in peerings:
                    if 'name' not in peer:
                        self.fail(msg='BGP Peering requires a name field to identify the '
                            'BGP Peering element. Peer info provided: %s' % peer)
                    if 'external_bgp_peer' not in peer and 'engine' not in peer:
                        self.fail(msg='Missing the external_bgp_peer or engine parameter '
                            'which defines the next hop for the BGP Peering')
                    if 'external_bgp_peer' in peer:
                        cache._add_entry('external_bgp_peer', peer['external_bgp_peer'])
                    elif 'engine' in peer:
                        cache._add_entry('fw_cluster', peer['engine'])
        
                if cache.missing:
                    self.fail(msg='Missing external BGP Peering elements: %s' % cache.missing)
                
                as_system = self.bgp.get('autonomous_system', {})
                if not engine:
                    # We are trying to enable BGP on a new engine, Autonomous System
                    # is required
                    if not as_system:
                        self.fail(msg='You must specify an Autonomous System when enabling '
                            'BGP on a newly created engine.')
                if as_system:
                    if 'name' not in as_system or 'as_number' not in as_system:
                        self.fail(msg='Autonomous System requires a name and and '
                            'as_number value.')

                spoofing = self.bgp.get('antispoofing_network', {})
                self.validate_antispoofing_network(spoofing)
                cache.add(spoofing)
                if cache.missing:
                    self.fail(msg='Missing elements in antispoofing configuration: %s' %
                        cache.missing)
                    
                networks = self.bgp.get('announced_network', [])
                announced_networks = self.validate_and_extract_announced(networks)
                cache.add(announced_networks)
                if cache.missing:
                    self.fail(msg='Missing elements in announced configuration: %s' % cache.missing)
                
            self.cache = cache
        
        try:
            
            if state == 'present':
                if not engine:

                    interfaces = [vars(intf) for intf in itf]
                    
                    cluster = {'interfaces': interfaces}
                    cluster.update(
                        name=self.name,
                        cluster_mode=self.cluster_mode,
                        primary_mgt=self.primary_mgt,
                        backup_mgt=self.backup_mgt,
                        primary_heartbeat=self.primary_heartbeat,
                        log_server_ref=self.log_server,
                        domain_server_address=self.domain_server_address,
                        default_nat=self.default_nat,
                        enable_antivirus=self.antivirus,
                        enable_gti=self.file_reputation,
                        location_ref=self.location,
                        snmp=self.snmp,
                        comment=self.comment)
                    
                    if self.check_mode:
                        return self.results
                    
                    engine = FirewallCluster.create_bulk(**cluster)
                    changed = True
                    
                else: # Engine exists, check for modifications
                    
                    # Changes made up to check mode are done on the
                    # cached instance of the engine and not sent to SMC
                    if self.update_general(engine):
                        changed = True
                    
                    if self.update_snmp(engine):
                        changed = True
                    
                    if self.cluster_mode and engine.cluster_mode != self.cluster_mode:
                        engine.data.update(cluster_mode=self.cluster_mode)
                        changed = True

                    if self.check_mode:
                        return self.results
                    
                    # Check engine location value
                    if self.update_location(engine):
                        changed = True
                    
                    # First actual engine update happens here
                    if changed:
                        engine.update()
                    
                    # Reset management interfaces before operating on interfaces
                    # in case interfaces are removed that might have previously
                    # been used as interface options (primary mgt, etc)
                    if self.reset_management(engine):
                        changed = True

                    # Set skip interfaces to bypass interface checks
                    if not self.skip_interfaces:
                        self.update_interfaces(engine)
                    
                    # Lastly, delete top level interfaces that are not defined in 
                    # the YAML or added while looping. Only delete if skip_interfaces
                    # was not provided and that delete_undefined_interfaces is set to True
                    if not self.skip_interfaces and self.delete_undefined_interfaces:
                        self.check_for_deletes(engine)
                
                ######                
                # Check for BGP configuration on either newly created engine
                # or on the existing
                ######
                if self.bgp:
                    bgp = engine.bgp
                    enabled = self.bgp.get('enabled', True)
                    if not enabled and bgp.status:
                        bgp.disable()
                        changed = True
                    
                    elif enabled:
                        
                        if self.update_bgp(bgp):

                            autonomous_system, created = get_or_create_asystem(
                                self.bgp.get('autonomous_system'))
                            
                            if created:
                                changed = True
                            
                            bgp.disable() # Reset BGP configuration
                            bgp.enable(
                                autonomous_system,
                                announced_networks=[],
                                antispoofing_networks=self.antispoofing_format(),
                                router_id=self.bgp.get('router_id', ''),
                                bgp_profile=self.cache.get('bgp_profile',
                                    self.bgp.get('bgp_profile', None)))
                            
                            for network in self.announced_network_format():
                                bgp.advertise_network(**network)
                            changed = True
                    
                    if changed:
                        engine.update()
                
                    if enabled:
                        # BGP Peering is last since the BGP configuration may be placed
                        # on interfaces that might have been modified or added. It is
                        # possible that this could fail 
                        peerings = self.bgp.get('bgp_peering', None)
                        if peerings:
                            for peer in peerings:
                                peering, created = get_or_create_bgp_peering(
                                    peer.pop('name'))
                                if created:
                                    changed = True
                                # Update the peering on the interface
                                if self.update_bgp_peering(engine, peering, peer):
                                    changed = True
                    
                if self.tags:
                    if self.add_tags(engine, self.tags):
                        changed = True
                else:
                    if self.clear_tags(engine):
                        changed = True

                self.results['engine'] = engine.data.data
                
            elif state == 'absent':
                if engine:
                    engine.delete()
                    changed = True
        
        except SMCException as err:
                self.fail(msg=str(err), exception=traceback.format_exc())
        
        if not changed and self.results.get('state'):
            changed = True
        self.results['changed'] = changed    
        return self.results
예제 #2
0
    def exec_module(self, **kwargs):
        state = kwargs.pop('state', 'present')
        for name, value in kwargs.items():
            setattr(self, name, value)
        
        rbvpn = self.fetch_element(RouteVPN)
        changed = False
        
        if state == 'present':
            
            # Short circuit disable
            if rbvpn and self.enabled is not None and (rbvpn.enabled and not self.enabled):
                rbvpn.disable()
                self.results['changed'] = True
                return self.results
            
            local_engine = self.get_managed_gateway(self.local_gw)
            local_tunnel_interface = self.get_tunnel_interface(
                local_engine, self.local_gw.get('tunnel_interface'))
            local_internal_endpoint = self.get_ipsec_endpoint(
                local_engine, self.local_gw.get('interface_id'),
                address=self.local_gw.get('address'))
            
            if self.remote_gw.get('type', None) != 'external_gateway':
                remote_engine = self.get_managed_gateway(self.remote_gw)
                remote_tunnel_interface = self.get_tunnel_interface(
                    remote_engine, self.remote_gw.get('tunnel_interface'))
                remote_internal_endpoint = self.get_ipsec_endpoint(
                    remote_engine, self.remote_gw.get('interface_id'),
                    address=self.remote_gw.get('address'))
            else:
                # External Gateway
                req = ('name', 'preshared_key', 'external_endpoint')
                for required in req:
                    if required not in self.remote_gw:
                        self.fail(msg='Missing required field for the external endpoint '
                            'configuration: %s' % required)
                
                cache = Cache()
                external_gateway = dict(name=self.remote_gw['name'])
                # External Endpoints are defined in the External Gateway.
                # Build the data structures for a call to ExternalGateway.update_or_create
                ctypes = [] # connection_type element
                for endpoint in self.remote_gw['external_endpoint']:
                    if 'name' not in endpoint or 'address' not in endpoint:
                        self.fail(msg='An external endpoint must have at least a '
                            'name and an address definition.')
                    
                    # SMC version 6.5 requires the connection type element to specify
                    # the role for the given external endpoint
                    if 'connection_type' not in endpoint:
                        self.fail(msg='You must provide the connection_type parameter '
                            'when creating an external endpoint')
                    ctypes.append(endpoint.get('connection_type'))                
                
                cache.add(dict(connection_type=ctypes))
                if cache.missing:
                    self.fail(msg=cache.missing)
                
                # Verify specified VPN Sites exist before continuing
                if 'vpn_site' in self.remote_gw:
                    site_name = self.remote_gw.get('vpn_site', {}).pop('name', None)
                    if not site_name:
                        self.fail(msg='A VPN site requires a name to continue')
                    
                    # Get the elements
                    cache.add(self.remote_gw.get('vpn_site', {}))
                    vpn_site_types = self.remote_gw.get('vpn_site', {}).keys() # Save the VPN site types for retrieval
                    if cache.missing:
                        self.fail(msg='Could not find the specified elements for the '
                            'VPN site configuration: %s' % cache.missing)

                    site_element = [element.href for element_type in vpn_site_types
                        for element in cache.get_type(element_type)]
                    external_gateway.update(
                        vpn_site=[dict(name=site_name, site_element=site_element)])
                
                external_endpoint = []
                for endpoint in self.remote_gw['external_endpoint']:
                    endpoint.update(connection_type_ref=\
                        cache.get('connection_type',endpoint.pop('connection_type')).href)
                    external_endpoint.append(endpoint)
                external_gateway.update(external_endpoint=external_endpoint)
            

        try:
            if state == 'present':
                
                if self.check_mode:
                    return self.results
 
                # Create the tunnel endpoints
                if not rbvpn:
                    local_gateway = TunnelEndpoint.create_ipsec_endpoint(
                        local_engine.vpn.internal_gateway, local_tunnel_interface)
                    
                    # Enable the IPSEC listener on specified interface/s
                    if self.update_ipsec_listener(local_internal_endpoint):
                        changed = True
                    
                    is_external = self.remote_gw.get('type', None) == 'external_gateway'
                    if not is_external:
                        remote_gateway = TunnelEndpoint.create_ipsec_endpoint(
                            remote_engine.vpn.internal_gateway, remote_tunnel_interface)
                        
                        if self.update_ipsec_listener(remote_internal_endpoint):
                            changed = True
                        
                    else: # Update or Create
                        gw, updated, created = ExternalGateway.update_or_create(
                            with_status=True, **external_gateway)
                        remote_gateway = TunnelEndpoint.create_ipsec_endpoint(gw) 
                        if created or updated:
                            changed = True
                    
                    vpn = dict(
                        name=self.name,
                        local_endpoint=local_gateway,
                        remote_endpoint=remote_gateway,
                        preshared_key=self.preshared_key)
                    
                    if is_external:
                        vpn.update(preshared_key=self.remote_gw['preshared_key'])
                    
                    rbvpn = RouteVPN.create_ipsec_tunnel(**vpn)
                    changed = True
                
                else:
                    #TODO: Update or create from top level RBVPN
                    #rbvpn.update_or_create()
                    
                    if rbvpn and self.enabled is not None and (not rbvpn.enabled and self.enabled):
                        rbvpn.enable()
                        changed = True
                    
                    if self.remote_gw.get('type') == 'external_gateway':
                        gw, updated, created = ExternalGateway.update_or_create(
                            with_status=True, **external_gateway)
                    
                        if updated or created:
                            changed = True
                    
                self.results['state'] = rbvpn.data.data
                self.results['changed'] = changed
        
            elif state == 'absent':
                if rbvpn:
                    rbvpn.delete()
                    changed = True
                
        except SMCException as err:
                self.fail(msg=str(err), exception=traceback.format_exc())

        self.results['changed'] = changed        
        return self.results
class ForcepointOSPFElement(ForcepointModuleBase):
    def __init__(self):
        
        self.module_args = dict(
            elements=dict(type='list', required=True),
            state=dict(default='present', type='str', choices=['present', 'absent'])
        )
        self.elements = None
        
        self.results = dict(
            changed=False,
            state=[]
        )
        super(ForcepointOSPFElement, self).__init__(self.module_args, supports_check_mode=True)
        
    def exec_module(self, **kwargs):
        state = kwargs.pop('state', 'present')
        for name, value in kwargs.items():
            setattr(self, name, value)
        
        changed = False
        
        try:
            if state == 'present':
                
                self.cache = Cache()
                
                self.check_elements()
                
                # Any missing dependencies..
                if self.cache.missing:
                    self.fail(msg='Missing dependent elements that are not created within this '
                        'playbook: %s' % self.cache.missing)
                
                deferrals, elements = self.resolve_references(self.elements)
                if self.cache.missing:
                    self.fail(msg='Missing elements that have a dependency, cannot continue: %s'
                        % self.cache.missing)
                 
                if self.check_mode:
                    return self.results
                
                for element in elements:
                    if self.create_or_update_element(element):
                        changed = True

                if deferrals:
                    _, ospf_elements = self.resolve_references(deferrals)
                    for element in ospf_elements:
                        if self.create_or_update_element(element):
                            changed = True
                 
            else:
                # No need to validate elements beyond type and name
                for element in self.elements:
                    for typeof, values in element.items():
                        klazz = lookup_class(typeof)
                        name = values.get('name')
                        if name:
                            try:
                                klazz(name).delete()
                                result = {'name': name, 'type': klazz.typeof, 'action': 'deleted'}
                                changed = True
                            except SMCException as e:
                                result = {'name': name, 'type': klazz.typeof, 'action': 'failed to delete '
                                    'with reason: %s' % str(e)}
                            finally:
                                self.results['state'].append(result)
    
        except SMCException as err:
            self.fail(msg=str(err), exception=traceback.format_exc())
        
        self.results['changed'] = changed
        return self.results 
    
    def serialize_fields(self, typeof, element_values):
        """
        Serialize fields to fit into the update_or_create constructors
        for the relevant types.
        
        :param str typeof: ospf element type
        :param dict element_values: values for dict from yaml
        :return: serialized dict for update_or_create
        """
        new_values = copy.deepcopy(element_values)
        if 'ospfv2_profile' in typeof:
            for entry in new_values.get('redistribution_entry', []):
                if 'filter' in entry:
                    if not entry.get('filter'): # Empty, clear existing
                        entry.update(filter=[])
                    else:
                        for typeof, value in entry.pop('filter', {}).items():
                            # Get element from cache
                            entry.update(filter=
                                self.cache.get(typeof, value[0]))
            
            if 'domain_settings_ref' in element_values:
                new_values.update(domain_settings_ref=
                    self.cache.get_href('ospfv2_domain_settings', element_values.get(
                        'domain_settings_ref')))
                
        elif 'ospfv2_area' in typeof:
            if 'interface_settings_ref' in new_values:
                new_values.update(
                    interface_settings_ref=self.cache.get_href('ospfv2_interface_settings',
                    new_values.pop('interface_settings_ref')))
    
            for _filter in ('inbound_filters', 'outbound_filters'):
                if _filter in new_values:
                    if not new_values.get(_filter): # Empty, clear existing
                        new_values[_filter] = []
                    else:
                        for k, value in new_values.pop(_filter, {}).items():
                            new_values.setdefault(_filter, []).append(
                                self.cache.get_href(k, value[0]))
        return new_values
        
    def create_or_update_element(self, element):
        """
        Create the element. 
        
        :param dict element: the element dict from elements
        """
        changed = False
        for typeof, values in element.items():
            klazz = lookup_class(typeof)
            
            s_values = self.serialize_fields(typeof, values)
            obj, modified, created = klazz.update_or_create(
                with_status=True, **s_values)
            
            if created or modified:
                self.results['state'].append(
                    {'name': obj.name, 'type': obj.typeof, 'action': 'created'
                        if created else 'modified'})
            
            changed = created or modified
        return changed    
        
    def resolve_references(self, elementlist):
        """
        Some elements have a dependency on another element being
        created. Check for those elements here and defer their
        creation until the end if the dependency is also being created.
        
        :rtype: tuple(list, list)
        """
        deferrals, elements = [], []
        for element in elementlist:
            if 'ospfv2_area' in element:
                area = element['ospfv2_area']
                if area.get('interface_settings_ref'):
                    if not self.dependency_being_created(
                        elementlist, 'ospfv2_interface_settings', area['interface_settings_ref']):
                        self.cache._add_entry('ospfv2_interface_settings', area['interface_settings_ref'])
                    else:
                        deferrals.append(element)
                        continue
        
            elif 'ospfv2_profile' in element:
                profile = element['ospfv2_profile']
                if profile.get('domain_settings_ref'):
                    if not self.dependency_being_created(
                        elementlist, 'ospfv2_domain_settings', profile['domain_settings_ref']):
                        self.cache._add_entry('ospfv2_domain_settings', profile['domain_settings_ref'])
                    else:
                        deferrals.append(element)
                        continue

            elements.append(element)
        return deferrals, elements

    def dependency_being_created(self, elementlist, typeof, name):
        """
        Check whether the specified dependency element type is in the
        list to be created or not. If this returns False, the dependency
        should be fetched to verify it exists before creating the element
        that requires it. Element list is the supported element format.
        
        :param list elementlist: list of elements to check for dependency
        :param str typeof: valid bgp element typeof
        :param str name: name to find
        :rtype: bool
        """
        for element in elementlist:
            if typeof in element:
                value = element.get(typeof)
                if value.get('name') == name:
                    return True
        return False

    def check_elements(self):
        """
        Check the elements for validity before continuing. 
        Only return elements that can be processed without being
        deferred due to references.
        
        :rtype: list
        """
        for element in self.elements:
            if not isinstance(element, dict):
                self.fail('OSPF element type must be defined as a dict, received: '
                    '%s, type: %s' % (element, type(element)))

            for ospf_element, values in element.items():
                if ospf_element not in ospf_elements:
                    self.fail(msg='OSPF element type specified is not a supported '
                        'element type, provided: %s. Valid values: %s' %
                        (ospf_element, list(ospf_elements)))
                if not isinstance(values, dict):
                    self.fail(msg='Element values must be of type dict. Received '
                        'values: %s of type: %s' % (values, type(values)))
                
                if 'name' not in values:
                    self.fail(msg='Name is a required field when creating or '
                        'modifying an element. Missing on definition: %s' % ospf_element)
                
                # Check each value against what the constructor requires
                allowed = allowed_args_by_lookup(ospf_element)
                for value in values:
                    if value not in allowed:
                        self.fail(msg='Provided an argument that is not valid for this '
                            'element type. Argument: %s, element type: %s, valid args: %s'
                             % (value, ospf_element, allowed))
                
                # Redistribution entries must exist as they are not created in this module
                if ospf_element == 'ospfv2_profile':
                    for redist in values.get('redistribution_entry', []):
                        for _type, _val in redist.get('filter', {}).items():
                            if _type not in ('ip_access_list', 'route_map'):
                                self.fail(msg='Redistribution entry filters must be either '
                                    'ip_access_list or route_map, received: %s' % _type)
                            self.cache.add(redist['filter'])
                
                elif ospf_element == 'ospfv2_area':
                    for _filter in ('inbound_filters', 'outbound_filters'):
                        if _filter in values:
                            for k, v in values.get(_filter).items():
                                if k not in ('ip_access_list', 'ip_prefix_list'):
                                    self.fail(msg='%s type invalid. Supported filters must '
                                        'be of type ip_access_list or ip_prefix_list. Received '
                                        '%s' % (_filter, k))
                                self.cache.add({k: v})
예제 #4
0
    def exec_module(self, **kwargs):
        state = kwargs.pop('state', 'present')
        for name, value in kwargs.items():
            setattr(self, name, value)

        changed = False

        if state == 'present':
            external_gateway = {'name': self.name}

            cache = Cache()
            # External Endpoints are defined in the External Gateway.
            # Build the data structures for a call to ExternalGateway.update_or_create
            ctypes = []  # connection_type element
            for endpoint in self.external_endpoint:
                if 'name' not in endpoint or 'address' not in endpoint:
                    self.fail(msg='An external endpoint must have at least a '
                              'name and an address definition.')

                # SMC version 6.5 provides the ability to add connection_type settings
                if 'connection_type' in endpoint:
                    ctypes.append(endpoint.get('connection_type'))

            cache.add(dict(connection_type=ctypes))
            if cache.missing:
                self.fail(msg=cache.missing)

            if self.vpn_site:
                site_name = self.vpn_site.pop('name', None)
                if not site_name:
                    self.fail(msg='VPN site requires a name attribute')

                vpn_site_types = self.vpn_site.keys(
                )  # Save the VPN site types for retrieval
                cache.add(self.vpn_site)
                if cache.missing:
                    self.fail(
                        msg='Could not find the specified elements for the '
                        'VPN site configuration: %s' % cache.missing)

                site_element = [
                    element.href for element_type in vpn_site_types
                    for element in cache.get_type(element_type)
                ]
                external_gateway.update(
                    vpn_site=[dict(name=site_name, site_element=site_element)])

            external_endpoint = []
            for endpoint in self.external_endpoint:
                if 'connection_type' in endpoint:
                    endpoint.update(connection_type_ref=\
                        cache.get('connection_type',endpoint.pop('connection_type')).href)
                external_endpoint.append(endpoint)
            external_gateway.update(external_endpoint=external_endpoint)

        try:
            if state == 'present':
                gateway, updated, created = ExternalGateway.update_or_create(
                    with_status=True, **external_gateway)

                if created or updated:
                    self.results['state'].append({
                        'name':
                        gateway.name,
                        'type':
                        gateway.typeof,
                        'action':
                        'created' if created else 'modified'
                    })
                    changed = True

                if self.tags:
                    if self.add_tags(gateway, self.tags):
                        changed = True

            elif state == 'absent':
                result = delete_element(ExternalGateway(self.name),
                                        self.ignore_err_if_not_found)

                if 'action' in result:
                    changed = True
                self.results['state'].append(result)

        except SMCException as err:
            self.fail(msg=str(err), exception=traceback.format_exc())

        self.results['changed'] = changed
        return self.results