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(Engine)

        if state == 'present':
            if not engine:
                # Find interface designated as management
                if not self.interfaces:
                    self.fail(msg='You must provide at least one interface '
                              'configuration to create a firewall.')

                if not self.primary_mgt:
                    self.fail(
                        msg='You must provide a primary_mgt interface to create '
                        'an engine.')

                if not self.type:
                    self.fail(
                        msg='You must specify an engine by type when creating a '
                        'new engine. Types: %s' % engine_types())

                if 'fw_cluster' in self.type and 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)

            else:
                self.type = engine.type
                if self.interfaces and not self.skip_interfaces:
                    itf = self.check_interfaces()
                else:
                    itf = []

            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 or 'interface_id' not in peer:
                        self.fail(
                            msg=
                            'BGP Peering requires a name and interface_id for the '
                            'BGP Peering element. Info provided: %s' % peer)
                    # The specified interface ID must exist for the BGP Peering to succeed. If
                    # the interface is defined in yaml, we'll assume it will be created. If the
                    # engine exists, check if it's defined or if it already exists
                    peer_id = str(peer['interface_id'])
                    if peer_id not in itf and not engine:
                        self.fail(
                            msg=
                            'Interface ID: %s specified for BGP Peering does not '
                            'exist. You must specify a valid interface to bind the peer '
                            % peer_id)
                    elif engine and (peer_id not in engine.interface
                                     and peer_id not in itf):
                        self.fail(
                            msg=
                            'BGP Peering interface id: %s specified does not exist '
                            'on the current engine: %s' %
                            (peer_id, engine.name))
                    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)

            if self.netlinks:
                # Netlinks can be specified on an interface along with destination elements
                # 'behind' these netlinks. Netlinks can only be placed on physical interface
                # types and not tunnel interfaces
                for netlink in self.netlinks:
                    if 'name' not in netlink or 'interface_id' not in netlink:
                        self.fail(
                            msg=
                            'Netlink requires a name and interface_id for the '
                            'Netlink element. Info provided: %s' % netlink)
                    int_id = str(netlink['interface_id'])
                    if int_id not in itf and not engine:
                        self.fail(
                            msg=
                            'Interface ID: %s specified for netlink does not '
                            'exist. You must specify a valid interface to bind the netlink'
                            % int_id)
                    elif engine and (int_id not in engine.physical_interface
                                     and int_id not in itf):
                        self.fail(
                            msg=
                            'Netlink interface id: %s specified does not exist '
                            'on the current engine: %s' %
                            (int_id, engine.name))

                    # Get required elements specified for configuration
                    cache._add_entry('netlink', netlink['name'])
                    # Destination elements for netlink are optional
                    if netlink.get('destination', []) and isinstance(
                            netlink['destination'], list):
                        for dest in netlink['destination']:
                            if 'name' not in dest or 'type' not in dest:
                                self.fail(
                                    msg=
                                    'Netlink destination element reference must '
                                    'contain name and type key values. Provided: %s'
                                    % dest)
                            cache._add_entry(dest['type'], dest['name'])
                if cache.missing:
                    self.fail(
                        msg='Missing elements in netlink configuration: %s' %
                        cache.missing)

            self.cache = cache

        try:

            if state == 'present':
                if not engine:

                    interfaces = [vars(intf) for intf in itf]

                    firewall = {'interfaces': interfaces}
                    firewall.update(
                        name=self.name,
                        primary_mgt=self.primary_mgt,
                        backup_mgt=self.backup_mgt,
                        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

                    if 'fw_cluster' in self.type:
                        firewall.update(
                            cluster_mode=self.cluster_mode,
                            primary_heartbeat=self.primary_heartbeat)

                        engine = FirewallCluster.create_bulk(**firewall)
                    else:
                        engine = Layer3Firewall.create_bulk(**firewall)

                    self.results['state'].append({
                        'name': engine.name,
                        'type': engine.type,
                        'action': 'created'
                    })
                    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 'fw_cluster' in self.type and \
                        (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.
                        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.netlinks:
                    if self.update_netlinks(engine):
                        changed = True

                if self.tags:
                    if self.add_tags(engine, self.tags):
                        changed = True
                else:
                    if self.clear_tags(engine):
                        changed = True

            elif state == 'absent':
                if engine:
                    engine.delete()
                    self.results['state'].append({
                        'name': engine.name,
                        'type': engine.type,
                        'action': 'deleted'
                    })
                    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
Beispiel #2
0
    def exec_module(self, **kwargs):
        state = kwargs.pop('state', 'present')
        for name, value in kwargs.items():
            setattr(self, name, value)

        changed = False

        try:
            # Now that we have valid option settings, we can hit the db
            if self.policy:
                policy = FirewallPolicy.get(self.policy)
            else:
                policy = FirewallSubPolicy.get(self.sub_policy)

            if state == 'present':

                for rule in self.rules:
                    try:
                        validate_rule(rule)
                    except Exception as e:
                        self.fail(msg=str(e))

                self.cache = Cache()

                for rule in self.rules:
                    # Resolve elements if they exist, calls to SMC could happen here
                    if 'sources' in rule:
                        self.field_resolver(rule.get('sources'), rule_targets)

                    if 'destinations' in rule:
                        self.field_resolver(rule.get('destinations'),
                                            rule_targets)

                    if 'services' in rule:
                        self.field_resolver(rule.get('services'),
                                            service_targets)

                    if 'vpn_policy' in rule:
                        self.cache._add_entry('vpn', rule.get('vpn_policy'))

                    if 'sub_policy' in rule:
                        self.cache._add_entry('sub_ipv4_fw_policy',
                                              rule.get('sub_policy'))

                    if 'authentication_options' in rule:
                        auth = rule['authentication_options']
                        if auth.get('require_auth'):
                            for method in auth.get('methods'):
                                self.cache._add_entry('authentication_service',
                                                      method)

                            for accounts in ('users', 'groups'):
                                self.cache._add_user_entries(
                                    accounts, auth.get(accounts, []))

                if self.cache.missing:
                    self.fail(
                        msg=
                        'Missing required elements that are referenced in this '
                        'configuration: %s' % self.cache.missing)

                if self.check_mode:
                    return self.results

                for rule in self.rules:
                    rule_dict = {}

                    if 'log_options' in rule:
                        log_options = LogOptions()
                        _log = rule['log_options']
                        for name, value in log_options.items():
                            if name not in _log:
                                log_options.pop(name)

                        log_options.update(rule.get('log_options', {}))
                        rule_dict.update(log_options=log_options)

                    if 'connection_tracking' in rule:
                        connection_tracking = ConnectionTracking()
                        _ct = rule['connection_tracking']
                        for name, value in connection_tracking.items():
                            if name not in _ct:
                                connection_tracking.pop(name)

                        connection_tracking.update(
                            rule.get('connection_tracking', {}))
                        rule_dict.update(
                            connection_tracking=connection_tracking)

                    action = Action()
                    action.action = rule.get('action', 'allow')

                    if 'inspection_options' in rule:
                        _inspection = rule['inspection_options']
                        for option in inspection_options:
                            if option in _inspection:
                                action[option] = _inspection.get(option)

                    if 'authentication_options' in rule:
                        _auth_options = rule['authentication_options']
                        auth_options = AuthenticationOptions()

                        if _auth_options.get('require_auth'):
                            auth_options.update(methods=[
                                self.get_value('authentication_service',
                                               m).href
                                for m in _auth_options.get('methods', [])
                            ],
                                                require_auth=True)

                            auth_options.update(users=[
                                entry.href for entry in self.cache.get_type(
                                    'user_element')
                            ])

                        rule_dict.update(authentication_options=auth_options)

                    rule_dict.update(action=action)

                    for field in ('sources', 'destinations', 'services'):
                        rule_dict[field] = self.get_values(
                            rule.get(field, None))

                    rule_dict.update(
                        vpn_policy=self.get_value('vpn',
                                                  rule.get('vpn_policy')),
                        sub_policy=self.get_value('sub_ipv4_fw_policy',
                                                  rule.get('sub_policy')),
                        mobile_vpn=rule.get('mobile_vpn', False))

                    if 'comment' in rule:
                        rule_dict.update(comment=rule.get('comment'))

                    rule_dict.update(name=rule.get('name'),
                                     is_disabled=rule.get(
                                         'is_disabled', False))

                    if 'tag' not in rule:
                        # If no tag is present, this is a create
                        rule_dict.update(before=rule.get('add_before'),
                                         after=rule.get('add_after'))

                        rule = policy.fw_ipv4_access_rules.create(**rule_dict)
                        changed = True
                        self.results['state'].append({
                            'rule': rule.name,
                            'type': rule.typeof,
                            'action': 'created'
                        })

                    else:
                        # Modify as rule has 'tag' defined. Fetch the rule first
                        # by it's tag reference, skip if tag not found
                        target_rule = self.rule_by_tag(policy, rule.get('tag'))
                        if not target_rule:
                            continue

                        changes = compare_rules(target_rule, rule_dict)
                        # Changes have already been merged if any
                        if rule.get('add_after', None):
                            rule_at_pos = self.rule_by_tag(
                                policy, rule.get('add_after'))
                            if rule_at_pos:
                                target_rule.move_rule_after(rule_at_pos)
                                changes.append('add_after')
                        elif rule.get('add_before', None):
                            rule_at_pos = self.rule_by_tag(
                                policy, rule.get('add_before'))
                            if rule_at_pos:
                                target_rule.move_rule_before(rule_at_pos)
                                changes.append('add_before')
                        elif changes:
                            target_rule.save()

                        if changes:
                            changed = True
                            self.results['state'].append({
                                'rule': target_rule.name,
                                'type': target_rule.typeof,
                                'action': 'modified',
                                'changes': changes
                            })

            elif state == 'absent':
                for rule in self.rules:
                    if 'tag' in rule:
                        target_rule = self.rule_by_tag(policy, rule.get('tag'))
                        if target_rule:
                            target_rule.delete()
                            changed = True
                            self.results['state'].append({
                                'rule': target_rule.name,
                                'type': target_rule.typeof,
                                'action': 'deleted'
                            })

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

        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)

                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
                external_endpoint = []
                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.')
                    external_endpoint.append(endpoint)
                external_gateway.update(external_endpoint=external_endpoint)

                # 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 = Cache()
                    cache.add(self.remote_gw.get('vpn_site', {}))
                    if cache.missing:
                        self.fail(
                            msg='Could not find the specified elements for the '
                            'VPN site configuration: %s' % cache.missing)
                    site_element = [
                        value.href for _, values in cache.cache.items()
                        for value in values
                    ]
                    external_gateway.update(vpn_site=[
                        dict(name=site_name, site_element=site_element)
                    ])

        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)

                    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
Beispiel #4
0
    def exec_module(self, **kwargs):
        state = kwargs.pop('state', 'present')
        for name, value in kwargs.items():
            setattr(self, name, value)

        ELEMENT_TYPES = element_type_dict()

        try:
            if state == 'present':
                deferred_elements = ('group', 'netlink')
                # Validate elements before proceeding.
                groups, netlinks = [], []
                for element in self.elements:
                    self.is_element_valid(element, ELEMENT_TYPES)
                    if 'group' in element:
                        groups.append(element)
                    elif 'netlink' in element:
                        netlinks.append(element)

                if groups or netlinks:
                    to_be_created = self.to_be_created_elements()
                    self.cache = Cache()

                if groups:
                    self.enum_group_members(groups, to_be_created)
                    if self.cache.missing:
                        self.fail(
                            msg=
                            'Group members referenced are missing and are not being '
                            'created in this playbook: %s' %
                            self.cache.missing)

                if netlinks:
                    self.enum_netlink_members(netlinks, to_be_created)
                    if self.cache.missing:
                        self.fail(
                            msg=
                            'Netlink elements referenced are missing and are not being '
                            'created in this playbook: %s' %
                            self.cache.missing)

                for element in self.elements:
                    for typeof, _ in element.items():
                        if typeof not in deferred_elements:
                            result = update_or_create(
                                element,
                                ELEMENT_TYPES,
                                check_mode=self.check_mode)
                            self.results['state'].append(result)

                for group in groups:
                    # Run through cache again, entries that exist will not be
                    # added twice but this captures elements that might have been
                    # added earlier by the playbook run
                    _group = copy.deepcopy(group)
                    members = _group.get('group', {}).get('members', {})
                    if members:
                        self.cache.add_many([members])
                        # Add to new members list
                        _members = [
                            self.cache.get(typeof, value)
                            for typeof, member in members.items()
                            for value in member
                        ]
                    else:  # No members defined
                        _members = []

                    _group.setdefault('group', {}).update(members=_members)

                    result = update_or_create(_group,
                                              ELEMENT_TYPES,
                                              check_mode=self.check_mode)
                    self.results['state'].append(result)

                for netlink in netlinks:
                    _netlink = copy.deepcopy(netlink)
                    gateway = _netlink.get('netlink', {}).get('gateway')
                    self.cache._add_entry(gateway.get('type'),
                                          gateway.get('name'))
                    _netlink.setdefault('netlink').update(
                        gateway=self.cache.get(gateway.get('type'),
                                               gateway.get('name')))

                    # Update networks
                    networks = _netlink.get('netlink').get('network')
                    for net in networks:
                        self.cache._add_entry('network', net)
                    _netlink.setdefault('netlink').update(network=[
                        self.cache.get('network', net) for net in networks
                    ])

                    result = update_or_create(_netlink,
                                              ELEMENT_TYPES,
                                              check_mode=self.check_mode)
                    self.results['state'].append(result)

                if self.check_mode:
                    return self.results

            else:
                for element in self.elements:
                    for typeof in element:
                        if typeof not in ELEMENT_TYPES:
                            self.fail(
                                msg=
                                'Element specified is not valid, got: {}, valid: {}'
                                .format(typeof, ELEMENT_TYPES.keys()))
                        else:
                            if not self.check_mode:
                                for elements in element[typeof]:
                                    result = delete_element(
                                        ELEMENT_TYPES.get(typeof)['type'](
                                            elements),
                                        self.ignore_err_if_not_found)
                                    self.results['state'].append(result)

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

        for results in self.results['state']:
            if 'action' in results:
                self.results['changed'] = True
                break
        return self.results
Beispiel #5
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}

            # External Endpoints are defined in the External Gateway.
            # Build the data structures for a call to ExternalGateway.update_or_create
            external_endpoint = []
            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.')
                external_endpoint.append(endpoint)
            external_gateway.update(external_endpoint=external_endpoint)

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

                cache = Cache()
                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 = [
                    value.href for _, values in cache.cache.items()
                    for value in values
                ]
                external_gateway.update(
                    vpn_site=[dict(name=site_name, site_element=site_element)])

        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