def create_update_traffic_manager_endpoint(self):
        '''
        Creates or updates a Traffic Manager endpoint.

        :return: deserialized Traffic Manager endpoint state dictionary
        '''
        self.log("Creating / Updating the Traffic Manager endpoint {0}".format(self.name))

        parameters = Endpoint(target_resource_id=self.target_resource_id,
                              target=self.target,
                              endpoint_status=self.endpoint_status,
                              weight=self.weight,
                              priority=self.priority,
                              endpoint_location=self.location,
                              min_child_endpoints=self.min_child_endpoints,
                              geo_mapping=self.geo_mapping)

        try:
            response = self.traffic_manager_management_client.endpoints.create_or_update(self.resource_group,
                                                                                         self.profile_name,
                                                                                         self.type,
                                                                                         self.name,
                                                                                         parameters)
            return traffic_manager_endpoint_to_dict(response)
        except CloudError as exc:
            request_id = exc.request_id if exc.request_id else ''
            self.fail("Error creating the Traffic Manager endpoint {0}, request id {1} - {2}".format(self.name, request_id, str(exc)))
 def _create_or_update_traffic_manager(
     self,
     tag: str,
     traffic_manager_name: str,
     appgw_public_ip_addresses: typing.List[PublicIPAddress],
 ) -> Profile:
     """
     Creates new or updates existing Traffic Manager in Azure.
     """
     logging.debug("Creating or updating traffic manager")
     tm_profile = self.traffic_manager.profiles.create_or_update(
         resource_group_name=self.options.azure_trafficmanager_resource_group,
         profile_name=traffic_manager_name,
         parameters=Profile(
             name=traffic_manager_name,
             location="global",
             tags=get_policy_tags(additional_tags={TAG_APPGWGROUP: tag}),
             profile_status="Enabled",
             traffic_routing_method="Priority",
             dns_config=DnsConfig(
                 relative_name=traffic_manager_name, ttl=TRAFFICMANAGER_DNS_TTL
             ),
             monitor_config=MonitorConfig(
                 protocol="https",
                 port=80,
                 path="/",
                 interval_in_seconds=10,
                 timeout_in_seconds=5,
                 tolerated_number_of_failures=1,
                 custom_headers=[
                     MonitorConfigCustomHeadersItem(
                         name="Host", value=TRAFFICMANAGER_HEALTHCHECK_HOSTNAME
                     )
                 ],
                 expected_status_code_ranges=[
                     MonitorConfigExpectedStatusCodeRangesItem(min=200, max=399)
                 ],
             ),
             endpoints=[],
         ),
     )
     # Construct endpoints for traffic manager
     for index, ip in enumerate(appgw_public_ip_addresses):
         self.traffic_manager.endpoints.create_or_update(
             resource_group_name=self.options.azure_trafficmanager_resource_group,
             profile_name=tm_profile.name,
             endpoint_type="AzureEndpoints",
             endpoint_name=f"endpoint-{index}",
             parameters=Endpoint(
                 name=f"endpoint-{index}",
                 target_resource_id=ip.id,
                 target=ip.ip_address,
             ),
         )
     return tm_profile
def create_endpoint_instance(endpoint):
    return Endpoint(id=endpoint['id'],
                    name=endpoint['name'],
                    type=endpoint['type'],
                    target_resource_id=endpoint['target_resource_id'],
                    target=endpoint['target'],
                    endpoint_status=endpoint['endpoint_status'],
                    weight=endpoint['weight'],
                    priority=endpoint['priority'],
                    endpoint_location=endpoint['endpoint_location'],
                    min_child_endpoints=endpoint['min_child_endpoints'],
                    geo_mapping=endpoint['geo_mapping'])
Example #4
0
    def _generate_traffic_managers(self, record):
        traffic_managers = []
        pools = record.dynamic.pools

        default = record.value[:-1]
        tm_suffix = _traffic_manager_suffix(record)
        profile = self._generate_tm_profile

        geo_endpoints = []
        pool_profiles = {}

        for rule in record.dynamic.rules:
            # Prepare the list of Traffic manager geos
            rule_geos = rule.data.get('geos', [])
            geos = []
            for geo in rule_geos:
                if '-' in geo:
                    # country/state
                    geos.append(geo.split('-', 1)[-1])
                else:
                    # continent
                    if geo == 'AS':
                        # Middle East is part of Asia in octoDNS, but
                        # Azure treats it as a separate "group", so let's
                        # add it in the list of geo mappings. We will drop
                        # it when we later parse the list of regions.
                        geos.append('GEO-ME')
                    elif geo == 'OC':
                        # Azure uses Australia/Pacific (AP) instead of
                        # Oceania
                        geo = 'AP'

                    geos.append('GEO-{}'.format(geo))
            if not geos:
                geos.append('WORLD')

            pool_name = rule.data['pool']
            rule_endpoints = []
            priority = 1
            default_seen = False

            while pool_name:
                # iterate until we reach end of fallback chain
                default_seen = False
                pool = pools[pool_name].data
                if len(pool['values']) > 1:
                    # create Weighted profile for multi-value pool
                    pool_profile = pool_profiles.get(pool_name)
                    if pool_profile is None:
                        endpoints = []
                        for val in pool['values']:
                            target = val['value']
                            # strip trailing dot from CNAME value
                            target = target[:-1]
                            ep_name = '{}--{}'.format(pool_name, target)
                            if target == default:
                                # mark default
                                ep_name += '--default--'
                                default_seen = True
                            endpoints.append(Endpoint(
                                name=ep_name,
                                target=target,
                                weight=val.get('weight', 1),
                            ))
                        profile_name = 'pool-{}--{}'.format(
                            pool_name, tm_suffix)
                        pool_profile = profile(profile_name, 'Weighted',
                                               endpoints, record)
                        traffic_managers.append(pool_profile)
                        pool_profiles[pool_name] = pool_profile

                    # append pool to endpoint list of fallback rule profile
                    rule_endpoints.append(Endpoint(
                        name=pool_name,
                        target_resource_id=pool_profile.id,
                        priority=priority,
                    ))
                else:
                    # Skip Weighted profile hop for single-value pool
                    # append its value as an external endpoint to fallback
                    # rule profile
                    target = pool['values'][0]['value'][:-1]
                    ep_name = pool_name
                    if target == default:
                        # mark default
                        ep_name += '--default--'
                        default_seen = True
                    rule_endpoints.append(Endpoint(
                        name=pool_name,
                        target=target,
                        priority=priority,
                    ))

                priority += 1
                pool_name = pool.get('fallback')

            # append default endpoint unless it is already included in
            # last pool of rule profile
            if not default_seen:
                rule_endpoints.append(Endpoint(
                    name='--default--',
                    target=default,
                    priority=priority,
                ))

            if len(rule_endpoints) > 1:
                # create rule profile with fallback chain
                rule_profile_name = 'rule-{}--{}'.format(
                    rule.data['pool'], tm_suffix)
                rule_profile = profile(rule_profile_name, 'Priority',
                                       rule_endpoints, record)
                traffic_managers.append(rule_profile)

                # append rule profile to top-level geo profile
                geo_endpoints.append(Endpoint(
                    name='rule-{}'.format(rule.data['pool']),
                    target_resource_id=rule_profile.id,
                    geo_mapping=geos,
                ))
            else:
                # Priority profile has only one endpoint; skip the hop and
                # append its only endpoint to the top-level profile
                rule_ep = rule_endpoints[0]
                if rule_ep.target_resource_id:
                    # point directly to the Weighted pool profile
                    geo_endpoints.append(Endpoint(
                        name='rule-{}'.format(rule.data['pool']),
                        target_resource_id=rule_ep.target_resource_id,
                        geo_mapping=geos,
                    ))
                else:
                    # just add the value of single-value pool
                    geo_endpoints.append(Endpoint(
                        name=rule_ep.name + '--default--',
                        target=rule_ep.target,
                        geo_mapping=geos,
                    ))

        if len(geo_endpoints) == 1 and \
           geo_endpoints[0].geo_mapping == ['WORLD'] and \
           geo_endpoints[0].target_resource_id:
            # Single WORLD rule does not require a Geographic profile, use
            # the target profile as the root profile
            target_profile_id = geo_endpoints[0].target_resource_id
            profile_map = dict((tm.id, tm) for tm in traffic_managers)
            target_profile = profile_map[target_profile_id]
            self._update_tm_name(target_profile, tm_suffix)
        else:
            geo_profile = profile(tm_suffix, 'Geographic', geo_endpoints,
                                  record)
            traffic_managers.append(geo_profile)

        return traffic_managers
Example #5
0
    def _data_for_dynamic(self, azrecord):
        default = set()
        pools = defaultdict(lambda: {'fallback': None, 'values': []})
        rules = []

        # top level profile
        root_profile = self._get_tm_profile_by_id(azrecord.target_resource.id)
        if root_profile.traffic_routing_method != 'Geographic':
            # This record does not use geo fencing, so we skip the Geographic
            # profile hop; let's pretend to be a geo-profile's only endpoint
            geo_ep = Endpoint(target_resource_id=root_profile.id)
            geo_ep.target_resource = root_profile
            endpoints = [geo_ep]
        else:
            endpoints = root_profile.endpoints

        for geo_ep in endpoints:
            rule = {}

            # resolve list of regions
            geo_map = list(geo_ep.geo_mapping or [])
            if geo_map and geo_map != ['WORLD']:
                if 'GEO-ME' in geo_map:
                    # Azure treats Middle East as a separate group, but
                    # its part of Asia in octoDNS, so we need to remove GEO-ME
                    # if GEO-AS is also in the list
                    # Throw exception otherwise, it should not happen if the
                    # profile was generated by octoDNS
                    if 'GEO-AS' not in geo_map:
                        msg = 'Profile={} for record {}: '.format(
                            root_profile.name, azrecord.fqdn)
                        msg += 'Middle East (GEO-ME) is not supported by ' + \
                               'octoDNS. It needs to be either paired ' + \
                               'with Asia (GEO-AS) or expanded into ' + \
                               'individual list of countries.'
                        raise AzureException(msg)
                    geo_map.remove('GEO-ME')
                geos = rule.setdefault('geos', [])
                for code in geo_map:
                    if code.startswith('GEO-'):
                        # continent
                        if code == 'GEO-AP':
                            # Azure uses Australia/Pacific (AP) instead of
                            # Oceania https://docs.microsoft.com/en-us/azure/
                            #                                traffic-manager/
                            #              traffic-manager-geographic-regions
                            geos.append('OC')
                        else:
                            geos.append(code[len('GEO-'):])
                    elif '-' in code:
                        # state
                        country, province = code.split('-', 1)
                        country = GeoCodes.country_to_code(country)
                        geos.append('{}-{}'.format(country, province))
                    else:
                        # country
                        geos.append(GeoCodes.country_to_code(code))

            # build fallback chain from second level priority profile
            if geo_ep.target_resource_id:
                target = geo_ep.target_resource
                if target.traffic_routing_method == 'Priority':
                    rule_endpoints = target.endpoints
                    rule_endpoints.sort(key=lambda e: e.priority)
                else:
                    # Weighted
                    geo_ep.name = target.endpoints[0].name.split('--', 1)[0]
                    rule_endpoints = [geo_ep]
            else:
                # this geo directly points to the default, so we skip the
                # Priority profile hop and directly use an external endpoint;
                # let's pretend to be a Priority profile's only endpoint
                rule_endpoints = [geo_ep]

            pool = None
            for rule_ep in rule_endpoints:
                pool_name = rule_ep.name

                # last/default pool
                if pool_name.endswith('--default--'):
                    default.add(rule_ep.target)
                    if pool_name == '--default--':
                        # this should be the last one, so let's break here
                        break
                    # last pool is a single value pool and its value is same
                    # as record's default value
                    pool_name = pool_name[:-len('--default--')]

                # set first priority endpoint as the rule's primary pool
                if 'pool' not in rule:
                    rule['pool'] = pool_name

                if pool:
                    # set current pool as fallback of the previous pool
                    pool['fallback'] = pool_name

                if pool_name in pools:
                    # we've already populated the pool
                    continue

                # populate the pool from Weighted profile
                # these should be leaf node entries with no further nesting
                pool = pools[pool_name]
                endpoints = []

                if rule_ep.target_resource_id:
                    # third (and last) level weighted RR profile
                    endpoints = rule_ep.target_resource.endpoints
                else:
                    # single-value pool, so we skip the Weighted profile hop
                    # and directly use an external endpoint; let's pretend to
                    # be a Weighted profile's only endpoint
                    endpoints = [rule_ep]

                for pool_ep in endpoints:
                    val = pool_ep.target
                    pool['values'].append({
                        'value': _check_endswith_dot(val),
                        'weight': pool_ep.weight or 1,
                    })
                    if pool_ep.name.endswith('--default--'):
                        default.add(val)

            rules.append(rule)

        # Order and convert to a list
        default = sorted(default)

        data = {
            'dynamic': {
                'pools': pools,
                'rules': rules,
            },
            'value': _check_endswith_dot(default[0]),
        }

        return data
Example #6
0
    def _generate_traffic_managers(self, record):
        traffic_managers = []
        pools = record.dynamic.pools

        tm_suffix = _traffic_manager_suffix(record)
        profile = self._generate_tm_profile

        geo_endpoints = []

        for rule in record.dynamic.rules:
            pool_name = rule.data['pool']
            rule_endpoints = []
            priority = 1

            while pool_name:
                # iterate until we reach end of fallback chain
                pool = pools[pool_name].data
                profile_name = 'pool-{}--{}'.format(pool_name, tm_suffix)
                if len(pool['values']) > 1:
                    # create Weighted profile for multi-value pool
                    endpoints = []
                    for val in pool['values']:
                        target = val['value']
                        # strip trailing dot from CNAME value
                        target = target[:-1]
                        endpoints.append(Endpoint(
                            name=target,
                            target=target,
                            weight=val.get('weight', 1),
                        ))
                    pool_profile = profile(profile_name, 'Weighted', endpoints,
                                           record)
                    traffic_managers.append(pool_profile)

                    # append pool to endpoint list of fallback rule profile
                    rule_endpoints.append(Endpoint(
                        name=pool_name,
                        target_resource_id=pool_profile.id,
                        priority=priority,
                    ))
                else:
                    # add single-value pool as an external endpoint
                    target = pool['values'][0]['value'][:-1]
                    rule_endpoints.append(Endpoint(
                        name=pool_name,
                        target=target,
                        priority=priority,
                    ))

                priority += 1
                pool_name = pool.get('fallback')

            # append default profile to the end
            rule_endpoints.append(Endpoint(
                name='--default--',
                target=record.value[:-1],
                priority=priority,
            ))
            # create rule profile with fallback chain
            rule_profile_name = 'rule-{}--{}'.format(rule.data['pool'],
                                                     tm_suffix)
            rule_profile = profile(rule_profile_name, 'Priority',
                                   rule_endpoints, record)
            traffic_managers.append(rule_profile)

            # append rule profile to top-level geo profile
            rule_geos = rule.data.get('geos', [])
            geos = []
            if len(rule_geos) > 0:
                for geo in rule_geos:
                    if '-' in geo:
                        # country or state
                        geos.append(geo.split('-', 1)[-1])
                    else:
                        # continent
                        if geo == 'AS':
                            # Middle East is part of Asia in octoDNS, but
                            # Azure treats it as a separate "group", so let's
                            # add it in the list of geo mappings. We will drop
                            # it when we later parse the list of regions.
                            geos.append('GEO-ME')
                        elif geo == 'OC':
                            # Azure uses Australia/Pacific (AP) instead of
                            # Oceania
                            geo = 'AP'

                        geos.append('GEO-{}'.format(geo))
            else:
                geos.append('WORLD')
            geo_endpoints.append(Endpoint(
                name='rule-{}'.format(rule.data['pool']),
                target_resource_id=rule_profile.id,
                geo_mapping=geos,
            ))

        geo_profile = profile(tm_suffix, 'Geographic', geo_endpoints, record)
        traffic_managers.append(geo_profile)

        return traffic_managers
    def create_or_update_traffic_manager_profile(self, update):
        '''
        Create or update a Traffic Manager profile.
        :param name: name of a traffic  manager
        :return: traffic manage object
        '''
        self.log('Creating or updating Traffic Manager {0}'.format(self.name))
        try:
            # Create MonitorConfig
            monitor_config = MonitorConfig(
                protocol=self.monitor_config.get('protocol', 'HTTP'),
                port=self.monitor_config.get('port', 80),
                path=self.monitor_config.get('path', '/'),
                timeout_in_seconds=self.monitor_config.get(
                    'timeout_in_seconds', 10),
                interval_in_seconds=self.monitor_config.get(
                    'interval_in_seconds', 30),
                tolerated_number_of_failures=self.monitor_config.get(
                    'tolerated_number_of_failures', 3))
            # Create DnsConfig
            dns_config = DnsConfig(relative_name=self.dns_config.get(
                'relative_name', self.name),
                                   ttl=self.dns_config.get('ttl', 60))

            # Create Endpoints
            endpoints = []
            for end in self.endpoints:
                endpoint_instance = Endpoint(
                    name=end.get('name'),
                    type=end.get('type'),
                    target=end.get('target'),
                    endpoint_status=end.get('endpoint_status', 'Enabled'),
                    weight=end.get('weight'),
                    priority=end.get('priority'),
                    target_resource_id=end.get('target_resource_id'),
                    endpoint_location=end.get('endpoint_location'),
                    min_child_endpoints=end.get('min_child_endpoints'),
                    geo_mapping=end.get('geo_mapping'))
                endpoints.append(endpoint_instance)

            profile = Profile(
                tags=self.tags,
                location="global",
                profile_status=self.profile_status,
                traffic_routing_method=self.traffic_routing_method,
                dns_config=dns_config,
                monitor_config=monitor_config,
                endpoints=endpoints)
            return self.trafficmanager_client.profiles.create_or_update(
                self.resource_group, self.name, profile).as_dict()
        except CloudError as cloud_error:
            if update:
                self.fail(
                    'Error Updating the Traffic Manager: {0}. {1}'.format(
                        self.name, str(cloud_error)))
            else:
                self.fail(
                    'Error Creating the Traffic Manager: {0}. {1}'.format(
                        self.name, str(cloud_error)))
        except Exception as exc:
            self.fail('Error retrieving Traffic Manager {0} - {1}'.format(
                self.name, str(exc)))