Beispiel #1
0
def _new_dyn_traffic_director_service(name,
                                      ttl=DEFAULT_TD_TTL,
                                      records=None,
                                      attach_nodes=None) -> TrafficDirector:
    """Creates an opinionated Dyn Traffic Director service

    - Creates a CNAME recordset
    - Expects all records to be DSFCNAMERecord type
    - Creates a single failover chain pointing to the recordset
    - Creates a single pool pointing to the failover chain
    - Creates a single ruleset that always responds and points to the pool

    NOTE: The dyn module TrafficDirector constructor has side effects and calls
          the Dyn API multiple times. It is possible that Dyn resources are
          created even if the constructor failed
    """

    if records is None:
        records = []
    if attach_nodes is None:
        attach_nodes = []

    try:
        record_set = DSFRecordSet('CNAME',
                                  label=name,
                                  automation='manual',
                                  records=records)
        failover_chain = DSFFailoverChain(label=name, record_sets=[record_set])
        rpool = DSFResponsePool(label=name, rs_chains=[failover_chain])
        ruleset = DSFRuleset(label=name,
                             criteria_type='always',
                             response_pools=[rpool])

        # Constructor does the actual resource creation and checking for
        # creation completion.
        # It returns returns a resource ID which we don't need
        return TrafficDirector(name,
                               ttl=ttl,
                               rulesets=[ruleset],
                               nodes=attach_nodes)
    except Exception as e:
        raise CreateTrafficDirectorError(
            f'Exception caught during creation of Traffic Director: {name} '
            f'The exception was: {e}')
Beispiel #2
0
    def _mod_rulesets(self, td, change):
        new = change.new

        # Response Pools
        pools = {}

        # Get existing pools. This should be simple, but it's not b/c the dyn
        # api is a POS. We need all response pools so we can GC and check to
        # make sure that what we're after doesn't already exist.
        # td.all_response_pools just returns thin objects that don't include
        # their rs_chains (and children down to actual records.) We could just
        # foreach over those turning them into full DSFResponsePool objects
        # with get_response_pool, but that'd be N round-trips. We can avoid
        # those round trips in cases where the pools are in use in rules where
        # they're already full objects.

        # First up populate all the full pools we have under rules, the _
        # prevents a td.refresh we don't need :-( seriously?
        existing_rulesets = td._rulesets
        for ruleset in existing_rulesets:
            for pool in ruleset.response_pools:
                pools[pool.response_pool_id] = pool
        # Now we need to find any pools that aren't referenced by rules
        for pool in td.all_response_pools:
            rpid = pool.response_pool_id
            if rpid not in pools:
                # we want this one, but it's thin, inflate it
                pools[rpid] = get_response_pool(rpid, td)
        # now that we have full objects for the complete set of existing pools,
        # a list will be more useful
        pools = pools.values()

        # Rulesets

        # add the default
        label = 'default:{}'.format(uuid4().hex)
        ruleset = DSFRuleset(label, 'always', [])
        ruleset.create(td, index=0)
        pool = self._find_or_create_pool(td, pools, 'default', new._type,
                                         new.values)
        # There's no way in the client lib to create a ruleset with an existing
        # pool (ref'd by id) so we have to do this round-a-bout.
        active_pools = {'default': pool.response_pool_id}
        ruleset.add_response_pool(pool.response_pool_id)

        monitor_id = self._traffic_director_monitor(new.fqdn).dsf_monitor_id
        # Geos ordered least to most specific so that parents will always be
        # created before their children (and thus can be referenced
        geos = sorted(new.geo.items(), key=lambda d: d[0])
        for _, geo in geos:
            if geo.subdivision_code:
                criteria = {'province': geo.subdivision_code.lower()}
            elif geo.country_code:
                criteria = {'country': geo.country_code}
            else:
                criteria = {'region': self.REGION_CODES[geo.continent_code]}

            label = '{}:{}'.format(geo.code, uuid4().hex)
            ruleset = DSFRuleset(label, 'geoip', [], {'geoip': criteria})
            # Something you have to call create others the constructor does it
            ruleset.create(td, index=0)

            first = geo.values[0]
            pool = self._find_or_create_pool(td, pools, first, new._type,
                                             geo.values, monitor_id)
            active_pools[geo.code] = pool.response_pool_id
            ruleset.add_response_pool(pool.response_pool_id)

            # look for parent rulesets we can add in the chain
            for code in geo.parents:
                try:
                    pool_id = active_pools[code]
                    # looking at client lib code, index > exists appends
                    ruleset.add_response_pool(pool_id, index=999)
                except KeyError:
                    pass
            # and always add default as the last
            pool_id = active_pools['default']
            ruleset.add_response_pool(pool_id, index=999)

        # we're done with active_pools as a lookup, convert it in to a set of
        # the ids in use
        active_pools = set(active_pools.values())
        # Clean up unused response_pools
        for pool in pools:
            if pool.response_pool_id in active_pools:
                continue
            pool.delete()

        # Clean out the old rulesets
        for ruleset in existing_rulesets:
            ruleset.delete()