Source code for dyn.tm.services.dsf

# -*- coding: utf-8 -*-
"""This module contains wrappers for interfacing with every element of a Traffic
Director (DSF) service.
"""
import dyn.tm.zones
from ..utils import APIList, Active
from ..errors import DynectInvalidArgumentError
from ..records import *
from ..session import DynectSession
from ...compat import force_unicode
from ..accounts import Notifier


__author__ = 'jnappi'
__all__ = ['get_all_dsf_services', 'get_all_record_sets','get_all_failover_chains',
           'get_all_response_pools', 'get_all_rulesets', 'get_all_dsf_monitors',
           'get_all_records', 'get_all_notifiers', 'DSFARecord', 'DSFSSHFPRecord',
           'DSFNotifier',
           'DSFAAAARecord', 'DSFALIASRecord', 'DSFCERTRecord', 'DSFCNAMERecord',
           'DSFDHCIDRecord', 'DSFDNAMERecord', 'DSFDNSKEYRecord', 'DSFDSRecord',
           'DSFKEYRecord', 'DSFKXRecord', 'DSFLOCRecord', 'DSFIPSECKEYRecord',
           'DSFMXRecord', 'DSFNAPTRRecord', 'DSFPTRRecord', 'DSFPXRecord',
           'DSFNSAPRecord', 'DSFRPRecord', 'DSFNSRecord', 'DSFSPFRecord',
           'DSFSRVRecord', 'DSFTXTRecord', 'DSFRecordSet', 'DSFFailoverChain',
           'DSFResponsePool', 'DSFRuleset', 'DSFMonitorEndpoint', 'DSFMonitor',
           'TrafficDirector']

[docs]def get_all_dsf_services(): """:return: A ``list`` of :class:`TrafficDirector` Services""" uri = '/DSF/' api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) directors = [] for dsf in response['data']: directors.append(TrafficDirector(None, api=False, **dsf)) return directors
[docs]def get_all_notifiers(): """:return: A ``list`` of :class:`DSFNotifier` Services""" uri = '/Notifier/' api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) notifiers = [] for notify in response['data']: notifiers.append(DSFNotifier(None, api=False, **notify)) return notifiers
[docs]def get_all_records(service): """ :param service: a dsf_id string, or :class:`TrafficDirector` :return: A ``list`` of :class:`DSFRecord`s from the passed in `service` Warning! This query may take a long time to run with services with many records! """ _service_id = _checkType(service) uri = '/DSFRecord/{}/'.format(_service_id) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) record_ids = [record['dsf_record_id'] for record in response['data']] records = list() for record_id in record_ids: uri = '/DSFRecord/{}/{}'.format(_service_id, record_id) response = DynectSession.get_session().execute(uri, 'GET', api_args) records += _constructor(response['data']) return records
[docs]def get_all_record_sets(service): """:param service: a dsf_id string, or :class:`TrafficDirector` :return: A ``list`` of :class:`DSFRecordSets` from the passed in `service`""" _service_id = _checkType(service) uri = '/DSFRecordSet/{}/'.format(_service_id) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) recordSets = list() for pool in response['data']: recordSets.append(DSFRecordSet(pool.pop('rdata_class'), api=False, **pool)) return recordSets
[docs]def get_all_failover_chains(service): """:param service: a dsf_id string, or :class:`TrafficDirector` :return: A ``list`` of :class:`DSFFailoverChains` from the passed in `service`""" _service_id = _checkType(service) uri = '/DSFRecordSetFailoverChain/{}/'.format(_service_id) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) failoverChains = list() for pool in response['data']: failoverChains.append(DSFFailoverChain(pool.pop('label'), api=False, **pool)) return failoverChains
[docs]def get_all_response_pools(service): """:param service: a dsf_id string, or :class:`TrafficDirector` :return: A ``list`` of :class:`DSFResponsePools` from the passed in `service`""" _service_id = _checkType(service) uri = '/DSFResponsePool/{}/'.format(_service_id) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) responsePools = list() for pool in response['data']: responsePools.append(DSFResponsePool(pool.pop('label'), api=False, **pool)) return responsePools
[docs]def get_all_rulesets(service): """:param service: a dsf_id string, or :class:`TrafficDirector` :return: A ``list`` of :class:`DSFRulesets` from the passed in `service`""" _service_id = _checkType(service) uri = '/DSFRuleset/{}/'.format(_service_id) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) ruleset = list() for rule in response['data']: ruleset.append(DSFRuleset(rule.pop('label'), api=False, **rule)) return ruleset
[docs]def get_all_dsf_monitors(): """:return: A ``list`` of :class:`DSFMonitor` Services""" uri = '/DSFMonitor/' api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) mons = [] for dsf in response['data']: mons.append(DSFMonitor(api=False, **dsf)) return mons
def _checkType(service): if isinstance(service, TrafficDirector): _service_id = service.service_id elif type(service) is str or type(service) is unicode: _service_id = service else: raise Exception('Value must be string, or TrafficDirector Object') return _service_id def _constructor(record): returnRecords = [] constructors = {'a': DSFARecord, 'aaaa': DSFAAAARecord, 'alias': DSFALIASRecord, 'cert': DSFCERTRecord, 'cname': DSFCNAMERecord, 'dhcid': DSFDHCIDRecord, 'dname': DSFDNAMERecord, 'dnskey': DSFDNSKEYRecord, 'ds': DSFDSRecord, 'key': DSFKEYRecord, 'kx': DSFKXRecord, 'loc': DSFLOCRecord, 'ipseckey': DSFIPSECKEYRecord, 'mx': DSFMXRecord, 'naptr': DSFNAPTRRecord, 'ptr': DSFPTRRecord, 'px': DSFPXRecord, 'nsap': DSFNSAPRecord, 'rp': DSFRPRecord, 'ns': DSFNSRecord, 'spf': DSFSPFRecord, 'srv': DSFSRVRecord, 'txt': DSFTXTRecord, 'sshfp': DSFSSHFPRecord} rec_type = record['rdata_class'].lower() constructor = constructors[rec_type] rdata_key = 'rdata_{}'.format(rec_type) kws = ('ttl', 'label', 'weight', 'automation', 'endpoints', 'endpoint_up_count', 'eligible', 'dsf_record_id', 'dsf_record_set_id', 'status', 'torpidity', 'service_id') for data in record['rdata']: record_data = data['data'][rdata_key] for kw in kws: record_data[kw] = record[kw] if constructor is DSFSRVRecord: record_data['rr_weight'] = record_data.pop('weight') returnRecords.append(constructor(**record_data)) return returnRecords class _DSFRecord(object): """Super Class for DSF Records.""" def __init__(self, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`_DSFRecord` object. :param label: A unique label for this :class:`DSFRecord` :param weight: Weight for this :class:`DSFRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ self.valid_automation = ('auto', 'auto_down', 'manual') self._label = label self._weight = weight if automation not in self.valid_automation: raise DynectInvalidArgumentError('automation', automation, self.valid_automation) self._automation = automation self._endpoints = endpoints self._endpoint_up_count = endpoint_up_count self._eligible = eligible self._service_id = self._dsf_record_set_id = self.uri = None self._dsf_record_id = self._status = None self._implicitPublish = True for key, val in kwargs.items(): setattr(self, '_' + key, val) def _post(self, dsf_id, record_set_id, publish=True): """Create a new :class:`DSFRecord` on the DynECT System :param dsf_id: The unique system id for the DSF service associated with this :class:`DSFRecord` :param record_set_id: The unique system id for the record set associated with this :class:`DSFRecord` """ self._service_id = dsf_id self._record_set_id = record_set_id self.uri = '/DSFRecord/{}/{}/'.format(self._service_id, self._record_set_id) api_args = {} api_args = self.to_json(skip_svc=True) if publish: api_args['publish'] = 'Y' response = DynectSession.get_session().execute(self.uri, 'POST', api_args) self._build(response['data']) def _get(self, dsf_id, dsf_record_id): """Get an existing :class:`DSFRecord` from the DynECT System :param dsf_id: The unique system id for the DSF service associated with this :class:`DSFRecord` :param dsf_record_id: The unique system id for the record set associated with this :class:`DSFRecord` """ self._service_id = dsf_id self._dsf_record_id = dsf_record_id self.uri = '/DSFRecord/{}/{}/'.format(self._service_id, self._dsf_record_id) api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ record_rdata = '{}_rdata'.format(self._record_type.replace('Record','').replace('DSF','').lower()) new_api_args = {'rdata': {record_rdata: api_args['rdata']}} if not self._record_type.endswith('Record'): self._record_type += 'Record' if publish and self._implicitPublish: new_api_args['publish'] = 'Y' self.uri = 'DSFRecord/{}/{}'.format(self._service_id, self._dsf_record_id) response = DynectSession.get_session().execute(self.uri, 'PUT', new_api_args) self._build(response['data']) def _update(self, api_args, publish=True): """API call to update non superclass record type parameters :param api_args: arguments to be pased to the API call """ if publish and self._implicitPublish: api_args['publish'] = 'Y' self.uri = 'DSFRecord/{}/{}'.format(self._service_id, self._dsf_record_id) response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): """Private build method :param data: API Response data """ for key, val in data.items(): if key == 'rdata': for rdata in val: blah = type(rdata) if isinstance(rdata, dict): for rdatas, rdata_data in rdata.items(): #necessary due to unicode! try: for rdata_type, data_value in rdata_data.items(): if rdata_type == 'rdata_{}'.format(self._rdata_class.lower()): for attribute, attrib_value in data_value.items(): setattr(self, '_' + attribute, attrib_value) except: pass else: setattr(self, '_' + key, val) def publish(self): """Publish changes to :class:`TrafficDirector`.""" uri = '/DSF/{}/'.format(self._service_id) api_args = {'publish':'Y'} DynectSession.get_session().execute(uri, 'PUT', api_args) self.refresh() def refresh(self): """Pulls data down from Dynect System and repopulates :class:`DSFRecord` """ self._get(self._service_id, self._dsf_record_id) def add_to_record_set(self, record_set, service = None, publish = True): """ Creates and links this :class:`DSFRecord` to passed in :class:`DSFRecordSet` Object :param record_set: Can either be the _dsf_record_set_id or a :class:`DSFRecordSet` Object. :param service: Only necessary if record_set is passed in as a string. This can be a :class:`TrafficDirector` Object. or the _service_id :param publish: Publish on execution (Default = True) """ if self._dsf_record_id: raise Exception('The record already exists in the system!') if isinstance(record_set, DSFRecordSet): _record_set_id = record_set._dsf_record_set_id _service_id = record_set._service_id elif type(record_set) is str or type(record_set) is unicode: if service is None: raise Exception('When record_set as a string, you must provide the service_id as service=') _record_set_id = record_set else: raise Exception('Could not make sense of Record Set Type') if service: _service_id = _checkType(service) self._post(_service_id, _record_set_id, publish=True ) @property def dsf_id(self): """The unique system id of the :class:`TrafficDirector` This :class:`DSFRecord` is attached to """ return self._service_id @property def record_id(self): """The unique system id for this :class:`DSFRecord` """ return self._dsf_record_id @property def record_set_id(self): """The unique system id of the :class:`DSFRecordSet` This :class:`DSFRecord` is attached to """ return self._record_set_id @property def label(self): """A unique label for this :class:`DSFRecord`""" return self._label @label.setter def label(self, value): api_args = {'label': value} self._update(api_args) if self._implicitPublish: self._label = value @property def weight(self): """Weight for this :class:`DSFRecord`""" return self._weight @weight.setter def weight(self, value): api_args = {'weight': value} self._update(api_args) if self._implicitPublish: self._weight = value @property def automation(self): """Defines how eligiblity can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' """ return self._automation @automation.setter def automation(self, value): api_args = {'automation': value} self._update(api_args) if self._implicitPublish: self._automation = value @property def endpoints(self): """Endpoints are used to determine status, torpidity, and eligible in response to monitor data """ return self._endpoints @endpoints.setter def endpoints(self, value): api_args = {'endpoints': value} self._update(api_args) if self._implicitPublish: self._endpoints = value @property def endpoint_up_count(self): """Number of endpoints that must be up for the Record status to be 'up' """ return self._endpoint_up_count @endpoint_up_count.setter def endpoint_up_count(self, value): api_args = {'endpoint_up_count': value} self._update(api_args) if self._implicitPublish: self._endpoint_up_count = value @property def eligible(self): """Indicates whether or not the Record can be served""" return self._eligible @eligible.setter def eligible(self, value): api_args = {'eligible': value} self._update(api_args) if self._implicitPublish: self._eligible = value @property def status(self): """Status of Record""" self.refresh() return self._status def to_json(self, svc_id=None, skip_svc=False): """Convert this DSFRecord to a json blob""" if self._service_id and not svc_id: svc_id = self._service_id json = {'label': self._label, 'weight': self._weight, 'automation': self._automation, 'endpoints': self._endpoints, 'eligible': self._eligible, 'endpoint_up_count': self._endpoint_up_count} json_blob = {x: json[x] for x in json if json[x] is not None} if hasattr(self, '_record_type'): rdata = self.rdata() outer_key = list(rdata.keys())[0] inner_data = rdata[outer_key] real_data = {x: inner_data[x] for x in inner_data if x not in json_blob and x not in self.__dict__ and x[1:] not in self.__dict__ and inner_data[x] is not None and x != 'record_set_id' and x != 'service_id' and x != 'implicitPublish'} json_blob['rdata'] = {outer_key: real_data} if svc_id and not skip_svc: json_blob['service_id'] = svc_id return json_blob @property def implicitPublish(self): "Toggle for this specific :class:`DSFRecord` for turning on and off implicit Publishing for record Updates." return self._implicitPublish @implicitPublish.setter def implicitPublish(self, value): if value != True and value != False: raise Exception('Value must be True or False') self._implicitPublish = value def delete(self): """Delete this :class:`DSFRecord`""" api_args = {'publish': 'Y'} uri = '/DSFRecord/{}/{}'.format(self._service_id,self._dsf_record_id) DynectSession.get_session().execute(uri, 'DELETE', api_args)
[docs]class DSFARecord(_DSFRecord, ARecord): """An :class:`ARecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, address, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFARecord` object :param address: IPv4 address for the record :param ttl: TTL for this record :param label: A unique label for this :class:`DSFARecord` :param weight: Weight for this :class:`DSFARecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ ARecord.__init__(self, None, None, address=address, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFARecord'
[docs]class DSFAAAARecord(_DSFRecord, AAAARecord): """An :class:`AAAARecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, address, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFAAAARecord` object :param address: IPv6 address for the record :param ttl: TTL for this record :param label: A unique label for this :class:`DSFAAAARecord` :param weight: Weight for this :class:`DSFAAAARecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ AAAARecord.__init__(self, None, None, address=address, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFAAAARecord'
[docs]class DSFALIASRecord(_DSFRecord, ALIASRecord): """An :class:`AliasRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, alias, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFALIASRecord` object :param alias: alias target name :param ttl: TTL for this record :param label: A unique label for this :class:`DSFALIASRecord` :param weight: Weight for this :class:`DSFALIASRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ ALIASRecord.__init__(self, None, None, alias=alias, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFALIASRecord'
[docs]class DSFCERTRecord(_DSFRecord, CERTRecord): """An :class:`CERTRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, format, tag, algorithm, certificate, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFCERTRecord` object :param format: Numeric value for the certificate type :param tag: Numeric value for the public key certificate :param algorithm: Public key algorithm number used to generate the certificate :param certificate: The public key certificate :param ttl: TTL for this record :param label: A unique label for this :class:`DSFCERTRecord` :param weight: Weight for this :class:`DSFCERTRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ CERTRecord.__init__(self, None, None, format=format, tag=tag, algorithm=algorithm, certificate=certificate, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFCERTRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['format', 'tag', 'algorithm', 'certificate'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFCERTRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFCNAMERecord(_DSFRecord, CNAMERecord): """An :class:`CNAMERecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, cname, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFCNAMERecord` object :param cname: Hostname :param ttl: TTL for this record :param label: A unique label for this :class:`DSFCNAMERecord` :param weight: Weight for this :class:`DSFCNAMERecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ CNAMERecord.__init__(self, None, None, cname=cname, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFCNAMERecord'
[docs]class DSFDHCIDRecord(_DSFRecord, DHCIDRecord): """An :class:`DHCIDRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, digest, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFDHCIDRecord` object :param digest: Base-64 encoded digest of DHCP data :param ttl: TTL for this record :param label: A unique label for this :class:`DSFDHCIDRecord` :param weight: Weight for this :class:`DSFDHCIDRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ DHCIDRecord.__init__(self, None, None, digest=digest, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFDHCIDRecord'
[docs]class DSFDNAMERecord(_DSFRecord, DNAMERecord): """An :class:`DNAMERecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, dname, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFDNAMERecord` object :param dname: Target Hostname :param ttl: TTL for this record :param label: A unique label for this :class:`DSFDNAMERecord` :param weight: Weight for this :class:`DSFDNAMERecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ DNAMERecord.__init__(self, None, None, dname=dname, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFDNAMERecord'
[docs]class DSFDNSKEYRecord(_DSFRecord, DNSKEYRecord): """An :class:`DNSKEYRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, protocol, public_key, algorithm=5, flags=256, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFDNSKEYRecord` object :param protocol: Numeric value for protocol :param public_key: The public key for the DNSSEC signed zone :param algorithm: Numeric value representing the public key encryption algorithm which will sign the zone. Must be one of 1 (RSA-MD5), 2 (Diffie-Hellman), 3 (DSA/SHA-1), 4 (Elliptic Curve), or 5 (RSA-SHA-1) :param flags: Numeric value confirming this is the zone's DNSKEY :param ttl: TTL for this record :param label: A unique label for this :class:`DSFDNSKEYRecord` :param weight: Weight for this :class:`DSFDNSKEYRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ DNSKEYRecord.__init__(self, None, None, protocol=protocol, public_key=public_key, algorithm=algorithm, flags=flags, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFDNSKEYRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['flags', 'algorithm', 'protocol', 'public_key'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFDNSKEYRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFDSRecord(_DSFRecord, DSRecord): """An :class:`DSRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, digest, keytag, algorithm=5, digtype=1, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFDSRecord` object :param digest: The digest in hexadecimal form. 20-byte, hexadecimal-encoded, one-way hash of the DNSKEY record surrounded by parenthesis characters '(' & ')' :param keytag: The digest mechanism to use to verify the digest :param algorithm: Numeric value representing the public key encryption algorithm which will sign the zone. Must be one of 1 (RSA-MD5), 2 (Diffie-Hellman), 3 (DSA/SHA-1), 4 (Elliptic Curve), or 5 (RSA-SHA-1) :param digtype: the digest mechanism to use to verify the digest. Valid values are SHA1, SHA256 :param ttl: TTL for this record :param label: A unique label for this :class:`DSFDSRecord` :param weight: Weight for this :class:`DSFDSRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ DSRecord.__init__(self, None, None, digest=digest, keytag=keytag, algorithm=algorithm, digtype=digtype, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFDSRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['digest', 'algorithm', 'digtype', 'key_tag'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFDSRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFKEYRecord(_DSFRecord, KEYRecord): """An :class:`KEYRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, algorithm, flags, protocol, public_key, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFKEYRecord` object :param algorithm: Numeric value representing the public key encryption algorithm which will sign the zone. Must be one of 1 (RSA-MD5), 2 (Diffie-Hellman), 3 (DSA/SHA-1), 4 (Elliptic Curve), or 5 (RSA-SHA-1) :param flags: See RFC 2535 for information on KEY record flags :param protocol: Numeric identifier of the protocol for this KEY record :param public_key: The public key for this record :param ttl: TTL for this record :param label: A unique label for this :class:`DSFKEYRecord` :param weight: Weight for this :class:`DSFKEYRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ KEYRecord.__init__(self, None, None, algorithm=algorithm, flags=flags, protocol=protocol, public_key=public_key, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFKEYRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['flags', 'algorithm', 'protocol', 'public_key'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFKEYRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFKXRecord(_DSFRecord, KXRecord): """An :class:`KXRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, exchange, preference, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFKXRecord` object :param exchange: Hostname that will act as the Key Exchanger. The hostname must have a :class:`CNAMERecord`, an :class:`ARecord` and/or an :class:`AAAARecord` associated with it :param preference: Numeric value for priority usage. Lower value takes precedence over higher value where two records of the same type exist on the zone/node :param ttl: TTL for this record :param label: A unique label for this :class:`DSFKXRecord` :param weight: Weight for this :class:`DSFKXRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ KXRecord.__init__(self, None, None, exchange=exchange, preference=preference, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFKXRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['preference', 'exchange',] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFKXRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFLOCRecord(_DSFRecord, LOCRecord): """An :class:`LOCRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, altitude, latitude, longitude, horiz_pre=10000, size=1, vert_pre=10, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFLOCRecord` object :param altitude: Measured in meters above sea level :param horiz_pre: :param latitude: Measured in degrees, minutes, and seconds with N/S indicator for North and South :param longitude: Measured in degrees, minutes, and seconds with E/W indicator for East and West :param size: :param version: :param vert_pre: :param ttl: TTL for this record :param label: A unique label for this :class:`DSFLOCRecord` :param weight: Weight for this :class:`DSFLOCRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ LOCRecord.__init__(self, None, None, altitude=altitude, latitude=latitude, longitude=longitude, horiz_pre=horiz_pre, size=size, vert_pre=vert_pre, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFLOCRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['altitude', 'horiz_pre', 'latitude', 'longitude', 'size', 'version', 'vert_pre'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFLOCRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFIPSECKEYRecord(_DSFRecord, IPSECKEYRecord): """An :class:`IPSECKEYRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, precedence, gatetype, algorithm, gateway, public_key, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFIPSECKEYRecord` object :param precedence: Indicates priority among multiple IPSECKEYS. Lower numbers are higher priority :param gatetype: Gateway type. Must be one of 0, 1, 2, or 3 :param algorithm: Public key's cryptographic algorithm and format. Must be one of 0, 1, or 2 :param gateway: Gateway used to create IPsec tunnel. Based on Gateway type :param public_key: Base64 encoding of the public key. Whitespace is allowed :param ttl: TTL for this record :param label: A unique label for this :class:`DSFIPSECKEYRecord` :param weight: Weight for this :class:`DSFIPSECKEYRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ IPSECKEYRecord.__init__(self, None, None, precedence=precedence, gatetype=gatetype, algorithm=algorithm, gateway=gateway, public_key=public_key, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFIPSECKEYRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['precedence', 'gatetype', 'gateway', 'public_key',] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFIPSECKEYRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFMXRecord(_DSFRecord, MXRecord): """An :class:`MXRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, exchange, preference=10, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFMXRecord` object :param exchange: Hostname of the server responsible for accepting mail messages in the zone :param preference: Numeric value for priority usage. Lower value takes precedence over higher value where two records of the same type exist on the zone/node. :param ttl: TTL for this record :param label: A unique label for this :class:`DSFMXRecord` :param weight: Weight for this :class:`DSFMXRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ MXRecord.__init__(self, None, None, exchange=exchange, preference=preference, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFMXRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['exchange', 'preference'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFMXRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFNAPTRRecord(_DSFRecord, NAPTRRecord): """An :class:`NAPTRRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, order, preference, services, regexp, replacement, flags='U', ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFNAPTRRecord` object :param order: Indicates the required priority for processing NAPTR records. Lowest value is used first. :param preference: Indicates priority where two or more NAPTR records have identical order values. Lowest value is used first. :param services: Always starts with "e2u+" (E.164 to URI). After the e2u+ there is a string that defines the type and optionally the subtype of the URI where this :class:`NAPTRRecord` points. :param regexp: The NAPTR record accepts regular expressions :param replacement: The next domain name to find. Only applies if this :class:`NAPTRRecord` is non-terminal. :param flags: Should be the letter "U". This indicates that this NAPTR record terminal :param ttl: TTL for this record :param label: A unique label for this :class:`DSFNAPTRRecord` :param weight: Weight for this :class:`DSFNAPTRRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ NAPTRRecord.__init__(self, None, None, order=order, preference=preference, services=services, regexp=regexp, replacement=replacement, flags=flags, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFNAPTRRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['order', 'preference', 'flags', 'services', 'regexp', 'replacement'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFNAPTRRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFPTRRecord(_DSFRecord, PTRRecord): """An :class:`PTRRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, ptrdname, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFPTRRecord` object :param ptrdname: The hostname where the IP address should be directed :param ttl: TTL for this record :param label: A unique label for this :class:`DSFPTRRecord` :param weight: Weight for this :class:`DSFPTRRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ PTRRecord.__init__(self, None, None, ptrdname=ptrdname, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFPTRRecord'
[docs]class DSFPXRecord(_DSFRecord, PXRecord): """An :class:`PXRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, preference, map822, mapx400, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFPXRecord` object :param preference: Sets priority for processing records of the same type. Lowest value is processed first. :param map822: mail hostname :param mapx400: The domain name derived from the X.400 part of MCGAM :param ttl: TTL for this record :param label: A unique label for this :class:`DSFPXRecord` :param weight: Weight for this :class:`DSFPXRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ PXRecord.__init__(self, None, None, preference=preference, map822=map822, mapx400=mapx400, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFPXRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['map822', 'preference', 'mapx400'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFPXRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFNSAPRecord(_DSFRecord, NSAPRecord): """An :class:`NSAPRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, nsap, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFNSAPRecord` object :param nsap: Hex-encoded NSAP identifier :param ttl: TTL for this record :param label: A unique label for this :class:`DSFNSAPRecord` :param weight: Weight for this :class:`DSFNSAPRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ NSAPRecord.__init__(self, None, None, nsap=nsap, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFNSAPRecord'
[docs]class DSFRPRecord(_DSFRecord, RPRecord): """An :class:`RPRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, mbox, txtdname, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFRPRecord` object :param mbox: Email address of the Responsible Person. :param txtdname: Hostname where a TXT record exists with more information on the responsible person. :param ttl: TTL for this record :param label: A unique label for this :class:`DSFRPRecord` :param weight: Weight for this :class:`DSFRPRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ RPRecord.__init__(self, None, None, mbox=mbox, txtdname=txtdname, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFRPRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['mbox', 'txtdname'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFRPRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFNSRecord(_DSFRecord, NSRecord): """An :class:`NSRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, nsdname, service_class='', ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFNSRecord` object :param nsdname: Hostname of the authoritative Nameserver for the zone :param service_class: Hostname of the authoritative Nameserver for the zone :param ttl: TTL for this record :param label: A unique label for this :class:`DSFNSRecord` :param weight: Weight for this :class:`DSFNSRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ NSRecord.__init__(self, None, None, nsdname=nsdname, service_class=service_class, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFNSRecord'
[docs]class DSFSPFRecord(_DSFRecord, SPFRecord): """An :class:`SPFRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, txtdata, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFSPFRecord` object :param txtdata: Free text containing SPF record information :param ttl: TTL for this record :param label: A unique label for this :class:`DSFSPFRecord` :param weight: Weight for this :class:`DSFSPFRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ SPFRecord.__init__(self, None, None, txtdata=txtdata, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFSPFRecord'
[docs]class DSFSRVRecord(_DSFRecord, SRVRecord): """An :class:`SRVRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, port, priority, target, rr_weight, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFSRVRecord` object :param port: Indicates the port where the service is running :param priority: Numeric value for priority usage. Lower value takes precedence over higher value where two records of the same type exist on the zone/node :param target: The domain name of a host where the service is running on the specified port :param rr_weight: Secondary prioritizing of records to serve. Records of equal priority should be served based on their weight. Higher values are served more often :param ttl: TTL for this record :param label: A unique label for this :class:`DSFSRVRecord` :param weight: Weight for this :class:`DSFSRVRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ SRVRecord.__init__(self, None, None, port=port, priority=priority, target=target, weight=rr_weight, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFSRVRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['port', 'priority', 'target', 'weight'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFSRVRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFSSHFPRecord(_DSFRecord, SSHFPRecord): """An :class:`SSHFPRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, fptype, algorithm, fingerprint, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFSSHFPRecord` object :param algorithm: Numeric value representing the public key encryption algorithm which will sign the zone. :param fptype: FingerPrint Type :param fingerprint: fingerprint value :param ttl: TTL for this record :param label: A unique label for this :class:`DSFSSHFPRecord` :param weight: Weight for this :class:`DSFSSHFPRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ SSHFPRecord.__init__(self, None, None, algorithm=algorithm, fptype=fptype, fingerprint=fingerprint, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFSSHFPRecord'
def _update_record(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ keys = ['fptype', 'fingerprint', 'algorithm'] self.refresh() for key in keys: if key not in api_args: api_args['rdata'][key] = getattr(self, key) super(DSFSSHFPRecord, self)._update_record(api_args, publish=publish)
[docs]class DSFTXTRecord(_DSFRecord, TXTRecord): """An :class:`TXTRecord` object which is able to store additional data for use by a :class:`TrafficDirector` service. """
[docs] def __init__(self, txtdata, ttl=0, label=None, weight=1, automation='auto', endpoints=None, endpoint_up_count=None, eligible=True, **kwargs): """Create a :class:`DSFTXTRecord` object :param txtdata: Plain text data to be served by this :class:`DSFTXTRecord` :param ttl: TTL for this record :param label: A unique label for this :class:`DSFTXTRecord` :param weight: Weight for this :class:`DSFTXTRecord` :param automation: Defines how eligible can be changed in response to monitoring. Must be one of 'auto', 'auto_down', or 'manual' :param endpoints: Endpoints are used to determine status, torpidity, and eligible in response to monitor data :param endpoint_up_count: Number of endpoints that must be up for the Record status to be 'up' :param eligible: Indicates whether or not the Record can be served """ TXTRecord.__init__(self, None, None, txtdata=txtdata, ttl=ttl, create=False) _DSFRecord.__init__(self, label, weight, automation, endpoints, endpoint_up_count, eligible, **kwargs) self._record_type = 'DSFTXTRecord'
[docs]class DSFRecordSet(object): """A Collection of DSFRecord Type objects belonging to a :class:`DSFFailoverChain` """
[docs] def __init__(self, rdata_class, label=None, ttl=None, automation=None, serve_count=None, fail_count=None, trouble_count=None, eligible=None, dsf_monitor_id=None, records=None, **kwargs): """Create a new :class:`DSFRecordSet` object :param rdata_class: The type of rdata represented by this :class:`DSFRecordSet` :param label: A unique label for this :class:`DSFRecordSet` :param ttl: Default TTL for :class:`DSFRecord`'s within this :class:`DSFRecordSet` :param automation: Defines how eligible can be changed in response to monitoring :param serve_count: How many Records to serve out of this :class:`DSFRecordSet` :param fail_count: The number of Records that must not be okay before this :class:`DSFRecordSet` becomes ineligible. :param trouble_count: The number of Records that must not be okay before this :class:`DSFRecordSet` becomes in trouble. :param eligible: Indicates whether or not this :class:`DSFRecordSet` can be served. :param dsf_monitor_id: The unique system id of the DSF Monitor attached to this :class:`DSFRecordSet` :param records: A list of :class:`DSFRecord`'s within this :class:`DSFRecordSet` :param kwargs: Used for manipulating additional data to be specified by the creation of other system objects. """ super(DSFRecordSet, self).__init__() self._label = label self._rdata_class = rdata_class self._ttl = ttl self._automation = automation self._serve_count = serve_count self._fail_count = fail_count self._trouble_count = trouble_count self._eligible = eligible self._dsf_monitor_id = dsf_monitor_id self._dsf_record_set_failover_chain_id = None self._implicitPublish = True if records is not None and len(records) > 0 and isinstance(records[0], dict): self._records = [] for record in records: self._records += _constructor(record) else: self._records = records or [] self.uri = self._master_line = self._rdata = self._status = None self._service_id = self._dsf_record_set_id = None for key, val in kwargs.items(): if key != 'records': setattr(self, '_' + key, val) # If dsf_id and dsf_response_pool_id were specified in kwargs if self._service_id is not None and self._dsf_record_set_id is not None: self.uri = '/DSFRecordSet/{}/{}/'.format(self._service_id, self._dsf_record_set_id)
def _post(self, service_id, publish=True): """Create a new :class:`DSFRecordSet` on the DynECT System :param dsf_id: The unique system id of the DSF service this :class:`DSFRecordSet` is attached to """ self._service_id = service_id self.uri = '/DSFRecordSet/{}'.format(self._service_id) api_args = {} api_args = self.to_json(skip_svc=True) if self._records: api_args['records'] = [record.to_json(skip_svc=True) for record in self._records] if self._dsf_record_set_failover_chain_id: api_args['dsf_record_set_failover_chain_id'] = self._dsf_record_set_failover_chain_id if publish: api_args['publish'] = 'Y' response = DynectSession.get_session().execute(self.uri, 'POST', api_args) self._build(response['data']) self.uri = '/DSFRecordSet/{}/{}/'.format(self._service_id, self._dsf_record_set_id) def _get(self, dsf_id, dsf_record_set_id): """Get an existing :class:`DSFRecordSet` from the DynECT System :param dsf_id: The unique system id of the DSF service this :class:`DSFRecordSet` is attached to :param dsf_record_set_id: The unique system id of the DSF Record Set this :class:`DSFRecordSet` is attached to """ self._service_id = dsf_id self._dsf_record_set_id = dsf_record_set_id self.uri = '/DSFRecordSet/{}/{}/'.format(self._service_id, self._dsf_record_set_id) api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args, publish = True): """Private update method""" if publish and self._implicitPublish: api_args['publish'] = 'Y' response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): """Private build method""" if data['records']: self._records = [] for key, val in data.items(): if key != 'records': setattr(self, '_' + key, val) if key == 'records': for record in val: self._records += _constructor(record) def __str__(self): str = list() str.append('RDClass: {}'.format(self.rdata_class)) str.append('Label: {}'.format(self.label)) if self._dsf_record_set_id: str.append('ID: {}'.format(self._dsf_record_set_id)) return ("<DSFRecordSet>: {}".format(', '.join(str))) __repr__ = __unicode__ = __str__
[docs] def publish(self): """Publish changes to :class:`TrafficDirector`.""" uri = '/DSF/{}/'.format(self._service_id) api_args = {'publish':'Y'} DynectSession.get_session().execute(uri, 'PUT', api_args) self.refresh()
[docs] def refresh(self): """Pulls data down from Dynect System and repopulates :class:`DSFRecordSet` """ self._get(self._service_id, self._dsf_record_set_id)
[docs] def add_to_failover_chain(self, failover_chain, service=None, publish=True): """ Creates and links this :class:`DSFRecordSet` to the passed in :class:`DSFFailoverChain` Object :param failover_chain: Can either be the _dsf_record_set_failover_chain_id or a :class:`DSFFailoverChain` Object. :param service: Only necessary is rs_chain is passed in as a string. This can be a :class:`TrafficDirector` Object. or the _service_id :param publish: Publish on execution (Default = True) """ if isinstance(failover_chain, DSFFailoverChain): _dsf_record_set_failover_chain_id = failover_chain._dsf_record_set_failover_chain_id _service_id = failover_chain._service_id elif type(failover_chain) is str or type(failover_chain) is unicode: if service is None: raise Exception('If passing failover_chain as a string, you must provide the service_id as service=') _dsf_record_set_failover_chain_id = failover_chain else: raise Exception('Could not make sense of Failover Chain Type') if service: _service_id = _checkType(service) if self._dsf_record_set_failover_chain_id: raise Exception('Records Set already attached to Failover Chain: {}.'.format( self._dsf_record_set_failover_chain_id)) self._dsf_record_set_failover_chain_id = _dsf_record_set_failover_chain_id self._post(_service_id, publish=publish)
@property def records(self): """The ``list`` of :class:`DSFRecord` types that are stored in this :class:`DSFRecordSet` """ return self._records @property def status(self): """The current status of this :class:`DSFRecordSet`""" self._get(self._service_id, self._dsf_record_set_id) return self._status @property def label(self): """A unique label for this :class:`DSFRecordSet`""" return self._label @label.setter def label(self, value): api_args = {'label': value} if self._master_line: api_args['master_line'] = self._master_line else: api_args['rdata'] = self._rdata self._update(api_args) if self._implicitPublish: self._label = value @property def rdata_class(self): """The rdata property is a read-only attribute""" return self._rdata_class @property def ttl(self): """Default TTL for :class:`DSFRecord`'s within this :class:`DSFRecordSet`""" return self._ttl @ttl.setter def ttl(self, value): api_args = {'ttl': value} if self._master_line: api_args['master_line'] = self._master_line else: api_args['rdata'] = self._rdata self._update(api_args) if self._implicitPublish: self._ttl = value @property def automation(self): """Defines how eligible can be changed in response to monitoring""" return self._automation @automation.setter def automation(self, value): api_args = {'automation': value} if self._master_line: api_args['master_line'] = self._master_line else: api_args['rdata'] = self._rdata self._update(api_args) if self._implicitPublish: self._automation = value @property def serve_count(self): """How many Records to serve out of this :class:`DSFRecordSet`""" return self._serve_count @serve_count.setter def serve_count(self, value): api_args = {'serve_count': value} if self._master_line: api_args['master_line'] = self._master_line else: api_args['rdata'] = self._rdata self._update(api_args) if self._implicitPublish: self._serve_count = value @property def fail_count(self): """The number of Records that must not be okay before this :class:`DSFRecordSet` becomes ineligible. """ return self._fail_count @fail_count.setter def fail_count(self, value): api_args = {'fail_count': value} if self._master_line: api_args['master_line'] = self._master_line else: api_args['rdata'] = self._rdata self._update(api_args) if self._implicitPublish: self._fail_count = value @property def trouble_count(self): """The number of Records that must not be okay before this :class:`DSFRecordSet` becomes in trouble. """ return self._trouble_count @trouble_count.setter def trouble_count(self, value): api_args = {'trouble_count': value} if self._master_line: api_args['master_line'] = self._master_line else: api_args['rdata'] = self._rdata self._update(api_args) if self._implicitPublish: self._trouble_count = value @property def eligible(self): """Indicates whether or not this :class:`DSFRecordSet` can be served.""" return self._eligible @eligible.setter def eligible(self, value): api_args = {'eligible': value} if self._master_line: api_args['master_line'] = self._master_line else: api_args['rdata'] = self._rdata self._update(api_args) if self._implicitPublish: self._eligible = value @property def dsf_monitor_id(self): """The unique system id of the DSF Monitor attached to this :class:`DSFRecordSet` """ return self._dsf_monitor_id @dsf_monitor_id.setter def dsf_monitor_id(self, value): """allows you to manually set the monitor_id, Legacy function for backward compatability""" api_args = {'dsf_monitor_id': value} self._update(api_args) if self._implicitPublish: self._dsf_monitor_id = value
[docs] def set_monitor(self, monitor): """ For attaching a :class:`DSFMonitor` to this record_set :param monitor: a :class:`DSFMonitor` or string of the dsf_monitor_id to attach to this record_set """ if isinstance(monitor, DSFMonitor): _monitor_id = monitor._dsf_monitor_id elif type(monitor) is str or type(monitor) is unicode: _monitor_id = monitor else: raise Exception('Could not make sense of Monitor Type') api_args = {'dsf_monitor_id': _monitor_id} self._update(api_args) self._dsf_monitor_id = _monitor_id
@property def dsf_id(self): """The unique system id of the :class:`TrafficDirector` This :class:`DSFRecordSet` is attached to """ return self._service_id @property def record_set_id(self): """The unique system id of this :class:`DSFRecordSet`""" return self._dsf_record_set_id @property def implicitPublish(self): "Toggle for this specific :class:`DSFRecordSet` for turning on and off implicit Publishing for record Updates." return self._implicitPublish @implicitPublish.setter def implicitPublish(self, value): if value != True and value != False: raise Exception('Value must be True or False') self._implicitPublish = value
[docs] def to_json(self, svc_id=None, skip_svc=False): """Convert this :class:`DSFRecordSet` to a JSON blob""" if self._service_id and not svc_id: svc_id = self._service_id json_blob = {'rdata_class': self._rdata_class} if svc_id and not skip_svc: json_blob['service_id'] = svc_id if self._label: json_blob['label'] = self._label if self._ttl: json_blob['ttl'] = self._ttl if self._automation: json_blob['automation'] = self._automation if self._serve_count: json_blob['serve_count'] = self._serve_count if self._fail_count: json_blob['fail_count'] = self._fail_count if self._trouble_count: json_blob['trouble_count'] = self._trouble_count if self._eligible: json_blob['eligible'] = self._eligible if self._dsf_monitor_id: json_blob['dsf_monitor_id'] = self._dsf_monitor_id if self._records: json_blob['records'] = [rec.to_json(svc_id) for rec in self._records] else: json_blob['records'] = [] return json_blob
[docs] def delete(self): """Delete this :class:`DSFRecordSet` from the Dynect System""" api_args = {'publish': 'Y'} DynectSession.get_session().execute(self.uri, 'DELETE', api_args)
[docs]class DSFFailoverChain(object): """docstring for DSFFailoverChain"""
[docs] def __init__(self, label=None, core=None, record_sets=None, **kwargs): """Create a :class:`DSFFailoverChain` object :param label: A unique label for this :class:`DSFFailoverChain` :param core: Indicates whether or not the contained :class:`DSFRecordSets` are core record sets :param record_sets: A list of :class:`DSFRecordSet`'s for this :class:`DSFFailoverChain` """ super(DSFFailoverChain, self).__init__() self._label = label self._core = core self._implicitPublish = True if isinstance(record_sets, list) and len(record_sets) > 0 and \ isinstance(record_sets[0], dict): # Clear record sets self._record_sets = [] # Create new record set objects for record_set in record_sets: if 'service_id' in record_set and \ record_set['service_id'] == '': record_set['service_id'] = kwargs['service_id'] self._record_sets.append(DSFRecordSet(**record_set)) else: self._record_sets = record_sets self._service_id = self._dsf_response_pool_id = self.uri = None self._dsf_record_set_failover_chain_id = None for key, val in kwargs.items(): setattr(self, '_' + key, val) # If dsf_id and dsf_response_pool_id were specified in kwargs if self._service_id is not None and self._dsf_response_pool_id is not None: r_pid = self._dsf_record_set_failover_chain_id self.uri = '/DSFRecordSetFailoverChain/{}/{}/'.format(self._service_id, r_pid)
def _post(self, dsf_id, dsf_response_pool_id, publish=True): """Create a new :class:`DSFFailoverChain` on the Dynect System :param dsf_id: The unique system id of the DSF service this :class:`DSFFailoverChain` is attached to :param dsf_response_pool_id: The unique system is of the DSF response pool this :class:`DSFFailoverChain` is attached to """ self._service_id = dsf_id self._dsf_response_pool_id = dsf_response_pool_id self.uri = '/DSFRecordSetFailoverChain/{}/{}/'.format(self._service_id, self._dsf_response_pool_id) api_args = {} if self._label: api_args['label'] = self._label if self._core: api_args['core'] = self._core if self._record_sets: api_args['record_sets'] = [set.to_json(skip_svc=True) for set in self._record_sets] if publish: api_args['publish'] = 'Y' response = DynectSession.get_session().execute(self.uri, 'POST', api_args) self._build(response['data']) def _get(self, dsf_id, dsf_record_set_failover_chain_id): """Retrieve an existing :class:`DSFFailoverChain` from the Dynect System :param dsf_id: The unique system id of the DSF service this :class:`DSFFailoverChain` is attached to :param dsf_record_set_failover_chain_id: The unique system id of this :class:`DSFFailoverChain`. """ self._service_id = dsf_id self._dsf_record_set_failover_chain_id = dsf_record_set_failover_chain_id self.uri = '/DSFRecordSetFailoverChain/{}/{}/'.format(self._service_id, self._dsf_record_set_failover_chain_id) api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args, publish=True): """API call to update non superclass record type parameters :param api_args: arguments to be pased to the API call """ if publish and self._implicitPublish: api_args['publish'] = 'Y' self.uri = 'DSFRecordSetFailoverChain/{}/{}'.format(self._service_id, self._dsf_record_set_failover_chain_id) response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): """Private build method""" if data['record_sets']: self._record_sets = [] for key, val in data.items(): if key != 'record_sets': setattr(self, '_' + key, val) if key == 'record_sets': for record_set in val: self._record_sets.append(DSFRecordSet(**record_set)) def __str__(self): str = list() str.append('Label: {}'.format(self.label)) if self._dsf_record_set_failover_chain_id: str.append('ID: {}'.format(self._dsf_record_set_failover_chain_id)) return ("<DSFFailoverChain>: {}".format(', '.join(str))) __repr__ = __unicode__ = __str__
[docs] def publish(self): """Publish changes to :class:`TrafficDirector`.""" uri = '/DSF/{}/'.format(self._service_id) api_args = {'publish':'Y'} DynectSession.get_session().execute(uri, 'PUT', api_args) self.refresh()
[docs] def refresh(self): """Pulls data down from Dynect System and repopulates :class:`DSFFailoverChain` """ self._get(self._service_id, self._dsf_record_set_failover_chain_id)
[docs] def add_to_response_pool(self, response_pool, service=None, publish=True): """ Creates and Adds this :class:`DSFFailoverChain` to a :class:`TrafficDirector` service. :param response_pool: Can either be the response_pool_id or a :class:`DSFResponsePool` Object. :param service: Only necessary when response_pool is passed as a string. Can either be the service_id or a :class:`TrafficDirector` Object :param publish: Publish on execution (Default = True) """ if isinstance(response_pool, DSFResponsePool): _response_pool_id = response_pool._dsf_response_pool_id _service_id = response_pool._service_id elif type(response_pool) is str or type(response_pool) is unicode: if service is None: raise Exception('If passing response_pool as a string, you must provide the service_id as service=') _response_pool_id = response_pool else: raise Exception('Could not make sense of Response Pool Type') if service: _service_id = _checkType(service) if self._dsf_response_pool_id: raise Exception('Records Set already attached to response pool: {}.'.format(self._dsf_response_pool_id)) self._post(_service_id, _response_pool_id, publish=publish)
@property def label(self): """A unique label for this :class:`DSFFailoverChain`""" return self._label @label.setter def label(self, value): api_args = {'label': value} self._update(api_args) if self._implicitPublish: self._label = value @property def core(self): """Indicates whether or not the contained :class:`DSFRecordSet`'s are core record sets. """ return self._core @core.setter def core(self, value): api_args = {'core': value} self._update(api_args) if self._implicitPublish: self._core = value @property def record_sets(self): """A list of :class:`DSFRecordSet` connected to this :class:`DSFFailvoerChain` """ return self._record_sets
[docs] def to_json(self, svc_id=None, skip_svc=False): """Convert this :class:`DSFFailoverChain` to a JSON blob""" if self._service_id and not svc_id: svc_id = self._service_id json_blob = {} if svc_id and not skip_svc: json_blob['service_id'] = svc_id if self._label: json_blob['label'] = self._label if self._dsf_record_set_failover_chain_id: json_blob['dsf_record_set_failover_chain_id'] = self._dsf_record_set_failover_chain_id if self._core: json_blob['core'] = self._core if self.record_sets: json_blob['record_sets'] = [rs.to_json(svc_id) for rs in self.record_sets] return json_blob
@property def dsf_id(self): """The unique system id of the :class:`TrafficDirector` This :class:`DSFFailoverChain` is attached to """ return self._service_id @property def response_pool_id(self): """The unique system id of the :class:`DSFResponsePool` this :class:`DSFFailoverChain` is attached to """ return self._dsf_response_pool_id @property def failover_chain_id(self): """The unique system id of this :class:`DSFFailoverChain` """ return self._dsf_record_set_failover_chain_id @property def implicitPublish(self): "Toggle for this specific :class:`DSFFailoverChain` for turning on and off implicit Publishing for record Updates." return self._implicitPublish @implicitPublish.setter def implicitPublish(self, value): if value != True and value != False: raise Exception('Value must be True or False') self._implicitPublish = value
[docs] def delete(self): """Delete this :class:`DSFFailoverChain` from the Dynect System""" api_args = {'publish': 'Y'} DynectSession.get_session().execute(self.uri, 'DELETE', api_args)
[docs]class DSFResponsePool(object): """docstring for DSFResponsePool"""
[docs] def __init__(self, label, core_set_count=1, eligible=True, automation='auto', dsf_ruleset_id=None, index=None, rs_chains=None, **kwargs): """Create a :class:`DSFResponsePool` object :param label: A unique label for this :class:`DSFResponsePool` :param core_set_count: If fewer than this number of core record sets are eligible, status will be set to fail :param eligible: Indicates whether or not the :class:`DSFResponsePool` can be served :param automation: Defines how eligible can be changed in response to monitoring :param dsf_ruleset_id: Unique system id of the Ruleset this :class:`DSFResponsePool` is attached to :param index: When specified with dsf_ruleset_id, indicates the position of the :class:`DSFResponsePool` :param rs_chains: A list of :class:`DSFFailoverChain` that are defined for this :class:`DSFResponsePool` """ super(DSFResponsePool, self).__init__() self._label = label self._core_set_count = core_set_count self._eligible = eligible self._automation = automation self._dsf_ruleset_id = dsf_ruleset_id self._dsf_response_pool_id = None self._index = index self._implicitPublish=True if isinstance(rs_chains, list) and len(rs_chains) > 0 and \ isinstance(rs_chains[0], dict): # Clear Failover Chains self._rs_chains = [] # Create a new FO Chain for each entry returned from API for chain in rs_chains: self._rs_chains.append(DSFFailoverChain(**chain)) else: self._rs_chains = rs_chains self._service_id = self._dsf_response_pool_id = self.uri = None for key, val in kwargs.items(): setattr(self, '_' + key, val) # If dsf_id and dsf_response_pool_id were specified in kwargs if self._service_id is not None and self._dsf_response_pool_id is not None: r_pid = self._dsf_response_pool_id self.uri = '/DSFResponsePool/{}/{}/'.format(self._service_id, r_pid)
def _post(self, service_id, publish=True): """Create a new :class:`DSFResponsePool` on the DynECT System :param service_id: the id of the DSF service this :class:`DSFResponsePool` is attached to """ self.service_id = service_id uri = '/DSFResponsePool/{}/'.format(self.service_id) api_args = {'publish': 'Y', 'label': self._label, 'core_set_count': self._core_set_count, 'eligible': self._eligible, 'automation': self._automation} if self._dsf_ruleset_id: api_args['dsf_ruleset_id'] = self._dsf_ruleset_id if self._index: api_args['index'] = self._index if self._rs_chains: api_args['rs_chains'] = [chain.to_json(skip_svc=True) for chain in self.rs_chains] if publish: api_args['publish'] = 'Y' response = DynectSession.get_session().execute(uri, 'POST', api_args) self._build(response['data']) self.uri = '/DSFResponsePool/{}/{}/'.format(self.service_id, self._dsf_response_pool_id) def _get(self, service_id, dsf_response_pool_id): """Get an existing :class:`DSFResponsePool` from the DynECT System :param service_id: the id of the DSF service this :class:`DSFResponsePool` is attached to :param dsf_response_pool_id: the id of this :class:`DSFResponsePool` """ self.service_id = service_id self._dsf_response_pool_id = dsf_response_pool_id self.uri = '/DSFResponsePool/{}/{}/'.format(self.service_id, self._dsf_response_pool_id) api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args, publish=True): """Make the API call to update the :class:`DSFResponsePool` :param api_args: arguments to be pased to the API call """ if publish and self._implicitPublish: api_args['publish'] = 'Y' self.uri = 'DSFResponsePool/{}/{}'.format(self._service_id, self._dsf_response_pool_id) response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): """Private build method""" if data['rs_chains']: self._rs_chains = [] for key, val in data.items(): if key != 'rs_chains': setattr(self, '_' + key, val) if key == 'rs_chains': for rs_chain in val: self._rs_chains.append(DSFFailoverChain(**rs_chain)) def __str__(self): str = list() str.append('Label: {}'.format(self.label)) if self._dsf_response_pool_id: str.append('ID: {}'.format(self._dsf_response_pool_id)) return ("<DSFResponsePool>: {}".format(', '.join(str))) __repr__ = __unicode__ = __str__
[docs] def create(self, service, publish=True): """Adds this :class:`DSFResponsePool` to the passed in :class:`TrafficDirector` :param service: a :class:`TrafficDirector` or id string for the :class:`TrafficDirector` you wish to add this :class:`DSFResponsePool` to. :param publish: publish at execution time. Default = True """ if self._dsf_response_pool_id: raise Exception('Response Pool Already Exists. ID: {}'.format(self._dsf_response_pool_id)) _service_id = _checkType(service) self._post(_service_id)
[docs] def publish(self): """Publish changes to :class:`TrafficDirector`.""" uri = '/DSF/{}/'.format(self._service_id) api_args = {'publish':'Y'} DynectSession.get_session().execute(uri, 'PUT', api_args) self.refresh()
[docs] def refresh(self): """Pulls data down from Dynect System and repopulates :class:`DSFResponsePool` """ self._get(self._service_id, self._dsf_response_pool_id)
@property def label(self): """A unique label for this :class:`DSFResponsePool`""" return self._label @label.setter def label(self, value): api_args = {'label': value} self._update(api_args) if self._implicitPublish: self._label = value @property def core_set_count(self): """If fewer than this number of core record sets are eligible, status will be set to fail """ return self._core_set_count @core_set_count.setter def core_set_count(self, value): api_args = {'core_set_count': value} self._update(api_args) if self._implicitPublish: self._core_set_count = value @property def eligible(self): """Indicates whether or not the :class:`DSFResponsePool` can be served """ return self._eligible @eligible.setter def eligible(self, value): api_args = {'eligible': value} self._update(api_args) if self._implicitPublish: self._eligible = value @property def automation(self): """Defines how eligiblity can be changed in response to monitoring""" return self._automation @automation.setter def automation(self, value): api_args = {'automation': value} self._update(api_args) if self._implicitPublish: self._automation = value @property def ruleset_ids(self): """List of Unique system ids of the :class:`DSFRuleset`s this :class:`DSFResponsePool` is attached to """ self._get(self._service_id, self._dsf_response_pool_id) return [ruleset['dsf_ruleset_id'] for ruleset in self._rulesets] @property def response_pool_id(self): """The Unique system id of this :class:`DSFResponsePool` """ return self._dsf_response_pool_id @property def dsf_id(self): """The unique system id of the :class:`TrafficDirector` This :class:`DSFResponsePool` is attached to """ return self._service_id @property def failover_chains(self): """A ``list`` of :class:`DSFFailoverChain` that are defined for this :class:`DSFResponsePool` """ return self._rs_chains @property def rs_chains(self): """A ``list`` of :class:`DSFFailoverChain` that are defined for this :class:`DSFResponsePool` (legacy call) """ return self._rs_chains
[docs] def to_json(self, svc_id=None, skip_svc=False): """Convert this :class:`DSFResponsePool` to a JSON blob""" if self._service_id and not svc_id: svc_id = self._service_id rs_json = [rs.to_json(svc_id) for rs in self._rs_chains] json_blob = {'label': self._label, 'eligible': self._eligible, 'core_set_count': self._core_set_count, 'automation': self._automation, 'rs_chains': rs_json} if self._index: json_blob['index'] = self._index if self._dsf_ruleset_id: json_blob['dsf_ruleset_id'] = self._dsf_ruleset_id if svc_id and not skip_svc: json_blob['service_id'] = svc_id return json_blob
@property def implicitPublish(self): "Toggle for this specific :class:`DSFResponsePool` for turning on and off implicit Publishing for record Updates." return self._implicitPublish @implicitPublish.setter def implicitPublish(self, value): if value != True and value != False: raise Exception('Value must be True or False') self._implicitPublish = value
[docs] def delete(self): """Delete this :class:`DSFResponsePool` from the DynECT System""" api_args = {'publish': 'Y'} DynectSession.get_session().execute(self.uri, 'DELETE', api_args)
[docs]class DSFRuleset(object): """docstring for DSFRuleset"""
[docs] def __init__(self, label, criteria_type, response_pools, criteria=None, failover=None, **kwargs): """Create a :class:`DSFRuleset` object :param label: A unique label for this :class:`DSFRuleset` :param criteria_type: A set of rules describing what traffic is applied to the :class:`DSFRuleset` :param criteria: Varied depending on the specified criteria_type :param failover: IP address or Hostname for a last resort failover. :param response_pools: A list of :class:`DSFResponsePool`'s for this :class:`DSFRuleset` """ super(DSFRuleset, self).__init__() self.valid_criteria_types = ('always', 'geoip') self.valid_criteria = {'always': (), 'geoip': ()} self._label = label self._criteria_type = criteria_type self._criteria = criteria self._failover = failover self._implicitPublish=True if isinstance(response_pools, list) and len(response_pools) > 0 and \ isinstance(response_pools[0], dict): self._response_pools = [] for pool in response_pools: pool = {x: pool[x] for x in pool if x != 'rulesets'} self._response_pools.append(DSFResponsePool(**pool)) else: self._response_pools = response_pools self._service_id = self._dsf_ruleset_id = self.uri = None for key, val in kwargs.items(): setattr(self, '_' + key, val) # If dsf_id and dsf_ruleset_id were specified in kwargs if self._service_id is not None and self._dsf_ruleset_id is not None: self.uri = '/DSFRuleset/{}/{}/'.format(self._service_id, self._dsf_ruleset_id)
def _post(self, dsf_id, publish=True): """Create a new :class:`DSFRuleset` on the DynECT System :param dsf_id: the id of the DSF service this :class:`DSFRuleset` is attached to :param publish: Publish at run time. Default is True """ self._service_id = dsf_id uri = '/DSFRuleset/{}/'.format(self._service_id) api_args = {'publish': 'Y', 'label': self._label, 'criteria_type': self._criteria_type, 'criteria': self._criteria} if self._response_pools: api_args['response_pools'] = [pool.to_json(skip_svc=True) for pool in self.response_pools] if publish: api_args['publish'] = 'Y' response = DynectSession.get_session().execute(uri, 'POST', api_args) self._build(response['data']) self.uri = '/DSFRuleset/{}/{}/'.format(self._service_id, self._dsf_ruleset_id) def _get(self, dsf_id, dsf_ruleset_id): """Get an existing :class:`DSFRuleset` from the DynECT System :param dsf_id: the id of the DSF service this :class:`DSFRuleset` is attached to :param dsf_ruleset_id: The unique system id of this :class:`DSFRuleset` """ self._service_id = dsf_id self._dsf_ruleset_id = dsf_ruleset_id self.uri = '/DSFRuleset/{}/{}/'.format(self._service_id, self._dsf_ruleset_id) api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args, publish=True): """Make the API call to update the current record type :param api_args: arguments to be pased to the API call """ if publish and self._implicitPublish: api_args['publish'] = 'Y' self.uri = 'DSFRuleset/{}/{}'.format(self._service_id, self._dsf_ruleset_id) response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): """Private build method""" if data['response_pools']: self._response_pools = [] for key, val in data.items(): if key != 'response_pools': setattr(self, '_' + key, val) if key == 'response_pools': for response_pool in val: self._response_pools.append(DSFResponsePool(**response_pool)) def __str__(self): str = list() str.append('Label: {}'.format(self.label)) if self._dsf_ruleset_id: str.append('ID: {}'.format(self._dsf_ruleset_id)) return ("<DSFRuleSet>: {}".format(', '.join(str))) __repr__ = __unicode__ = __str__
[docs] def add_response_pool(self, response_pool, index=0, publish=True): """ Adds passed in :class:`DSFResponsePool` to this :class:`DSFRuleSet` By default this adds it to the front of the list. :param response_pool: Can either be the response_pool_id or a :class:`DSFResponsePool` Object. :param index: where in the list of response pools to place this pool. 0 is the first position, 0 is the default. :param publish: Publish on execution (Default = True) """ if isinstance(response_pool, DSFResponsePool): _response_pool_id = response_pool._dsf_response_pool_id elif type(response_pool) is str or type(response_pool) is unicode: _response_pool_id = response_pool else: raise Exception('Could not make sense of Response Pool Type') self._get(self._service_id, self._dsf_ruleset_id) api_args = dict() api_args['response_pools'] = list() hit = False for pIndex, old_pool in enumerate(self._response_pools): if pIndex == index: api_args['response_pools'].append({'dsf_response_pool_id': _response_pool_id}) hit = True api_args['response_pools'].append({'dsf_response_pool_id': old_pool._dsf_response_pool_id}) #If the index was greater than what was available, just append to the end. if not hit: api_args['response_pools'].append({'dsf_response_pool_id': _response_pool_id}) self._update(api_args, publish)
[docs] def remove_response_pool(self, response_pool, publish=True): """ Removes passed in :class:`DSFResponsePool` from this :class:`DSFRuleSet`. :param service: Can either be the service_id or a `TrafficDirector` Object :param response_pool: Can either be the response_pool_id or a `DSFResponsePool` Object. :param publish: Publish on execution (Default = True) """ if isinstance(response_pool, DSFResponsePool): _response_pool_id = response_pool._dsf_response_pool_id _service_id = response_pool._service_id elif type(response_pool) is str or type(response_pool) is unicode: _response_pool_id = response_pool else: raise Exception('Could not make sense of Response Pool Type') self.refresh() api_args = dict() api_args['response_pools'] = list() system_pool_ids = [pool._dsf_response_pool_id for pool in self._response_pools] for pool_id in system_pool_ids: if pool_id != _response_pool_id: api_args['response_pools'].append({'dsf_response_pool_id': pool_id}) self._update(api_args, publish)
[docs] def add_failover_ip(self, ip, publish=True): """ Adds passed in :class:`DSFResponsePool` to the end of this :class:`DSFRuleSet` This effectively creates a special new Record chain with a single IP. It can be accessed as a responce pool with label equal to the ip passed in. :param service: Can either be the service_id or a `TrafficDirector` Object :param publish: Publish on execution (Default = True) """ api_args = dict() api_args['response_pools'] = list() for old_pool in self._response_pools: api_args['response_pools'].append({'dsf_response_pool_id': old_pool._dsf_response_pool_id}) api_args['response_pools'].append({'failover': ip}) print api_args self._update(api_args, publish)
[docs] def order_response_pools(self, pool_list, publish=True): """ For reordering the ruleset list. simply pass in a ``list`` of :class:`DSFResponcePool`s in the order you wish them to failover. :param pool_list: ordered ``list`` of :class:`DSFResponcePool` :param publish: Publish on execution. default = True """ if not isinstance(pool_list, list): raise Exception('You must pass in an ordered list of response pool objects, or ids.') _pool_list = list() for list_item in pool_list: if isinstance(list_item, DSFResponsePool): _pool_list.append(list_item._dsf_response_pool_id) elif type(list_item) is str or type(list_item) is unicode: _pool_list.append(list_item) api_args = dict() api_args['response_pools'] = list() for pool_id in _pool_list: api_args['response_pools'].append({'dsf_response_pool_id': pool_id}) self._update(api_args, publish)
[docs] def create(self, service, publish=True): """Adds this :class:`DSFRuleset` to the passed in :class:`TrafficDirector` :param service: a :class:`TrafficDirector` or id string for the :class:`TrafficDirector` you wish to add this :class:`DSFRuleset` to. :param publish: publish at execution time. Default = True """ if self._dsf_ruleset_id: raise Exception('Rule Set Already Exists. ID: {}'.format(self._dsf_ruleset_id)) _service_id = _checkType(service) self._post(_service_id, publish)
[docs] def publish(self): """Publish changes to :class:`TrafficDirector`.""" uri = '/DSF/{}/'.format(self._service_id) api_args = {'publish':'Y'} DynectSession.get_session().execute(uri, 'PUT', api_args) self.refresh()
[docs] def refresh(self): """Pulls data down from Dynect System and repopulates :class:`DSFRuleset` """ self._get(self._service_id, self._dsf_ruleset_id)
@property def label(self): """A unique label for this :class:`DSFRuleset`""" return self._label @label.setter def label(self, value): api_args = {'label': value} self._update(api_args) if self._implicitPublish: self._label = value @property def criteria_type(self): """A set of rules describing what traffic is applied to the :class:`DSFRuleset` """ return self._criteria_type @criteria_type.setter def criteria_type(self, value): api_args = {'criteria_type': value} self._update(api_args) if self._implicitPublish: self._criteria_type = value @property def criteria(self): """The criteria rules, will be varied depending on the specified criteria_type """ return self._criteria @criteria.setter def criteria(self, value): api_args = {'criteria': value} self._update(api_args) if self._implicitPublish: self._criteria = value @property def response_pools(self): """A list of :class:`DSFResponsePool`'s for this :class:`DSFRuleset`""" return self._response_pools @property def dsf_id(self): """The unique system id of the :class:`TrafficDirector` This :class:`DSFRuleset` is attached to """ return self._service_id @property def ruleset_id(self): """The unique system id of this :class:`DSFRuleset` """ return self._dsf_ruleset_id @property def implicitPublish(self): "Toggle for this specific :class:`DSFRuleset` for turning on and off implicit Publishing for record Updates." return self._implicitPublish @implicitPublish.setter def implicitPublish(self, value): if value != True and value != False: raise Exception('Value must be True or False') self._implicitPublish = value @property def _json(self, svc_id=None, skip_svc=False): """JSON-ified version of this DSFRuleset Object""" if self._service_id and not svc_id: svc_id = self._service_id pool_json = [pool.to_json(svc_id) for pool in self._response_pools] if self._failover: pool_json.append({'failover': self._failover}) json_blob = {'label': self._label, 'criteria_type': self._criteria_type, 'criteria': self._criteria, 'response_pools': pool_json} if svc_id and not skip_svc: json_blob['service_id'] = svc_id return json_blob
[docs] def delete(self): """Remove this :class:`DSFRuleset` from it's associated :class:`TrafficDirector` Service """ api_args = {'publish': 'Y'} DynectSession.get_session().execute(self.uri, 'DELETE', api_args)
class DSFMonitorEndpoint(object): """An Endpoint object to be passed to a :class:`DSFMonitor`""" def __init__(self, address, label, active='Y', site_prefs=None): """Create a :class:`DSFMonitorEndpoint` object :param address: The address to monitor. :param label: A label to identify this :class:`DSFMonitorEndpoint`. :param active: Indicates whether or not this :class:`DSFMonitorEndpoint` endpoint is active. Must be one of True, False, 'Y', or 'N' :param site_prefs: A ``list`` of site codes from which this :class:`DSFMonitorEndpoint` will be monitored """ self._address = address self._label = label self._active = Active(active) self._site_prefs = site_prefs self._monitor = None def _update(self, api_args): """Update this :class:`DSFMonitorEndpoint` with the provided api_args :param api_args: arguments to pass to the API via PUT """ if self._monitor is not None: full_list = self._monitor.endpoints args_list = [] for endpoint in full_list: if id(endpoint) == id(self): args_list.append(api_args) else: args_list.append(endpoint._json) api_args = {'endpoints': args_list} self._monitor._update(api_args) @property def _json(self): """Get the JSON representation of this :class:`DSFMonitorEndpoint` object """ json_blob = {'address': self._address, 'label': self._label, 'active': str(self._active), 'site_prefs': self._site_prefs} return {x: json_blob[x] for x in json_blob if json_blob[x] is not None} @property def active(self): """Indicates if this :class:`DSFMonitorEndpoint` is active. When updating valid arguments are 'Y' or True to activate, or 'N' or False to deactivate. :returns: An :class:`Active` object representing the current state of this :class:`DSFMonitorEndpoint` """ return self._active @active.setter def active(self, value): valid_input = ('Y', 'N', True, False) if value not in valid_input: raise DynectInvalidArgumentError('active', value, valid_input) api_args = self._json api_args['active'] = value self._update(api_args) @property def label(self): return self._label @label.setter def label(self, value): api_args = self._json api_args['label'] = value self._update(api_args) @property def address(self): return self._address @address.setter def address(self, value): api_args = self._json api_args['address'] = value self._update(api_args) @property def site_prefs(self): return self._site_prefs @site_prefs.setter def site_prefs(self, value): api_args = self._json api_args['site_prefs'] = value self._update(api_args)
[docs]class DSFMonitor(object): """A Monitor for a :class:`TrafficDirector` Service"""
[docs] def __init__(self, *args, **kwargs): """Create a new :class:`DSFMonitor` object :param label: A unique label to identify this :class:`DSFMonitor` :param protocol: The protocol to monitor. Must be one of 'HTTP', 'HTTPS', 'PING', 'SMTP', or 'TCP' :param response_count: The number of responses to determine whether or not the endpoint is 'up' or 'down' :param probe_interval: How often to run this :class:`DSFMonitor` :param retries: How many retries this :class:`DSFMonitor` should attempt on failure before giving up. :param active: Indicates if this :class:`DSFMonitor` is active :param options: Additional options pertaining to this :class:`DSFMonitor` :param endpoints: A List of :class:`DSFMonitorEndpoint`'s that are associated with this :class:`DSFMonitor` """ super(DSFMonitor, self).__init__() self.uri = None self._monitor_id = None self._label = self._protocol = self._response_count = None self._probe_interval = self._retries = self._active = None self._options = self._dsf_monitor_id = self._timeout = self._port = None self._path = self._host = self._header = self._expected = None self._endpoints = [] if 'api' in kwargs: del kwargs['api'] for key, val in kwargs.items(): setattr(self, '_' + key, val) self.uri = '/DSFMonitor/{}/'.format(self._dsf_monitor_id) elif len(args) + len(kwargs) == 1: self._get(*args, **kwargs) else: self._post(*args, **kwargs)
def _get(self, monitor_id): """Get an existing :class:`DSFMonitor` from the DynECT System""" self._monitor_id = monitor_id self.uri = '/DSFMonitor/{}/'.format(self._monitor_id) api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _post(self, label, protocol, response_count, probe_interval, retries, active='Y', timeout=None, port=None, path=None, host=None, header=None, expected=None, endpoints=None): """Create a new :class:`DSFMonitor` on the DynECT System""" uri = '/DSFMonitor/' self._label = label self._protocol = protocol self._response_count = response_count self._probe_interval = probe_interval self._retries = retries self._active = Active(active) self._options = {} if timeout: self._timeout = timeout self._options['timeout'] = timeout if port: self._port = port self._options['port'] = port if path: self._path = path self._options['path'] = path if host: self._host = host self._options['host'] = host if header: self._header = header self._options['header'] = header if expected: self._expected = expected self._options['expected'] = expected self._endpoints = endpoints api_args = {'label': self._label, 'protocol': self._protocol, 'response_count': self._response_count, 'probe_interval': self._probe_interval, 'retries': self._retries, 'active': str(self._active), 'options': self._options} if self._endpoints is not None: api_args['endpoints'] = [x._json for x in self._endpoints] response = DynectSession.get_session().execute(uri, 'POST', api_args) self._build(response['data']) self.uri = '/DSFMonitor/{}/'.format(self._dsf_monitor_id) def _update(self, api_args): """Private Update method""" self.uri = '/DSFMonitor/{}/'.format(self._dsf_monitor_id) response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): """Update this object based on the information passed in via data :param data: The ``['data']`` field from an API JSON response """ for key, val in data.items(): if key == 'endpoints': self._endpoints = [] for endpoint in val: ep = DSFMonitorEndpoint(**endpoint) ep._monitor = self self._endpoints.append(ep) elif key == 'options': for opt_key, opt_val in val.items(): setattr(self, '_' + opt_key, opt_val) elif key == 'active': self._active = Active(val) else: setattr(self, '_' + key, val) @property def dsf_monitor_id(self): """The unique system id of this :class:`DSFMonitor`""" return self._dsf_monitor_id @dsf_monitor_id.setter def dsf_monitor_id(self, value): pass @property def label(self): """A unique label to identify this :class:`DSFMonitor`""" return self._label @label.setter def label(self, value): self._label = value api_args = {'label': self._label} self._update(api_args) @property def protocol(self): """The protocol to monitor. Must be one of 'HTTP', 'HTTPS', 'PING', 'SMTP', or 'TCP' """ return self._protocol @protocol.setter def protocol(self, value): self._protocol = value api_args = {'protocol': self._protocol} self._update(api_args) @property def response_count(self): """The minimum number of agents reporting the host as up for failover not to occur. Must be 0, 1 or 2 """ return self._response_count @response_count.setter def response_count(self, value): self._response_count = value api_args = {'response_count': self._response_count} self._update(api_args) @property def probe_interval(self): """How often to run this :class:`DSFMonitor`""" return self._probe_interval @probe_interval.setter def probe_interval(self, value): self._probe_interval = value api_args = {'probe_interval': self._probe_interval} self._update(api_args) @property def retries(self): """How many retries this :class:`DSFMonitor` should attempt on failure before giving up. """ return self._retries @retries.setter def retries(self, value): self._retries = value api_args = {'retries': self._retries} self._update(api_args) @property def active(self): """Returns whether or not this :class:`DSFMonitor` is active. Will return either 'Y' or 'N' """ return self._active @active.setter def active(self, value): self._active = value api_args = {'active': self._active} self._update(api_args) @property def endpoints(self): """A list of the endpoints (and their statuses) that this :class:`DSFMonitor` is currently monitoring. """ self._get(self.dsf_monitor_id) return self._endpoints @endpoints.setter def endpoints(self, value): pass @property def options(self): """Additional options pertaining to this :class:`DSFMonitor`""" return self._options @options.setter def options(self, value): self._options = value api_args = {'options': self._options} self._update(api_args) def __str__(self): """str override""" return force_unicode('<DSFMonitor>: {}, ID: {}').format(self._label, self._dsf_monitor_id) __repr__ = __unicode__ = __str__
[docs] def delete(self): """Delete an existing :class:`DSFMonitor` from the DynECT System""" api_args = {} self.uri = '/DSFMonitor/{}/'.format(self._dsf_monitor_id) DynectSession.get_session().execute(self.uri, 'DELETE', api_args)
[docs]class DSFNotifier(object):
[docs] def __init__(self, *args, **kwargs): """ Create a :class:`Notifier` object :param label: :param recipients: ``list`` of Contact Names :param dsf_services: :param monitor_services: """ self._label = self._notifier_id = self._recipients = None self._services = None if 'api' in kwargs: del kwargs['api'] self._build(kwargs) return if 'td' in kwargs: del kwargs['td'] self._build(kwargs['notifier']) return elif len(args) + len(kwargs) == 1: self._get(*args, **kwargs) else: self._post(*args, **kwargs) self.uri = '/Notifier/'
def _post(self, label, dsf_services=None, monitor_services=None, recipients=None): """Create a new :class:`TrafficDirector` on the DynECT System""" uri = '/Notifier/' api_args = {} if recipients: api_args['recipients'] = list() for recipient in recipients: api_args['recipients'].append({'recipient': recipient, 'format':'email'}) if dsf_services or monitor_services: api_args['services'] = list() if dsf_services: api_args['services'] += [{'service_class': 'DSF', 'service_id': service_id} for service_id in dsf_services] if monitor_services: api_args['services'] += [{'service_class': 'Monitor', 'service_id': service_id} for service_id in monitor_services] self._label = label api_args['label'] = label response = DynectSession.get_session().execute(uri, 'POST', api_args) self.uri = '/Notifier/{}/'.format(response['data']['notifier_id']) self._build(response['data']) def _get(self, notifier_id): self._notifier_id = notifier_id self.uri = '/Notifier/{}/'.format(self._notifier_id) api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args): """Private update method""" self.uri = '/Notifier/{}/'.format(self._notifier_id) response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): for key, val in data.items(): setattr(self, '_' + key, val) @property def label(self): return self._label @label.setter def label(self, value): api_args = {'label': value} self._update(api_args) self._label = value @property def recipients(self): return self._recipients
[docs] def add_recipient(self, new_recipient, format='email'): recipients = self._recipients for recipient in recipients: recipient.pop('details', None) recipient.pop('features', None) recipients.append({'recipient': new_recipient, 'format': format}) api_args = {'recipients': recipients} self._update(api_args)
[docs] def del_recipient(self, recipient): recipients = [srecipient for srecipient in self._recipients if srecipient['recipient'] != recipient] for recipient in recipients: recipient.pop('details', None) recipient.pop('features', None) api_args = {'recipients': recipients} self._update(api_args)
@property def dsf_service_ids(self): return [service['service_id'] for service in self._services if service['service_class'] == 'DSF'] @property def monitor_service_ids(self): return [service['service_id'] for service in self._services if service['service_class'] == 'Monitor']
[docs] def to_json(self): json_blob = {} if self._label: json_blob['label'] = self._label if self._recipients: json_blob['recipients'] = [recipient['recipient'] for recipient in self._recipients] if self._services: json_blob['dsf_services'] = [dsf['service_id'] for dsf in self._services if dsf['service_class'] == 'DSF'] json_blob['monitor_services'] = [mon['service_id'] for mon in self._services if mon['service_class'] == 'Monitor'] return json_blob
def __str__(self): """str override""" return force_unicode('<DSFNotifier>: {}, ID: {}').format(self._label, self._notifier_id) __repr__ = __unicode__ = __str__
[docs] def delete(self): """Delete this :class:`DSFNotifier` from the Dynect System """ self.uri = '/Notifier/{}/'.format(self._notifier_id) DynectSession.get_session().execute(self.uri, 'DELETE')
[docs]class TrafficDirector(object): """Traffic Director is a DNS based traffic routing and load balancing service that is Geolocation aware and can support failover by monitoring endpoints. """
[docs] def __init__(self, *args, **kwargs): """Create a :class:`TrafficDirector` object :param label: A unique label for this :class:`TrafficDirector` service :param ttl: The default TTL to be used across this service :param publish: If Y, service will be published on creation :param nodes: A Node Object, a zone, FQDN pair in a hash, or a list containing those two things (can be mixed) that are to be linked to this :class:`TrafficDirector` service: :param notifiers: A list of notifier ids associated with this :class:`TrafficDirector` service :param rulesets: A list of :class:`DSFRulesets` that are defined for this :class:`TrafficDirector` service """ super(TrafficDirector, self).__init__() self._label = self._ttl = self._publish = self._response_pools = None self._record_sets = self.uri = self._service_id = None self._notifiers = APIList(DynectSession.get_session, 'notifiers') self._nodes = APIList(DynectSession.get_session, 'nodes') self._rulesets = APIList(DynectSession.get_session, 'rulesets') self._implicitPublish = True if 'api' in kwargs: del kwargs['api'] self._build(kwargs) elif len(args) + len(kwargs) == 1: self._get(*args, **kwargs) else: self._post(*args, **kwargs) self.uri = '/DSF/{}/'.format(self._service_id) self._rulesets.uri = self.uri
def _post(self, label, ttl=None, publish='Y', nodes=None, notifiers=None, rulesets=None): """Create a new :class:`TrafficDirector` on the DynECT System""" uri = '/DSF/' self._label = label self._ttl = ttl self._nodes = nodes self._notifiers = notifiers self._rulesets = rulesets api_args = {'label': self._label, 'publish': publish} if ttl: api_args['ttl'] = self._ttl if nodes: _nodeList=[] if isinstance(nodes, list): for node in nodes: if isinstance(node, dyn.tm.zones.Node): _nodeList.append({'zone':node.zone, 'fqdn':node.fqdn}) elif isinstance(node, dict): _nodeList.append(node) elif isinstance(nodes,dict): _nodeList.append(nodes) elif isinstance(nodes, dyn.tm.zones.Node): _nodeList.append({'zone':nodes.zone, 'fqdn':nodes.fqdn}) self._nodes=_nodeList api_args['nodes'] = self._nodes if notifiers: api_args['notifiers'] = [] for notifier in notifiers: if isinstance(notifier, DSFNotifier): api_args['notifiers'].append({'notifier_id':notifier._notifier_id}) elif isinstance(notifier, Notifier): api_args['notifiers'].append({'notifier_id':notifier._notifier_id}) elif type(notifier) is str or type(notifier) is unicode: api_args['notifiers'].append({'notifier_id':notifier}) else: raise Exception('notifiers must be a list containing DSFNotifier objects, or notifier_id strings.') if rulesets: api_args['rulesets'] = [rule._json for rule in self._rulesets] response = DynectSession.get_session().execute(uri, 'POST', api_args) self.uri = '/DSF/{}/'.format(response['data']['service_id']) self._build(response['data']) def _build(self, data): for key, val in data.items(): if key == 'notifiers': self._notifiers = [] for notifier in val: self._notifiers.append(DSFNotifier(None, td=False, **notifier)) elif key == 'rulesets': # Clear Rulesets self._rulesets = APIList(DynectSession.get_session, 'rulesets') self._rulesets.uri = None # For each Ruleset returned, create a new DSFRuleset object for ruleset in val: self._rulesets.append(DSFRuleset(**ruleset)) elif key == 'nodes': # nodes are now returned as Node Objects self._nodes = [dyn.tm.zones.Node(node['zone'], node['fqdn']) for node in val] else: setattr(self, '_' + key, val) self.uri = '/DSF/{}/'.format(self._service_id) self._rulesets.uri = self.uri def _get(self, service_id): """Get an existing :class:`TrafficDirector` from the DynECT System""" self._service_id = service_id self.uri = '/DSF/{}/'.format(self._service_id) api_args = {'pending_changes': 'Y'} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args, publish = True): """Private update method""" if publish and self._implicitPublish: api_args['publish'] = 'Y' self.uri = '/DSF/{}/'.format(self._service_id) response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'])
[docs] def publish(self): """Publish changes to :class:`TrafficDirector`.""" uri = '/DSF/{}/'.format(self._service_id) api_args = {'publish':'Y'} DynectSession.get_session().execute(uri, 'PUT', api_args) self.refresh()
[docs] def refresh(self): """Pulls data down from Dynect System and repopulates :class:`TrafficDirector` """ self._get(self._service_id)
@property def all_records(self): """Returns All :class:`DSFRecord` in :class:`TrafficDirector`""" return get_all_records(self) @property def all_record_sets(self): """Returns All :class:`DSFRecordSet` in :class:`TrafficDirector`""" return get_all_record_sets(self) @property def all_failover_chains(self): """Returns All :class:`DSFFailoverChain` in :class:`TrafficDirector`""" return get_all_failover_chains(self) @property def all_response_pools(self): """Returns All :class:`DSFResponsePool` in :class:`TrafficDirector`""" return get_all_response_pools(self) @property def all_rulesets(self): """Returns All :class:`DSFRuleset` in :class:`TrafficDirector`""" return get_all_rulesets(self)
[docs] def revert_changes(self): """Clears the changeset for this service and reverts all non-published changes to their original state """ api_args = {'revert': True} self._update(api_args)
[docs] def add_notifier(self, notifier): """Links the :class:`DSFNotifier` with the specified id to this Traffic Director service """ if isinstance(notifier, DSFNotifier): _notifier_id = notifier._notifier_id elif isinstance(notifier, Notifier): _notifier_id = notifier._notifier_id elif type(notifier) is str or type(notifier) is unicode: _notifier_id = notifier else: raise Exception("Cannot sensibly determine Notifier type, must be DSFNotifier, or notifier_id string") api_args = {'add_notifier': True, 'notifier_id': _notifier_id} self._update(api_args)
[docs] def del_notifier(self, notifier): """Links the :class:`DSFNotifier` with the specified id to this Traffic Director service """ if isinstance(notifier, DSFNotifier): _notifier_id = notifier._notifier_id elif isinstance(notifier, Notifier): _notifier_id = notifier._notifier_id elif type(notifier) is str or type(notifier) is unicode: _notifier_id = notifier else: raise Exception("Cannot sensibly determine Notifier type, must be DSFNotifier, or notifier_id string") self.refresh() safeNotifiers= [{'notifier_id': notifier._notifier_id} for notifier in self._notifiers if notifier._notifier_id != _notifier_id] api_args = {'notifiers': safeNotifiers} self._update(api_args)
[docs] def remove_orphans(self): """Remove Record Set Chains which are no longer referenced by a :class:`DSFResponsePool` """ api_args = {'remove_orphans': 'Y'} self._update(api_args)
@property def service_id(self): """The unique System id of this DSF Service""" return self._service_id @property def records(self): """A list of this :class:`TrafficDirector` Services' DSFRecords""" self.refresh() return [record for ruleset in self._rulesets for response_pool in ruleset.response_pools for rs_chains in response_pool.rs_chains for record_set in rs_chains.record_sets for record in record_set.records] @records.setter def records(self, value): pass @property def record_sets(self): """A list of this :class:`TrafficDirector` Services :class:`DSFRecordSet`'s """ self.refresh() return [record_set for ruleset in self._rulesets for response_pool in ruleset.response_pools for rs_chains in response_pool.rs_chains for record_set in rs_chains.record_sets] @record_sets.setter def record_sets(self, value): pass @property def response_pools(self): """A list of this :class:`TrafficDirector` Services :class:`DSFResponsePool`'s """ self.refresh() return [response_pool for ruleset in self._rulesets for response_pool in ruleset.response_pools] @response_pools.setter def response_pools(self, value): pass @property def failover_chains(self): """A list of this :class:`TrafficDirector` Services :class:`DSFFailoverChain`'s """ self.refresh() return [rs_chains for ruleset in self._rulesets for response_pool in ruleset.response_pools for rs_chains in response_pool.rs_chains] @failover_chains.setter def rs_chains(self, value): pass @property def notifiers(self): """A list of names of :class:`DSFNotifier` associated with this :class:`TrafficDirector` service """ self.refresh() return self._notifiers @property def rulesets(self): """A list of :class:`DSFRulesets` that are defined for this :class:`TrafficDirector` service """ self.refresh() return self._rulesets @rulesets.setter def rulesets(self, value): if isinstance(value, list) and not isinstance(value, APIList): self._rulesets = APIList(DynectSession.get_session, 'rulesets', None, value) elif isinstance(value, APIList): self._rulesets = value self._rulesets.uri = self.uri @property def nodeObjects(self): """A list of :class:`Node` Objects that are linked to this :class:`TrafficDirector` service""" uri = '/DSFNode/{}'.format(self._service_id) api_args = {} response = DynectSession.get_session().execute(uri, 'GET', api_args) self._nodes = [dyn.tm.zones.Node(node['zone'], node['fqdn']) for node in response['data']] return self._nodes @property def nodes(self): """A list of hashes of zones, fqdn for each DSF node that is linked to this :class:`TrafficDirector` service""" uri = '/DSFNode/{}'.format(self._service_id) api_args = {} response = DynectSession.get_session().execute(uri, 'GET', api_args) self._nodes = [dyn.tm.zones.Node(node['zone'], node['fqdn']) for node in response['data']] return [{'zone': node['zone'], 'fqdn': node['fqdn']} for node in response['data']] @nodes.setter def nodes(self, nodes): """A :class:`Node` Object, a zone, FQDN pair in a hash, or a list containing those two things (can be mixed) that are to be linked to this :class:`TrafficDirector` service. This overwrites whatever nodes are already linked to this :class:`TrafficDirector` service .""" _nodeList=[] if isinstance(nodes, list): for node in nodes: if isinstance(node, dyn.tm.zones.Node): _nodeList.append({'zone':node.zone, 'fqdn':node.fqdn}) elif isinstance(node, dict): _nodeList.append(node) elif isinstance(nodes,dict): _nodeList.append(nodes) elif isinstance(nodes, dyn.tm.zones.Node): _nodeList.append({'zone':nodes.zone, 'fqdn':nodes.fqdn}) uri = '/DSFNode/{}'.format(self._service_id) api_args = {'nodes': _nodeList, 'publish': 'Y'} response = DynectSession.get_session().execute(uri, 'PUT', api_args) self._nodes = [dyn.tm.zones.Node(node['zone'], node['fqdn']) for node in response['data']]
[docs] def add_node(self, node): """A :class:`Node` object, or a zone, FQDN pair in a hash to be added to this :class:`TrafficDirector` service:""" if isinstance(node, dyn.tm.zones.Node): _node = {'zone':node.zone, 'fqdn':node.fqdn} elif isinstance(node, dict): _node = node uri = '/DSFNode/{}'.format(self._service_id) api_args = {'node': _node, 'publish': 'Y'} response = DynectSession.get_session().execute(uri, 'POST', api_args) self._nodes = [dyn.tm.zones.Node(node['zone'], node['fqdn']) for node in response['data']]
[docs] def remove_node(self, node): """A :class:`Node` object, or a zone, FQDN pair in a hash to be removed to this :class:`TrafficDirector` service:""" if isinstance(node, dyn.tm.zones.Node): _node = {'zone':node.zone, 'fqdn':node.fqdn} elif isinstance(node, dict): _node = node uri = '/DSFNode/{}'.format(self._service_id) api_args = {'node': _node, 'publish': 'Y'} response = DynectSession.get_session().execute(uri, 'DELETE', api_args) self._nodes = [dyn.tm.zones.Node(node['zone'], node['fqdn']) for node in response['data']]
@property def label(self): """A unique label for this :class:`TrafficDirector` service""" return self._label @label.setter def label(self, value): api_args = {'label': value} self._update(api_args) if self._implicitPublish: self._label = value @property def ttl(self): """The default TTL to be used across this service""" if not isinstance(self._ttl, int): self._ttl = int(self._ttl) return self._ttl @ttl.setter def ttl(self, value): api_args = {'ttl': value} self._update(api_args) if self._implicitPublish: self._ttl = value @property def implicitPublish(self): "Toggle for this specific :class:`TrafficDirector` for turning on and off implicit Publishing for record Updates." return self._implicitPublish @implicitPublish.setter def implicitPublish(self, value): if value != True and value != False: raise Exception('Value must be True or False') self._implicitPublish = value
[docs] def delete(self): """Delete this :class:`TrafficDirector` from the DynECT System""" api_args = {} self.uri = '/DSF/{}/'.format(self._service_id) DynectSession.get_session().execute(self.uri, 'DELETE', api_args)
def __str__(self): """str override""" return force_unicode('<TrafficDirector>: {}, ID: {}').format(self._label, self._service_id) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())