# -*- coding: utf-8 -*-
"""This module contains wrappers for interfacing with every element of a
Traffic Director (DSF) service.
"""
from collections import Iterable
from dyn.compat import force_unicode, string_types
from dyn.tm.utils import APIList, Active
from dyn.tm.errors import DynectInvalidArgumentError
from dyn.tm.records import (ARecord, AAAARecord, ALIASRecord, CAARecord,
CDSRecord, CDNSKEYRecord, CSYNCRecord, CERTRecord,
CNAMERecord, DHCIDRecord, DNAMERecord,
DNSKEYRecord, DSRecord, KEYRecord, KXRecord,
LOCRecord, IPSECKEYRecord, MXRecord, NAPTRRecord,
PTRRecord, PXRecord, NSAPRecord, RPRecord,
NSRecord, SOARecord, SPFRecord, SRVRecord,
TLSARecord, TXTRecord, SSHFPRecord, UNKNOWNRecord)
from dyn.tm.session import DynectSession
from dyn.tm.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', 'get_record',
'get_record_set', 'get_failover_chain', 'get_response_pool',
'get_ruleset', 'get_dsf_monitor', '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', 'DSFNode',
'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 = _check_type(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 = _check_type(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 = _check_type(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 = _check_type(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 = _check_type(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 get_record(record_id, service, always_list=False):
"""
returns :class:`DSFRecord`
:param record_id: id of record you wish to pull up
:param service: id of service which this record belongs. Can either be
the service_id or a :class:`TrafficDirector` Object
:param always_list: Force the returned record to always be in a list.
:return returns single record, unless this is a special record type, then
a list is returned
"""
_service_id = _check_type(service)
uri = '/DSFRecord/{}/{}'.format(_service_id, record_id)
api_args = {'detail': 'Y'}
response = DynectSession.get_session().execute(uri, 'GET', api_args)
record = _constructor(response['data'])
if len(record) > 1 or always_list:
return record
else:
return record[0]
def get_record_set(record_set_id, service):
"""
returns :class:`DSFRecordSet`
:param record_set_id: id of record set you wish to pull up
:param service: id of service which this record belongs. Can either be
the service_id or a :class:`TrafficDirector` Object
:return returns :class:`DSFRecordSet` Object
"""
_service_id = _check_type(service)
uri = '/DSFRecordSet/{}/{}'.format(_service_id, record_set_id)
api_args = {'detail': 'Y'}
response = DynectSession.get_session().execute(uri, 'GET', api_args)
return DSFRecordSet(response['data'].pop('rdata_class'), api=False,
**response['data'])
def get_failover_chain(failover_chain_id, service):
"""
returns :class:`DSFFailoverChain`
:param failover_chain_id: id of :class:`DSFFailoverChain` you wish to pull
up
:param service: id of service which this record belongs. Can either be
the service_id or a :class:`TrafficDirector` Object
:return returns :class:`DSFFailoverChain` Object
"""
_service_id = _check_type(service)
uri = '/DSFRecordSetFailoverChain/{}/{}'.format(_service_id,
failover_chain_id)
api_args = {'detail': 'Y'}
response = DynectSession.get_session().execute(uri, 'GET', api_args)
return DSFFailoverChain(response['data'].pop('label'), api=False,
**response['data'])
def get_response_pool(response_pool_id, service):
"""
returns :class:`DSFResponsePool`
:param response_pool_id: id of :class:`DSFResponsePool` you wish to pull up
:param service: id of service which this record belongs. Can either be
the service_id or a :class:`TrafficDirector` Object
:return returns :class:`DSFResponsePool` Object
"""
_service_id = _check_type(service)
uri = '/DSFResponsePool/{}/{}'.format(_service_id, response_pool_id)
api_args = {'detail': 'Y'}
response = DynectSession.get_session().execute(uri, 'GET', api_args)
return DSFResponsePool(response['data'].pop('label'), api=False,
**response['data'])
def get_ruleset(ruleset_id, service):
"""
returns :class:`DSFRuleset`
:param ruleset_id: id of :class:`DSFRuleset` you wish to pull up
:param service: id of service which this record belongs. Can either be
the service_id or a :class:`TrafficDirector` Object
:return returns :class:`DSFRuleset` Object
"""
_service_id = _check_type(service)
uri = '/DSFRuleset/{}/{}'.format(_service_id, ruleset_id)
api_args = {'detail': 'Y'}
response = DynectSession.get_session().execute(uri, 'GET', api_args)
return DSFRuleset(response['data'].pop('label'), api=False,
**response['data'])
def get_dsf_monitor(monitor_id):
""" A quick :class:`DSFmonitor` getter, for consistency sake.
:param monitor_id: id of :class:`DSFmonitor` you wish to pull up
:return returns :class:`DSFmonitor` Object"""
uri = '/DSFMonitor/{}'.format(monitor_id)
api_args = {'detail': 'Y'}
response = DynectSession.get_session().execute(uri, 'GET', api_args)
return DSFMonitor(api=False, **response['data'])
def _check_type(service):
if isinstance(service, TrafficDirector):
_service_id = service.service_id
elif isinstance(service, string_types):
_service_id = service
else:
raise Exception('Value must be string, or TrafficDirector Object')
return _service_id
def _constructor(record):
return_records = []
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')
return_records.append(constructor(**record_data))
return return_records
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 = self._note = None
self._implicitPublish = True
for key, val in kwargs.items():
setattr(self, '_' + key, val)
def _post(self, dsf_id, record_set_id, publish=True, notes=None):
"""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'
if notes:
api_args['notes'] = notes
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'
if self._note:
new_api_args['notes'] = self._note
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'])
# We hose the note if a publish was requested
if new_api_args.get('publish') == 'Y':
self._note = None
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'
if self._note:
api_args['notes'] = self._note
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'])
# We hose the note if a publish was requested
if api_args.get('publish') == 'Y':
self._note = None
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:
if isinstance(rdata, dict):
for rdatas, rdata_data in rdata.items():
# necessary due to unicode!
try:
for rtype, rdata_v in rdata_data.items():
if rtype == 'rdata_{}'.format(
self._rdata_class.lower()):
for k, v in rdata_v.items():
setattr(self, '_' + k, v)
except:
pass
else:
setattr(self, '_' + key, val)
def publish(self, notes=None):
"""Publish changes to :class:`TrafficDirector`.
:param notes: Optional Note that will be added to the
zone notes of zones attached to this service.
"""
uri = '/DSF/{}/'.format(self._service_id)
api_args = {'publish': 'Y'}
if self._note:
api_args['notes'] = self._note
self._note = None
# if notes are passed in, we override.
if notes:
api_args['notes'] = notes
DynectSession.get_session().execute(uri, 'PUT', api_args)
self.refresh()
@property
def publish_note(self):
"""Returns Current Publish Note, which will be used on the next
publish action"""
return self._note
@publish_note.setter
def publish_note(self, note):
"""Adds this note to the next action which also performs a publish
"""
self._note = note
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, notes=None):
"""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)
:param notes: Optional Zone publish Notes
"""
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 isinstance(record_set, string_types):
if service is None:
msg = ('When record_set as a string, you must provide the '
'service_id as service=')
raise Exception(msg)
_record_set_id = record_set
else:
raise Exception('Could not make sense of Record Set Type')
if service:
_service_id = _check_type(service)
self._post(_service_id, _record_set_id, publish=True,
notes=notes)
@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 implicit_publish(self):
"""Toggle for this specific :class:`DSFRecord` for turning on and off
implicit Publishing for record Updates."""
return self._implicitPublish
@implicit_publish.setter
def implicit_publish(self, value):
if not isinstance(value, bool):
raise Exception('Value must be True or False')
self._implicitPublish = value
implicitPublish = implicit_publish # NOQA
def delete(self, notes=None, publish=True):
"""Delete this :class:`DSFRecord`
:param notes: Optional zone publish notes
:param publish: Publish at run time. Default is True
"""
api_args = {}
if publish and self._implicitPublish:
api_args['publish'] = 'Y'
if notes:
api_args['notes'] = notes
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 = self._note = 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, notes=None):
"""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'
if notes:
api_args['notes'] = notes
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'
if self._note:
api_args['notes'] = self._note
response = DynectSession.get_session().execute(self.uri, 'PUT',
api_args)
self._build(response['data'])
# We hose the note if a publish was requested
if api_args.get('publish') == 'Y':
self._note = None
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, notes=None):
"""Publish changes to :class:`TrafficDirector`.
:param notes: Optional Note that will be added to the zone notes of
zones attached to this service.
"""
uri = '/DSF/{}/'.format(self._service_id)
api_args = {'publish': 'Y'}
if self._note:
api_args['notes'] = self._note
self._note = None
# if notes are passed in, we override.
if notes:
api_args['notes'] = notes
DynectSession.get_session().execute(uri, 'PUT', api_args)
self.refresh()
@property
def publish_note(self):
"""Returns Current Publish Note, which will be
used on the next publish action"""
return self._note
@publish_note.setter
def publish_note(self, note):
"""Adds this note to the next action which also performs a publish
"""
self._note = note
[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, notes=None):
"""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)
:param notes: Optional Zone publish Notes
"""
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 isinstance(failover_chain, string_types):
if service is None:
msg = ('If passing failover_chain as a string, you must '
'provide the service_id as service=')
raise Exception(msg)
_dsf_record_set_failover_chain_id = failover_chain
else:
raise Exception('Could not make sense of Failover Chain Type')
if service:
_service_id = _check_type(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, notes=notes)
@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 isinstance(monitor, string_types):
_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 implicit_publish(self):
"""Toggle for this specific :class:`DSFRecordSet` for turning on and
off implicit Publishing for record Updates.
"""
return self._implicitPublish
@implicit_publish.setter
def implicit_publish(self, value):
if not isinstance(value, bool):
raise Exception('Value must be True or False')
self._implicitPublish = value
implicitPublish = implicit_publish
[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, notes=None, publish=True):
"""Delete this :class:`DSFRecordSet` from the Dynect System
:param notes: Optional zone publish notes
:param publish: Publish at run time. Default is True
"""
api_args = {}
if publish and self._implicitPublish:
api_args['publish'] = 'Y'
if notes:
api_args['notes'] = notes
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._note = None
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, notes=None):
"""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'
if notes:
api_args['notes'] = notes
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'
if self._note:
api_args['notes'] = self._note
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'])
# We hose the note if a publish was requested
if api_args.get('publish') == 'Y':
self._note = None
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, notes=None):
"""Publish changes to :class:`TrafficDirector`.
:param notes: Optional Note that will be added to the zone
notes of zones attached to this service.
"""
uri = '/DSF/{}/'.format(self._service_id)
api_args = {'publish': 'Y'}
if self._note:
api_args['notes'] = self._note
self._note = None
# if notes are passed in, we override.
if notes:
api_args['notes'] = notes
DynectSession.get_session().execute(uri, 'PUT', api_args)
self.refresh()
@property
def publish_note(self):
"""Returns Current Publish Note, which will be
used on the next publish action"""
return self._note
@publish_note.setter
def publish_note(self, note):
"""Adds this note to the next action which also performs a publish
"""
self._note = note
[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, notes=None):
"""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`
:param publish: Publish on execution (Default = True)
:param notes: Optional Zone publish Notes
"""
if isinstance(response_pool, DSFResponsePool):
_response_pool_id = response_pool._dsf_response_pool_id
_service_id = response_pool._service_id
elif isinstance(response_pool, string_types):
if service is None:
msg = ('If passing response_pool as a string, you must '
'provide the service_id as service=')
raise Exception(msg)
_response_pool_id = response_pool
else:
raise Exception('Could not make sense of Response Pool Type')
if service:
_service_id = _check_type(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,
notes=notes)
@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 implicit_publish(self):
"""Toggle for this specific :class:`DSFFailoverChain` for turning on
and off implicit Publishing for record Updates.
"""
return self._implicitPublish
@implicit_publish.setter
def implicit_publish(self, value):
if not isinstance(value, bool):
raise Exception('Value must be True or False')
self._implicitPublish = value
implicitPublish = implicit_publish
[docs] def delete(self, notes=None, publish=True):
"""Delete this :class:`DSFFailoverChain` from the Dynect System
:param notes: Optional zone publish notes
:param publish: Publish at run time. Default is True
"""
api_args = {}
if publish and self._implicitPublish:
api_args['publish'] = 'Y'
if notes:
api_args['notes'] = notes
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 = self._note = 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, notes=None):
"""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': 'N', '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'
if notes:
api_args['notes'] = notes
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'
if self._note:
api_args['notes'] = self._note
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'])
# We hose the note if a publish was requested
if api_args.get('publish') == 'Y':
self._note = None
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, notes=None):
"""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
:param notes: Optional Zone publish Notes
"""
if self._dsf_response_pool_id:
raise Exception('Response Pool Already Exists. ID: {}'.format(
self._dsf_response_pool_id))
_service_id = _check_type(service)
self._post(_service_id, publish=publish, notes=notes)
[docs] def publish(self, notes=None):
"""Publish changes to :class:`TrafficDirector`.
:param notes: Optional Note that will be added to the
zone notes of zones attached to this service.
"""
uri = '/DSF/{}/'.format(self._service_id)
api_args = {'publish': 'Y'}
if self._note:
api_args['notes'] = self._note
self._note = None
# if notes are passed in, we override.
if notes:
api_args['notes'] = notes
DynectSession.get_session().execute(uri, 'PUT', api_args)
self.refresh()
@property
def publish_note(self):
"""Returns Current Publish Note, which will
be used on the next publish action"""
return self._note
@publish_note.setter
def publish_note(self, note):
"""Adds this note to the next action which also performs a publish
"""
self._note = note
[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 implicit_publish(self):
"""Toggle for this specific :class:`DSFResponsePool` for turning on and
off implicit Publishing for record Updates.
"""
return self._implicitPublish
@implicit_publish.setter
def implicit_publish(self, value):
if not isinstance(value, bool):
raise Exception('Value must be True or False')
self._implicitPublish = value
implicitPublish = implicit_publish
[docs] def delete(self, notes=None, publish=True):
"""Delete this :class:`DSFResponsePool` from the DynECT System
:param notes: Optional zone publish notes
:param publish: Publish at run time. Default is True
"""
api_args = {}
if publish and self._implicitPublish:
api_args['publish'] = 'Y'
if notes:
api_args['notes'] = notes
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._ordering = self._note = None
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, notes=None):
"""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._ordering is not None:
api_args['ordering'] = self._ordering
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'
if notes:
api_args['notes'] = notes
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'
if self._note:
api_args['notes'] = self._note
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'])
# We hose the note if a publish was requested
if api_args.get('publish') == 'Y':
self._note = None
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 isinstance(response_pool, string_types):
_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 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
elif isinstance(response_pool, string_types):
_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 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})
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):
msg = ('You must pass in an ordered list of response pool '
'objects, or ids.')
raise Exception(msg)
_pool_list = list()
for list_item in pool_list:
if isinstance(list_item, DSFResponsePool):
_pool_list.append(list_item._dsf_response_pool_id)
elif isinstance(list_item, string_types):
_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, index=None, publish=True, notes=None):
"""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 index: in what position to serve this ruleset. 0 = first.
:param publish: publish at execution time. Default = True
:param notes: Optional Zone publish Notes
"""
if self._dsf_ruleset_id:
raise Exception(
'Rule Set Already Exists. ID: {}'.format(self._dsf_ruleset_id))
_service_id = _check_type(service)
if index is not None:
self._ordering = index
self._post(_service_id, publish=publish, notes=notes)
[docs] def publish(self, notes=None):
"""Publish changes to :class:`TrafficDirector`.
:param notes: Optional Note that will be added to the zone notes
of zones attached to this service.
"""
uri = '/DSF/{}/'.format(self._service_id)
api_args = {'publish': 'Y'}
if self._note:
api_args['notes'] = self._note
self._note = None
# if notes are passed in, we override.
if notes:
api_args['notes'] = notes
DynectSession.get_session().execute(uri, 'PUT', api_args)
self.refresh()
@property
def publish_note(self):
"""Returns Current Publish Note, which will
be used on the next publish action"""
return self._note
@publish_note.setter
def publish_note(self, note):
"""Adds this note to the next action which also performs a publish
"""
self._note = note
[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 = dict()
if isinstance(value, dict):
if value.get('geoip'):
for key, val in value['geoip'].items():
if len(val) != 0:
api_args['criteria_type'] = 'geoip'
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 implicit_publish(self):
"""Toggle for this specific :class:`DSFRuleset` for turning on and
off implicit Publishing for record Updates.
"""
return self._implicitPublish
@implicit_publish.setter
def implicit_publish(self, value):
if not isinstance(value, bool):
raise Exception('Value must be True or False')
self._implicitPublish = value
implicitPublish = implicit_publish
@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, notes=None, publish=True):
"""Remove this :class:`DSFRuleset` from it's associated
:class:`TrafficDirector` Service
:param notes: Optional zone publish notes
:param publish: Publish at run time. Default is True
"""
api_args = {}
if publish and self._implicitPublish:
api_args['publish'] = 'Y'
if notes:
api_args['notes'] = notes
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 endpoint.address == self.address:
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 = None
self._port = self._path = self._host = self._header = None
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 == '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'], link_id=kwargs['link_id'])
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, link_id=None):
for key, val in data.items():
setattr(self, '_' + key, val)
self._link_id = link_id
@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 link_id(self):
""" Link ID connecting thie Notifier to TD service """
return self._link_id
@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')
class DSFNode(object):
"""DSFNode object. Represents a valid fqdn node within a zone. It should be
noted that simply creating a :class:`DSFNode` object does not actually
create anything on the DynECT System. The only way to actively create a
:class:`DSFNode` on the DynECT System is by attaching either a record or a
service to it.
"""
def __init__(self, zone, fqdn=None):
"""Create a :class:`Node` object
:param zone: name of the zone that this Node belongs to
:param fqdn: the fully qualified domain name of this zone
"""
super(DSFNode, self).__init__()
self.zone = zone
self.fqdn = fqdn or self.zone + '.'
self.records = {}
self.recs = {'A': ARecord, 'AAAA': AAAARecord,
'ALIAS': ALIASRecord, 'CAA': CAARecord, 'CDS': CDSRecord,
'CDNSKEY': CDNSKEYRecord, 'CSYNC': CSYNCRecord,
'CERT': CERTRecord, 'CNAME': CNAMERecord,
'DHCID': DHCIDRecord, 'DNAME': DNAMERecord,
'DNSKEY': DNSKEYRecord, 'DS': DSRecord,
'KEY': KEYRecord, 'KX': KXRecord,
'LOC': LOCRecord, 'IPSECKEY': IPSECKEYRecord,
'MX': MXRecord, 'NAPTR': NAPTRRecord,
'PTR': PTRRecord, 'PX': PXRecord,
'NSAP': NSAPRecord, 'RP': RPRecord,
'NS': NSRecord, 'SOA': SOARecord,
'SPF': SPFRecord, 'SRV': SRVRecord,
'TLSA': TLSARecord, 'TXT': TXTRecord,
'SSHFP': SSHFPRecord, 'UNKNOWN': UNKNOWNRecord}
def add_record(self, record_type='A', *args, **kwargs):
"""Adds an a record with the provided data to this :class:`Node`
:param record_type: The type of record you would like to add.
Valid record_type arguments are: 'A', 'AAAA', 'CAA', 'CERT',
'CNAME', 'DHCID', 'DNAME', 'DNSKEY', 'DS', 'KEY', 'KX', 'LOC',
'IPSECKEY', 'MX', 'NAPTR', 'PTR', 'PX', 'NSAP', 'RP', 'NS', 'SOA',
'SPF', 'SRV', and 'TXT'.
:param args: Non-keyword arguments to pass to the Record constructor
:param kwargs: Keyword arguments to pass to the Record constructor
"""
# noinspection PyCallingNonCallable
rec = self.recs[record_type](self.zone, self.fqdn, *args, **kwargs)
if record_type in self.records:
self.records[record_type].append(rec)
else:
self.records[record_type] = [rec]
return rec
def get_all_records(self):
"""Retrieve a list of all record resources for the specified node and
zone combination as well as all records from any Base_Record below that
point on the zone hierarchy
"""
self.records = {}
uri = '/AllRecord/{}/'.format(self.zone)
if self.fqdn is not None:
uri += '{}/'.format(self.fqdn)
api_args = {'detail': 'Y'}
response = DynectSession.get_session().execute(uri, 'GET', api_args)
# Strip out empty record_type lists
record_lists = {label: rec_list for label, rec_list in
response['data'].items() if rec_list != []}
for key, record_list in record_lists.items():
search = key.split('_')[0].upper()
try:
constructor = self.recs[search]
except KeyError:
constructor = self.recs['UNKNOWN']
list_records = []
for record in record_list:
del record['zone']
fqdn = record['fqdn']
del record['fqdn']
# Unpack rdata
for r_key, r_val in record['rdata'].items():
record[r_key] = r_val
record['create'] = False
list_records.append(constructor(self.zone, fqdn, **record))
self.records[key] = list_records
return self.records
def get_all_records_by_type(self, record_type):
"""Get a list of all :class:`DNSRecord` of type ``record_type`` which
are owned by this node.
:param record_type: The type of :class:`DNSRecord` you wish returned.
Valid record_type arguments are: 'A', 'AAAA', 'CAA', 'CERT',
'CNAME', 'DHCID', 'DNAME', 'DNSKEY', 'DS', 'KEY', 'KX', 'LOC',
'IPSECKEY', 'MX', 'NAPTR', 'PTR', 'PX', 'NSAP', 'RP', 'NS', 'SOA',
'SPF', 'SRV', and 'TXT'.
:return: A list of :class:`DNSRecord`'s
"""
names = {'A': 'ARecord', 'AAAA': 'AAAARecord',
'CAA': 'CAARecord', 'CERT': 'CERTRecord',
'CNAME': 'CNAMERecord', 'DHCID': 'DHCIDRecord',
'DNAME': 'DNAMERecord', 'DNSKEY': 'DNSKEYRecord',
'DS': 'DSRecord', 'KEY': 'KEYRecord', 'KX': 'KXRecord',
'LOC': 'LOCRecord', 'IPSECKEY': 'IPSECKEYRecord',
'MX': 'MXRecord', 'NAPTR': 'NAPTRRecord', 'PTR': 'PTRRecord',
'PX': 'PXRecord', 'NSAP': 'NSAPRecord', 'RP': 'RPRecord',
'NS': 'NSRecord', 'SOA': 'SOARecord', 'SPF': 'SPFRecord',
'SRV': 'SRVRecord', 'TLSA': 'TLSARecord', 'TXT': 'TXTRecord',
'SSHFP': 'SSHFPRecord', 'ALIAS': 'ALIASRecord'}
constructor = self.recs[record_type]
uri = '/{}/{}/{}/'.format(names[record_type], self.zone,
self.fqdn)
api_args = {'detail': 'Y'}
response = DynectSession.get_session().execute(uri, 'GET', api_args)
records = []
for record in response['data']:
fqdn = record['fqdn']
del record['fqdn']
del record['zone']
# Unpack rdata
for key, val in record['rdata'].items():
record[key] = val
del record['rdata']
record['create'] = False
records.append(constructor(self.zone, fqdn, **record))
return records
def get_any_records(self):
"""Retrieve a list of all recs"""
if self.fqdn is None:
return
api_args = {'detail': 'Y'}
uri = '/ANYRecord/{}/{}/'.format(self.zone, self.fqdn)
response = DynectSession.get_session().execute(uri, 'GET', api_args)
# Strip out empty record_type lists
record_lists = {label: rec_list for label, rec_list in
response['data'].items() if rec_list != []}
for key, record_list in record_lists.items():
search = key.split('_')[0].upper()
try:
constructor = self.recs[search]
except KeyError:
constructor = self.recs['UNKNOWN']
list_records = []
for record in record_list:
del record['zone']
del record['fqdn']
# Unpack rdata
for r_key, r_val in record['rdata'].items():
record[r_key] = r_val
record['create'] = False
list_records.append(
constructor(self.zone, self.fqdn, **record))
self.records[key] = list_records
return self.records
def delete(self):
"""Delete this node, any records within this node, and any nodes
underneath this node
"""
uri = '/Node/{}/{}'.format(self.zone, self.fqdn)
DynectSession.get_session().execute(uri, 'DELETE', {})
def __str__(self):
"""str override"""
return force_unicode('<DSFNode>: {}').format(self.fqdn)
__repr__ = __unicode__ = __str__
def __bytes__(self):
"""bytes override"""
return bytes(self.__str__())
[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 notes: Optional Publish Zone Notes.
: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._note = None
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, notes=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,
'notes': notes}
if ttl:
api_args['ttl'] = self._ttl
if nodes:
_nodeList = []
if isinstance(nodes, list):
for node in nodes:
if isinstance(node, DSFNode) or\
type(node).__name__ == '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, DSFNode) or type(nodes).__name__ == '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 isinstance(notifier, string_types):
api_args['notifiers'].append({'notifier_id': notifier})
else:
msg = ('notifiers must be a list containing DSFNotifier '
'objects, or notifier_id strings.')
raise Exception(msg)
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 = [DSFNode(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'
if self._note:
api_args['notes'] = self._note
self.uri = '/DSF/{}/'.format(self._service_id)
response = DynectSession.get_session().execute(self.uri, 'PUT',
api_args)
self._build(response['data'])
# We hose the note if a publish was requested
if api_args.get('publish', None):
if api_args.get('publish') == 'Y':
self._note = None
[docs] def publish(self, notes=None):
"""Publish changes to :class:`TrafficDirector`.
:param notes: Optional Note that will be added to
the zone notes of zones attached to this service.
"""
uri = '/DSF/{}/'.format(self._service_id)
api_args = {'publish': 'Y'}
if self._note:
api_args['notes'] = self._note
self._note = None
# if notes are passed in, we override.
if notes:
api_args['notes'] = notes
DynectSession.get_session().execute(uri, 'PUT', api_args)
self.refresh()
@property
def publish_note(self):
"""Returns Current Publish Note, which will
be used on the next publish action"""
return self._note
@publish_note.setter
def publish_note(self, note):
"""Adds this note to the next action which also performs a publish
"""
self._note = note
[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, notes=None):
"""Links the :class:`DSFNotifier` with the specified id
to this Traffic Director service, Accepts :class:`DSFNotifier`
or :class:`Notifier` or the notifier public id.
"""
if isinstance(notifier, DSFNotifier):
_notifier_id = notifier._notifier_id
elif isinstance(notifier, Notifier):
_notifier_id = notifier._notifier_id
elif isinstance(notifier, string_types):
_notifier_id = notifier
else:
msg = ('Cannot sensibly determine Notifier type, must be '
'DSFNotifier, or notifier_id string')
raise Exception(msg)
api_args = {'add_notifier': True, 'notifier_id': _notifier_id}
if notes:
api_args['notes'] = notes
self._update(api_args)
[docs] def del_notifier(self, notifier, notes=None):
"""delinks the :class:`DSFNotifier` with the specified id to
this Traffic Director service. Accepts :class:`DSFNotifier` or
:class:`Notifier`.
"""
if isinstance(notifier, DSFNotifier):
_link_id = notifier._link_id
elif isinstance(notifier, Notifier):
_link_id = notifier._link_id
else:
raise Exception("Cannot sensibly determine Notifier type,\
must be DSFNotifier, or notifier_id string")
api_args = {'remove_notifier': True, 'link_id': _link_id}
if notes:
api_args['notes'] = notes
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)
[docs] def replace_all_rulesets(self, rulesets):
"""This request will replace all rulesets with a new list of rulesets.
:param rulesets: a list of rulesets :class:DSFRuleset to be published
to the service Warning! This call takes extra time as it is several
api calls.
"""
if isinstance(rulesets, Iterable) and isinstance(rulesets[0],
DSFRuleset):
old_rulesets = self.all_rulesets
for old_rule in old_rulesets:
old_rule.delete()
for new_rule in rulesets:
new_rule._dsf_ruleset_id = None
new_rule.create(self)
else:
raise Exception(
"rulesets parameter must be a list of DSFRuleset objects")
[docs] def replace_one_ruleset(self, ruleset):
"""This request will replace a single ruleset and maintain the order
of the list.
Warning! This call takes extra time as it is several api calls.
:param ruleset: A single object of :class:DSFRuleset` The replacement
is keyed by the DSFRuleset label value.
"""
if isinstance(ruleset, DSFRuleset):
old_rulesets = self.all_rulesets
old_rule = None
rule_index = 0
for rule in old_rulesets:
if rule.label == ruleset.label:
old_rule = rule
rule_index += 1
break
rule_index += 1
if old_rule is not None:
old_rule.delete()
ruleset._dsf_ruleset_id = None
ruleset.create(self, index=rule_index)
else:
ruleset.create(self)
else:
msg = ('rulesets parameter must be a single object of class '
'DSFRuleset')
raise Exception(msg)
@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 failover_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
[docs] def order_rulesets(self, ruleset_list, publish=True):
"""For reordering the ruleset list. simply pass in a ``list`` of
:class:`DSFRulesets`s in the order you wish them to be served.
:param ruleset_list: ordered ``list`` of :class:`DSFRulesets`
:param publish: Publish on execution. default = True
"""
if not isinstance(ruleset_list, list):
msg = ('You must pass in an ordered list of response pool '
'objects, or ids.')
raise Exception(msg)
_ruleset_list = list()
for list_item in ruleset_list:
if isinstance(list_item, DSFRuleset):
_ruleset_list.append(list_item._dsf_ruleset_id)
elif isinstance(list_item, string_types):
_ruleset_list.append(list_item)
api_args = dict()
api_args['rulesets'] = list()
for ruleset_id in _ruleset_list:
api_args['rulesets'].append({'dsf_ruleset_id': ruleset_id})
self._update(api_args, publish)
@property
def node_objects(self):
"""A list of :class:`DSFNode` 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 = [DSFNode(node['zone'], node['fqdn']) for node
in response['data']]
return self._nodes
nodeObjects = node_objects
@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 = [DSFNode(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:`DSFNode` 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, DSFNode) or type(node).__name__ == '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, DSFNode) or type(nodes).__name__ == 'Node':
_nodeList.append({'zone': nodes.zone, 'fqdn': nodes.fqdn})
uri = '/DSFNode/{}'.format(self._service_id)
publish = "Y" if self._implicitPublish else "N"
api_args = {'nodes': _nodeList, 'publish': publish}
response = DynectSession.get_session().execute(uri, 'PUT',
api_args)
self._nodes = [DSFNode(node['zone'], node['fqdn']) for node
in response['data']]
[docs] def add_node(self, node):
"""A :class:`DSFNode` object, or a zone, FQDN pair in a hash to be added
to this :class:`TrafficDirector` service
"""
if isinstance(node, DSFNode) or type(node).__name__ == 'Node':
_node = {'zone': node.zone, 'fqdn': node.fqdn}
elif isinstance(node, dict):
_node = node
uri = '/DSFNode/{}'.format(self._service_id)
publish = "Y" if self._implicitPublish else "N"
api_args = {'node': _node, 'publish': publish}
response = DynectSession.get_session().execute(uri, 'POST',
api_args)
self._nodes = [DSFNode(nd['zone'], nd['fqdn']) for nd
in response['data']]
[docs] def remove_node(self, node):
"""A :class:`DSFNode` object, or a zone, FQDN pair in a hash to be
removed to this :class:`TrafficDirector` service
"""
if isinstance(node, DSFNode) or type(node).__name__ == 'Node':
_node = {'zone': node.zone, 'fqdn': node.fqdn}
elif isinstance(node, dict):
_node = node
uri = '/DSFNode/{}'.format(self._service_id)
publish = "Y" if self._implicitPublish else "N"
api_args = {'node': _node, 'publish': publish}
response = DynectSession.get_session().execute(uri, 'DELETE',
api_args)
self._nodes = [DSFNode(nd['zone'], nd['fqdn']) for nd
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 implicit_publish(self):
"""Toggle for this specific :class:`TrafficDirector` for turning on and
off implicit Publishing for record Updates.
"""
return self._implicitPublish
@implicit_publish.setter
def implicit_publish(self, value):
if not isinstance(value, bool):
raise Exception('Value must be True or False')
self._implicitPublish = value
implicitPublish = implicit_publish
[docs] def delete(self):
"""Delete this :class:`TrafficDirector` from the DynECT System
:param notes: Optional zone publish notes
"""
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__())