class NetworkElement(ForcepointModuleBase):
    def __init__(self):

        self.module_args = dict(elements=dict(type='list', required=True),
                                ignore_err_if_not_found=dict(type='bool',
                                                             default=True),
                                state=dict(default='present',
                                           type='str',
                                           choices=['present', 'absent']))

        self.elements = None
        self.ignore_err_if_not_found = None

        self.results = dict(changed=False, state=[])
        super(NetworkElement, 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)

        ELEMENT_TYPES = element_type_dict()
        GROUP_MEMBER_TYPES = ro_element_type_dict(map_only=True)
        GROUP_MEMBER_TYPES.update(ELEMENT_TYPES)

        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' not in\
                        element else GROUP_MEMBER_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

    def to_be_created_elements(self):
        """
        Get a dict of all elements that are to be created by this playbook.
        This is used when nested elements are being created that have
        requirements on other elements. This allows nested elements to be
        created alongside of their dependency.
        
        :return: dict of element by type: set([names]) to be created
        :rtype: dict
        """
        nested_element = ('netlink', 'group')
        to_be_created = {
        }  # dict of element by type: set([names]) to be created.
        for element in self.elements:
            for typeof, values in element.items():
                if typeof not in nested_element:
                    to_be_created.setdefault(typeof,
                                             set()).add(values.get('name'))
        return to_be_created

    def enum_group_members(self, groups, pending_elements):
        """
        Check group membership. Groups reference only the type of element and
        the names of those members. If the element is being created, skip the
        existence check. If the member is not being created, attempt to fetch
        the member and save to cache. Return the cache. Check cache.missing
        before continuing to ensure all required elements are found.
        
        :param list groups: list of groups extracted from elements
        :param dict pending_elements: elements waiting to be created
        :return: None
        """
        for group in groups:
            members = group.get('group', {}).get('members', {})
            members = {} if members is None else members
            for typeof, member in members.items():
                for name in member:
                    if name not in pending_elements.get(typeof, set()):
                        self.cache._add_entry(typeof, name)

    def enum_netlink_members(self, netlinks, pending_elements):
        """
        Netlinks reference nested elements gateway and networks. Attempt to
        locate these either in definitions to be created by this playbook or
        existing already in SMC. Populate cache with href and name map and
        catch any missing before continuing.
        
        :return: None
        """
        for netlink in netlinks:
            values = netlink.get('netlink', [])
            for req in ('gateway', 'network'):
                if req not in values:
                    self.fail(
                        msg='Netlink requires a gateway and list of networks, '
                        'received: %s' % values)
            # Add requirements to cache
            gateway = values['gateway']
            if not isinstance(
                    gateway,
                    dict) or 'name' not in gateway or 'type' not in gateway:
                self.fail(
                    msg=
                    'Netlink gateway must be a dict with a name and type key value: %s'
                    % gateway)

            if gateway.get('type') not in ('engine', 'router'):
                self.fail(
                    msg=
                    'Netlink types can only be of type engine or router, got: %s'
                    % gateway.get('type'))

            networks = values['network']
            if not isinstance(networks, list):
                self.fail(
                    msg='Netlink networks must be defined as a list, got: %s' %
                    type(networks))

            if gateway.get('name') not in pending_elements.get(
                    gateway.get('type'), set()):
                self.cache._add_entry(gateway.get('type'), gateway.get('name'))

            for network in networks:
                if network not in pending_elements.get('network', set()):
                    self.cache._add_entry('network', network)

            for attr in ('probe_address', 'domain_server_address'):
                if attr in values and not isinstance(values.get(attr), list):
                    self.fail(msg='%s must be in list format' % attr)
예제 #2
0
class FirewallRule(ForcepointModuleBase):
    def __init__(self):
        
        self.module_args = dict(
            policy=dict(type='str'),
            sub_policy=dict(type='str'),
            rules=dict(type='list', default=[]),
            state=dict(default='present', type='str', choices=['present', 'absent'])
        )
        
        self.policy = None
        self.sub_policy = None
        self.rules = None
        
        mutually_exclusive = [
            ['policy', 'sub_policy'],
        ]
         
        required_one_of = [
            [ 'policy', 'sub_policy' ]
        ]
        
        self.results = dict(
            changed=False,
            state=[]
        )
        
        super(FirewallRule, self).__init__(self.module_args,
            mutually_exclusive=mutually_exclusive, required_one_of=required_one_of,
            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:            
            # 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_syntax(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() # If no action, set to default based on version
                    if 'action' not in rule:
                        action.action = 'allow' if not is_sixdotsix_compat() else ['allow']
                    else:
                        action.action = rule.get('action')
                    
                    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 rule_by_tag(self, policy, tag):
        """
        Get the rule referenced by it's tag. Tag will be in format
        '1234566.0'. When doing a search_rule, you must omit the part
        after the dot(.) to find the rule.
        
        :param FirewallPolicy policy: policy reference
        :param str tag: tag
        :rtype: Rule or None
        """
        resolved_tag = get_tag(tag)
        if resolved_tag:
            rule = policy.search_rule('@{}'.format(resolved_tag))
            return rule[0] if rule else None
    
    def field_resolver(self, elements, types):
        """
        Field resolver, specific to retrieving network or service level
        elements in different formats. If elements are referencing existing
        elements, they will be loaded in the cache for retrieval.
        
        Format #1, as list (elements are expected to exist):
            - tcp_service:
                - service1
                - service2
        
        Format #2, as dict, only used for specifying any:
            any: true
        
        Format #3, if you have retrieved the rule and the sources, services,
        and destinations are in href format, pass through.
        
            - sources:
                - http://1.1.1.1/elements/host/1
                - http://1.1.1.1/elements/host/2
                ...
    
        .. note:: This is optimal if sources, destinations or services are
            not being changed as it will not result in queries to SMC. This
            is accomplished by not setting `expand` when running the facts
            module as_yaml.
            
        :param list elements: list of elements as parsed from YAML file
        :param dict type_dict: type dictionary for elements that should be
            supported for this run.
        """
        if isinstance(elements, dict):
            if 'any' in elements or 'none' in elements:
                return
            
            for name, value in elements.items():
                if name not in types:
                    self.fail(msg='Invalid element type specified: %s. Valid '
                        'types are: %s' % (name, list(types)))
                if not isinstance(value, list):
                    self.fail(msg='Elements specified for type: %s should be in list '
                        'format, got: %s' % (name, type(value)))
            
            self.cache.add_many([elements])

        elif isinstance(elements, list):
            for entry in elements:
                if not isinstance(entry, string_types) or not entry.startswith('http'):
                    self.fail(msg='List entry is expected to be the raw href of '
                        'the element. Received: %s' % entry)
    
    def get_value(self, typeof, element):
        """
        Get single value from cache
        
        :param str typeof: typeof element by key
        :param str element to fetch
        """
        return self.cache.get(typeof, element)
        
    def get_values(self, elements):
        """
        Get the values for source, destination and service cells. If these
        are not provided, return 'any'.
        """
        if not elements:
            return 'any'
        
        if isinstance(elements, dict):
            if 'any' in elements:
                return 'any'
            elif 'none' in elements:
                return None
        
            # Resolve out of cache, return as Element
            return [self.cache.get(typeof, value)
                for typeof, values in elements.items()
                for value in values]

        elif isinstance(elements, list):
            return elements
예제 #3
0
class FirewallNATRule(ForcepointModuleBase):
    def __init__(self):
        self.module_args = dict(policy=dict(type='str'),
                                sub_policy=dict(type='str'),
                                rules=dict(type='list', default=[]),
                                state=dict(default='present',
                                           type='str',
                                           choices=['present', 'absent']))

        self.policy = None
        self.sub_policy = None
        self.rules = None

        mutually_exclusive = [
            ['policy', 'sub_policy'],
        ]

        required_one_of = [['policy', 'sub_policy']]

        self.results = dict(changed=False, state=[])

        super(FirewallNATRule,
              self).__init__(self.module_args,
                             mutually_exclusive=mutually_exclusive,
                             required_one_of=required_one_of,
                             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:
            # 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':

                self.cache = Cache()

                for rule in self.rules:
                    if 'tag' not in rule and 'name' not in rule:
                        self.fail(
                            msg='A rule must have either a rule tag or a '
                            'name field: %s' % rule)

                    if 'used_on' in rule:
                        used_on = rule['used_on']
                        if not isinstance(used_on, string_types):
                            self.fail(
                                msg='Used on field should be the name of an '
                                'engine or the value "ANY". Received: %s' %
                                used_on)

                    if all(ntype in rule
                           for ntype in ('static_src_nat', 'dynamic_src_nat')):
                        self.fail(
                            msg=
                            'You must specify either static or dynamic source '
                            'NAT, not both: %s' % rule)

                    # Resolve elements if they exist, calls to SMC could happen here
                    if self.field_resolver(rule.get('sources', {'none': True}),
                                           rule_targets):
                        if 'static_src_nat' in rule:
                            self.fail(
                                msg=
                                'You must specify a source value when configuring '
                                'static_src_nat. ANY and None are not valid: %s'
                                % rule)

                    if self.field_resolver(
                            rule.get('destinations', {'none': True}),
                            rule_targets):
                        if 'static_dst_nat' in rule:
                            self.fail(
                                msg=
                                'You must specify a destination value when configuring '
                                'static_dst_nat. ANY and None are not valid: %s'
                                % rule)

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

                    # Evaluate the NAT definitions first as they might have embedded
                    # element references.
                    for nat in nat_type:
                        if nat not in rule:
                            continue

                        if not isinstance(rule[nat], dict):
                            self.fail(
                                msg=
                                'NAT definition must be type dict. Rule was: %s'
                                % rule)

                        nat_value_dict = rule.get(nat)
                        translated_value = nat_value_dict.get(
                            'translated_value')
                        if not translated_value or not isinstance(
                                translated_value, dict):
                            self.fail(
                                msg=
                                'NAT translated value must exist and be in dict '
                                'format. Rule was: %s' % rule)

                        if all(port in translated_value
                               for port in ('min_port', 'max_port')):
                            min_port = translated_value.get('min_port')
                            max_port = translated_value.get('max_port')

                            # Port ranges are not valid for dynamic source NAT
                            if any('-' in port for port in map(str, (min_port, max_port))) and \
                                'dynamic_src_nat' in rule:
                                self.fail(
                                    msg=
                                    'Dynamic source NAT port definitions must be in '
                                    'single port (str or int) format. Ranges are not valid: %s'
                                    % rule)

                            if not is_a_valid_port(min_port, max_port):
                                self.fail(
                                    msg=
                                    'Ports specified for nat type: %r are not valid. '
                                    'Ports for dynamic_src_nat must be between 1-65535 and port '
                                    'ranges used for static_dst_nat must be of equal lengths. '
                                    'Min port: %s, max_port: %s' %
                                    (nat, min_port, max_port))

                        if all(k in translated_value
                               for k in ('name', 'type')):
                            # Add elements to cache if defined
                            self.cache._add_entry(translated_value.get('type'),
                                                  translated_value.get('name'))

                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

                # If we've gotten here, cache is populated and we're not missing anything
                for rule in self.rules:
                    rule_dict = {}

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

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

                    for nat in nat_type:
                        if nat in rule:
                            rule_dict.update(
                                self.nat_definition(nat, rule.get(nat)))

                    if 'tag' not in rule:
                        rule = policy.fw_ipv4_nat_rules.create(**rule_dict)
                        changed = True
                        self.results['state'].append({
                            'rule': rule.name,
                            'type': rule.typeof,
                            'action': 'created'
                        })
                    else:
                        target_rule = self.rule_by_tag(policy, rule.get('tag'))
                        if not target_rule:
                            continue

                        changes = compare_rules(target_rule, rule_dict)
                        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 rule_by_tag(self, policy, tag):
        """
        Get the rule referenced by it's tag. Tag will be in format
        '1234566.0'. When doing a search_rule, you must omit the part
        after the dot(.) to find the rule.
        
        :param FirewallPolicy policy: policy reference
        :param str tag: tag
        :rtype: Rule or None
        """
        resolved_tag = get_tag(tag)
        if resolved_tag:
            rule = policy.search_rule('@{}'.format(resolved_tag))
            return rule[0] if rule else None

    def field_resolver(self, elements, types):
        """
        Field resolver, specific to retrieving network or service level
        elements in different formats. If elements are referencing existing
        elements, they will be loaded in the cache for retrieval.
        
        Format #1, as list (elements are expected to exist):
            - tcp_service:
                - service1
                - service2
        
        Format #2, as dict, only used for specifying any:
            any: true
        
        Format #3, if you have retrieved the rule and the sources, services,
        and destinations are in href format, pass through.
        
            - sources:
                - http://1.1.1.1/elements/host/1
                - http://1.1.1.1/elements/host/2
                ...
    
        .. note:: This is optimal if sources, destinations or services are
            not being changed as it will not result in queries to SMC. This
            is accomplished by not setting `expand` when running the facts
            module as_yaml.
        
        Return True if any or none are used. This is necessary to verify for
        NAT rules as source ANY/None or destinations ANY/None are not valid
        when doing static_src_nat or static_dst_nat.
            
        :param list elements: list of elements as parsed from YAML file
        :param dict type_dict: type dictionary for elements that should be
            supported for this run.
        :return: returns True if any or none are set on these fields. Otherwise
            None is returned and elements are added to cache or cache missing
        :rtype: bool or None
        """
        if isinstance(elements, dict):
            if 'any' in elements or 'none' in elements:
                return True

            for name, value in elements.items():
                if name not in types:
                    self.fail(msg='Invalid element type specified: %s. Valid '
                              'types are: %s' % (name, list(types)))
                if not isinstance(value, list):
                    self.fail(
                        msg='Elements specified for type: %s should be in list '
                        'format, got: %s' % (name, type(value)))

            self.cache.add_many([elements])

        elif isinstance(elements, list):
            for entry in elements:
                if not isinstance(
                        entry, string_types) or not entry.startswith('http'):
                    self.fail(
                        msg='List entry is expected to be the raw href of '
                        'the element. Received: %s' % entry)

    def get_values(self, elements):
        """
        Get the values for source, destination and service cells. If these
        are not provided, return 'any'.
        """
        if not elements:
            return 'any'

        if isinstance(elements, dict):
            if 'any' in elements:
                return 'any'
            elif 'none' in elements:
                return None

            # Resolve out of cache, return as Element
            return [
                self.cache.get(typeof, value)
                for typeof, values in elements.items() for value in values
            ]

        elif isinstance(elements, list):
            return elements

    def nat_definition(self, nat_type, data_dict):
        """
        Return a dict for the rule create constructor.
        
        :param str nat_type: nat type retrieved from yaml
        :param dict data_dict: nat dict from yaml definition
        :rtype: dict
        """
        nat_dict = {}
        translated_value = data_dict.get('translated_value')
        if nat_type in ('dynamic_src_nat', 'static_src_nat'):
            # Validate ports first. Range of ports must have equal length
            if 'min_port' in translated_value and 'max_port' in translated_value:
                nat_dict['%s_ports' %
                         nat_type] = (translated_value.get('min_port'),
                                      translated_value.get('max_port'))

        if 'name' in translated_value and 'type' in translated_value:
            nat_dict[nat_type] = self.cache.get(translated_value['type'],
                                                translated_value['name'])
        else:
            nat_dict[nat_type] = translated_value.get('ip_descriptor')
        return nat_dict