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
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})
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