class GSLBRegion(object): """docstring for GSLBRegion""" def __init__(self, zone, fqdn, region_code, *args, **kwargs): """Create a :class:`GSLBRegion` object :param zone: Zone monitored by this :class:`GSLBRegion` :param fqdn: The fqdn of the specific node which will be monitored by this :class:`GSLBRegion` :param region_code: ISO region code of this :class:`GSLBRegion` :param pool: (*arg) The IP Pool list for this :class:`GSLBRegion` :param serve_count: How many records will be returned in each DNS response :param failover_mode: How the :class:`GSLBRegion` should failover. Must be one of 'ip', 'cname', 'region', 'global' :param failover_data: Dependent upon failover_mode. Must be one of 'ip', 'cname', 'region', 'global' """ super(GSLBRegion, self).__init__() self.valid_region_codes = ('US West', 'US Central', 'US East', 'Asia', 'EU West', 'EU Central', 'EU East', 'global') self.valid_modes = ('ip', 'cname', 'region', 'global') self._zone = zone self._fqdn = fqdn self._pool = self._serve_count = self._failover_mode = None self._failover_data = None self._task_id = None if region_code not in self.valid_region_codes: raise DynectInvalidArgumentError('region_code', region_code, self.valid_region_codes) self._region_code = region_code self.uri = '/GSLBRegion/{}/{}/{}/'.format(self._zone, self._fqdn, self._region_code) self._pool = [] if len(args) == 0 and len(kwargs) == 0: self._get() if len(kwargs) > 0: self._build(kwargs) elif len(args) > 0: for pool in args[0]: if isinstance(pool, dict): self._pool.append(GSLBRegionPoolEntry(self._zone, self._fqdn, self._region_code, **pool)) else: self._pool.append(pool) def _post(self, pool, serve_count=None, failover_mode=None, failover_data=None): """Create a new :class:`GSLBRegion` on the DynECT System""" self._pool = pool self._serve_count = serve_count self._failover_mode = failover_mode self._failover_data = failover_data uri = '/GSLBRegion/{}/{}/'.format(self._zone, self._fqdn) api_args = {'pool': self._pool.to_json(), 'region_code': self._region_code} if serve_count: api_args['serve_count'] = self._serve_count if self._failover_mode: if self._failover_mode not in self.valid_modes: raise DynectInvalidArgumentError('failover_mode', self._failover_mode, self.valid_modes) api_args['failover_mode'] = self._failover_mode if self._failover_data: if self._failover_data not in self.valid_modes: raise DynectInvalidArgumentError('failover_data', self._failover_data, self.valid_modes) api_args['failover_data'] = self._failover_data response = DynectSession.get_session().execute(uri, 'POST', api_args) self._build(response['data']) def _get(self): """Get an existing :class:`GSLBRegion` object""" api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args): """Private udpate method for PUT commands""" response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): self._pool = [] for key, val in data.items(): if key == 'pool': for pool in val: if isinstance(pool, dict): self._pool.append(GSLBRegionPoolEntry( self._zone, self._fqdn, self._region_code, **pool) ) else: self._pool.append(pool) elif key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) @property def task(self): """:class:`Task` for most recent system action on this :class:`ActiveFailover`.""" if self._task_id: self._task_id.refresh() return self._task_id def sync(self): """Sync this :class:`GSLBRegion` object with the DynECT System""" self._get() @property def zone(self): """Zone monitored by this :class:`GSLBRegion`""" return self._zone @zone.setter def zone(self, value): pass @property def fqdn(self): """The fqdn of the specific node which will be monitored by this :class:`GSLBRegion` """ return self._fqdn @fqdn.setter def fqdn(self, value): pass @property def region_code(self): """ISO region code of this :class:`GSLBRegion`""" return self._region_code @region_code.setter def region_code(self, value): pass @property def serve_count(self): """How many records will be returned in each DNS response""" return self._serve_count @serve_count.setter def serve_count(self, value): self._serve_count = value api_args = {'serve_count': self._serve_count} self._update(api_args) @property def failover_mode(self): """How the :class:`GSLBRegion` should failover. Must be one of 'ip', 'cname', 'region', 'global' """ return self._failover_mode @failover_mode.setter def failover_mode(self, value): self._failover_mode = value api_args = {'failover_mode': self._failover_mode} self._update(api_args) @property def failover_data(self): """Dependent upon failover_mode. Must be one of 'ip', 'cname', 'region', 'global' """ return self._failover_data @failover_data.setter def failover_data(self, value): self._failover_data = value api_args = {'failover_data': self._failover_data} self._update(api_args) @property def pool(self): """The IP Pool list for this :class:`GSLBRegion`""" return self._pool @pool.setter def pool(self, value): self._pool = value api_args = {'pool': self._pool.to_json()} self._update(api_args) @property def _json(self): """Convert this :class:`GSLBRegion` to a json blob""" output = {'region_code': self.region_code, 'pool': [pool.to_json() for pool in self._pool]} if self._serve_count: output['serve_count'] = self._serve_count if self._failover_mode: output['failover_mode'] = self._failover_mode if self._failover_data: output['failover_data'] = self._failover_data return output def delete(self): """Delete this :class:`GSLBRegion`""" api_args = {} DynectSession.get_session().execute(self.uri, 'DELETE', api_args) def __str__(self): """str override""" return force_unicode('<GSLBRegion>: {}').format(self._region_code) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())
class GSLB(object): """A Global Server Load Balancing (GSLB) service""" def __init__(self, zone, fqdn, *args, **kwargs): """Create a :class:`GSLB` object :param auto_recover: Indicates whether or not the service should automatically come out of failover when the IP addresses resume active status or if the service should remain in failover until manually reset. Must be 'Y' or 'N' :param ttl: Time To Live in seconds of records in the service. Must be less than 1/2 of the Health Probe's monitoring interval. Must be one of 30, 60, 150, 300, or 450 :param notify_events: A comma separated list of the events which trigger notifications. Must be one of 'ip', 'svc', or 'nosrv' :param syslog_server: The Hostname or IP address of a server to receive syslog notifications on monitoring events :param syslog_port: The port where the remote syslog server listens for notifications :param syslog_ident: The ident to use when sending syslog notifications :param syslog_facility: The syslog facility to use when sending syslog notifications. Must be one of 'kern', 'user', 'mail', 'daemon', 'auth', 'syslog', 'lpr', 'news', 'uucp', 'cron', 'authpriv', 'ftp', 'ntp', 'security', 'console', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', or 'local7' :param syslog_delivery: The syslog delivery action type. 'all' will deliver notifications no matter what the endpoint state. 'change' (default) will deliver only on change in the detected endpoint state :param region: A list of :class:`GSLBRegion`'s :param monitor: The health :class:`Monitor` for this service :param contact_nickname: Name of contact to receive notifications :param syslog_probe_fmt: see below for format: :param syslog_status_fmt: see below for format: Use the following format for syslog_xxxx_fmt paramaters. %hos hostname %tim current timestamp or monitored interval %reg region code %sta status %ser record serial %rda rdata %sit monitoring site %rti response time %msg message from monitoring %adr address of monitored node %med median value %rts response times (RTTM) :param recovery_delay: number of up status polling intervals to consider service up """ super(GSLB, self).__init__() self.valid_auto_recover = ('Y', 'N') self.valid_ttls = (30, 60, 150, 300, 450) self.valid_notify_events = ('ip', 'svc', 'nosrv') self.valid_syslog_facility = ('kern', 'user', 'mail', 'daemon', 'auth', 'syslog', 'lpr', 'news', 'uucp', 'cron', 'authpriv', 'ftp', 'ntp', 'security', 'console', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7') self._zone = zone self._fqdn = fqdn self.uri = '/GSLB/{}/{}/'.format(self._zone, self._fqdn) self._auto_recover = self._ttl = self._notify_events = None self._syslog_server = self._syslog_port = self._syslog_ident = None self._syslog_facility = self._monitor = self._contact_nickname = None self._syslog_probe_fmt = self._syslog_status_fmt = None self._active = self._status = self._syslog_delivery = None self._task_id = None self._recovery_delay = None self._region = APIList(DynectSession.get_session, 'region') if 'api' in kwargs: del kwargs['api'] self._build(kwargs) elif len(args) == 0 and len(kwargs) == 0: self._get() else: self._post(*args, **kwargs) self._region.uri = self.uri def _post(self, contact_nickname, region, auto_recover=None, ttl=None, notify_events=None, syslog_server=None, syslog_port=514, syslog_ident='dynect', syslog_facility='daemon', syslog_probe_fmt=None, syslog_status_fmt=None, monitor=None, syslog_delivery='change', recovery_delay=None): """Create a new :class:`GSLB` service object on the DynECT System""" self._auto_recover = auto_recover self._ttl = ttl self._notify_events = notify_events self._syslog_server = syslog_server self._syslog_port = syslog_port self._syslog_ident = syslog_ident self._syslog_facility = syslog_facility self._syslog_delivery = syslog_delivery self._syslog_probe_fmt = syslog_probe_fmt self._syslog_status_fmt = syslog_status_fmt self._recovery_delay = recovery_delay self._region += region self._monitor = monitor self._contact_nickname = contact_nickname api_args = {'contact_nickname': self._contact_nickname, 'region': [r._json for r in self._region]} if auto_recover: api_args['auto_recover'] = self._auto_recover if ttl: api_args['ttl'] = self._ttl if notify_events: api_args['notify_events'] = self._notify_events if syslog_server: api_args['syslog_server'] = self._syslog_server if syslog_port: api_args['syslog_port'] = self._syslog_port if syslog_ident: api_args['syslog_ident'] = self._syslog_ident if syslog_facility: api_args['syslog_facility'] = self._syslog_facility if syslog_delivery: api_args['syslog_delivery'] = self._syslog_delivery if syslog_probe_fmt: api_args['syslog_probe_fmt'] = self._syslog_probe_fmt if syslog_status_fmt: api_args['syslog_status_fmt'] = self._syslog_status_fmt if recovery_delay: api_args['recovery_delay'] = self._recovery_delay if monitor: api_args['monitor'] = self._monitor.to_json() self._monitor.zone = self._zone self._monitor.fqdn = self._fqdn response = DynectSession.get_session().execute(self.uri, 'POST', api_args) self._build(response['data']) def _get(self): """Get an existing :class:`GSLB` service object""" api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _build(self, data, region=True): """Private method which builds the objects fields based on the data returned by an API call :param data: the data from the JSON respnose :param region: Boolean flag specifying whether to rebuild the region objects or not """ for key, val in data.items(): if key == 'region': if region: self._region = APIList(DynectSession.get_session, 'region') for region in val: region_code = region.pop('region_code', None) self._region.append(GSLBRegion(self._zone, self._fqdn, region_code, **region)) elif key == 'monitor': # We already have the monitor object, no need to rebuild it pass elif key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) self._region.uri = self.uri @property def task(self): """:class:`Task` for most recent system action on this :class:`GSLB`. """ if self._task_id: self._task_id.refresh() return self._task_id def sync(self): """Sync this :class:`GSLB` object with the DynECT System""" self._get() def activate(self): """Activate this :class:`GSLB` service on the DynECT System""" api_args = {'activate': True} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) def deactivate(self): """Deactivate this :class:`GSLB` service on the DynECT System""" api_args = {'deactivate': True} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) def recover(self, address=None): """Recover the GSLB service on the designated zone node or a specific node IP within the service """ api_args = {} if address: api_args['recoverip'] = True api_args['address'] = address else: api_args['recover'] = True response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def auto_recover(self): """Indicates whether or not the service should automatically come out of failover when the IP addresses resume active status or if the service should remain in failover until manually reset. Must be 'Y' or 'N' """ return self._auto_recover @auto_recover.setter def auto_recover(self, value): if value not in self.valid_auto_recover: raise DynectInvalidArgumentError('auto_recover', value, self.valid_auto_recover) self._auto_recover = value api_args = {'auto_recover': self._auto_recover} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def status(self): """The current state of the service. Will be one of 'unk', 'ok', 'trouble', or 'failover' """ api_args = {} respnose = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._status = respnose['data']['status'] return self._status @status.setter def status(self, value): pass @property def active(self): """Indicates if the service is active. When setting directly, rather than using activate/deactivate valid arguments are 'Y' or True to activate, or 'N' or False to deactivate. Note: If your service is already active and you try to activate it, nothing will happen. And vice versa for deactivation. :returns: An :class:`Active` object representing the current state of this :class:`GSLB` Service """ return self._active @active.setter def active(self, value): deactivate = ('N', False) activate = ('Y', True) if value in deactivate and self.active: self.deactivate() elif value in activate and not self.active: self.activate() @property def ttl(self): """Time To Live in seconds of records in the service. Must be less than 1/2 of the Health Probe's monitoring interval. Must be one of 30, 60, 150, 300, or 450 """ return self._ttl @ttl.setter def ttl(self, value): if value not in self.valid_ttls: raise DynectInvalidArgumentError('ttl', value, self.valid_ttls) self._ttl = value api_args = {'ttl': self._ttl} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def notify_events(self): """A comma separated list of the events which trigger notifications. Must be one of 'ip', 'svc', or 'nosrv' """ return self._notify_events @notify_events.setter def notify_events(self, value): if value not in self.valid_notify_events: raise DynectInvalidArgumentError('notify_events', value, self.valid_notify_events) self._notify_events = value api_args = {'notify_events': self._notify_events} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def syslog_server(self): """The Hostname or IP address of a server to receive syslog notifications on monitoring events """ self._get() return self._syslog_server @syslog_server.setter def syslog_server(self, value): self._syslog_server = value api_args = {'syslog_server': self._syslog_server} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def syslog_port(self): """The port where the remote syslog server listens for notifications""" self._get() return self._syslog_port @syslog_port.setter def syslog_port(self, value): self._syslog_port = value api_args = {'syslog_port': self._syslog_port} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def syslog_ident(self): """The ident to use when sending syslog notifications""" self._get() return self._syslog_ident @syslog_ident.setter def syslog_ident(self, value): self._syslog_ident = value api_args = {'syslog_ident': self._syslog_ident} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def syslog_facility(self): """The syslog facility to use when sending syslog notifications. Must be one of 'kern', 'user', 'mail', 'daemon', 'auth', 'syslog', 'lpr', 'news', 'uucp', 'cron', 'authpriv', 'ftp', 'ntp', 'security', 'console', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', or 'local7' """ self._get() return self._syslog_facility @syslog_facility.setter def syslog_facility(self, value): if value not in self.valid_syslog_facility: raise DynectInvalidArgumentError('syslog_facility', value, self.valid_syslog_facility) self._syslog_facility = value api_args = {'syslog_facility': self._syslog_facility} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def syslog_delivery(self): self._get() return self._syslog_delivery @syslog_delivery.setter def syslog_delivery(self, value): api_args = {'syslog_delivery': value} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def syslog_probe_format(self): self._get() return self._syslog_probe_fmt @syslog_probe_format.setter def syslog_probe_format(self, value): api_args = {'syslog_probe_fmt': value} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def syslog_status_format(self): self._get() return self._syslog_status_fmt @syslog_status_format.setter def syslog_status_format(self, value): api_args = {'syslog_status_fmt': value} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def recovery_delay(self): self._get() return self._recovery_delay @recovery_delay.setter def recovery_delay(self, value): api_args = {'recovery_delay': value} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) @property def region(self): """A list of :class:`GSLBRegion`'s""" return self._region @region.setter def region(self, value): if isinstance(value, list) and not isinstance(value, APIList): self._region = APIList(DynectSession.get_session, 'region', None, value) elif isinstance(value, APIList): self._region = value self._region.uri = self.uri @property def monitor(self): """The health :class:`Monitor` for this service""" return self._monitor @monitor.setter def monitor(self, value): # We're only going accept new monitors of type Monitor if isinstance(value, Monitor): api_args = {'monitor': value.to_json()} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) self._monitor = value @property def contact_nickname(self): """Name of contact to receive notifications from this :class:`GSLB` service """ return self._contact_nickname @contact_nickname.setter def contact_nickname(self, value): self._contact_nickname = value api_args = {'contact_nickname': self._contact_nickname} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data'], region=False) def delete(self): """Delete this :class:`GSLB` service from the DynECT System""" api_args = {} DynectSession.get_session().execute(self.uri, 'DELETE', api_args) def __str__(self): """str override""" return force_unicode('<GSLB>: {}').format(self._fqdn) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())
class Zone(object): """A class representing a DynECT Zone""" def __init__(self, name, *args, **kwargs): """Create a :class:`Zone` object. Note: When creating a new :class:`Zone` if no contact is specified the path to a local zone file must be passed to the ``file_name`` param. :param name: the name of the zone to create :param contact: Administrative contact for this zone :param ttl: TTL (in seconds) for records in the zone :param serial_style: The style of the zone's serial. Valid values: increment, epoch, day, minute :param file_name: The path to a valid RFC1035, BIND, or tinydns style Master file. Note: this file must be under 1mb in size. :param master_ip: The IP of the master server from which to fetch zone data for Transferring this :class:`Zone`. Note: This argument is required for performing a valid ZoneTransfer operation. :param timeout: The time, in minutes, to wait for a zone xfer to complete """ super(Zone, self).__init__() self.valid_serials = ('increment', 'epoch', 'day', 'minute') self._name = name self._fqdn = self._name if self._fqdn and not self._fqdn.endswith('.'): self._fqdn += '.' self._contact = self._ttl = self._serial_style = self._serial = None self._zone = self._status = None self.records = {} self._task_id = None self.services = {} self.uri = '/Zone/{}/'.format(self._name) if 'api' in kwargs: del kwargs['api'] for key, val in kwargs.items(): setattr(self, '_' + key, val) self._name = self._zone self.uri = '/Zone/{}/'.format(self._name) elif len(args) == 0 and len(kwargs) == 0: self._get() else: self._post(*args, **kwargs) self._status = 'active' def _post(self, contact=None, ttl=60, serial_style='increment', file_name=None, master_ip=None, timeout=None): """Create a new :class:`Zone` object on the DynECT System""" if contact is None and file_name is None and master_ip is None: raise DynectInvalidArgumentError('contact', None) if file_name is not None: self._post_with_file(file_name) elif master_ip is not None: self._xfer(master_ip, timeout) else: self._contact = contact self._ttl = ttl if serial_style not in self.valid_serials: raise DynectInvalidArgumentError(serial_style, self.valid_serials) self._serial_style = serial_style api_args = {'zone': self._name, 'rname': self._contact, 'ttl': self._ttl, 'serial_style': self._serial_style} response = DynectSession.get_session().execute(self.uri, 'POST', api_args) self._build(response['data']) def _post_with_file(self, file_name): """Create a :class:`Zone` from a RFC1035 style Master file. A ZoneFile for BIND or tinydns will also be accepted :param file_name: The path to a valid ZoneFile """ full_path = os.path.abspath(file_name) file_size = os.path.getsize(full_path) if file_size > 1048576: raise DynectInvalidArgumentError('Zone File Size', file_size, 'Under 1MB') else: uri = '/ZoneFile/{}/'.format(self.name) f = open(full_path, 'r') content = f.read() f.close() api_args = {'file': content} response = DynectSession.get_session().execute( uri, 'POST', api_args) self.__poll_for_get() self._build(response['data']) def _xfer(self, master_ip, timeout=None): """Create a :class:`Zone` by ZoneTransfer by providing an optional master_ip argument. """ uri = '/ZoneTransfer/{}/'.format(self.name) api_args = {'master_ip': master_ip} response = DynectSession.get_session().execute(uri, 'POST', api_args) self._build(response['data']) time_out = timeout or 10 count = 0 while count < time_out: response = DynectSession.get_session().execute(uri, 'GET', {}) if response['status'] == 'running' and response['message'] == '': sleep(60) count += 1 else: break self._get() def __poll_for_get(self, n_loops=10, xfer=False, xfer_master_ip=None): """For use ONLY by _post_with_file and _xfer. Will wait at MOST ``n_loops * 2`` seconds for a successfull GET API response. If no successfull get is recieved no error will be raised. """ count = 0 got = False while count < n_loops: try: self._get() got = True break except DynectGetError: sleep(2) count += 1 if not got and xfer: uri = '/ZoneTransfer/{}/'.format(self.name) api_args = {} if xfer_master_ip is not None: api_args['master_ip'] = xfer_master_ip response = DynectSession.get_session().execute(uri, 'GET', api_args) error_labels = ['running', 'waiting', 'failed', 'canceled'] ok_labels = ['ready', 'unpublished', 'ok'] if response['data']['status'] in error_labels: raise DynectCreateError(response['msgs']) elif response['data']['status'] in ok_labels: self._get() else: pass # Should never get here def _get(self): """Get an existing :class:`Zone` object from the DynECT System""" api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _build(self, data): """Build the variables in this object by pulling out the data from data """ for key, val in data.items(): if key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) def _update(self, api_args): """Update this :class:`ActiveFailover`, via the API, with the args in api_args """ response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) @property def __root_soa(self): """Return the SOA record associated with this Zone""" return self.get_all_records_by_type('SOA')[0] @property def name(self): """The name of this :class:`Zone`""" return self._name @name.setter def name(self, value): pass @property def fqdn(self): """The name of this :class:`Zone`""" return self._fqdn @fqdn.setter def fqdn(self, value): pass @property def contact(self): """The email address of the primary :class:`Contact` associated with this :class:`Zone` """ self._contact = self.__root_soa.rname return self._contact @contact.setter def contact(self, value): self.__root_soa.rname = value @property def ttl(self): """This :class:`Zone`'s default TTL""" self._ttl = self.__root_soa.ttl return self._ttl @ttl.setter def ttl(self, value): self.__root_soa.ttl = value @property def serial(self): """The current serial of this :class:`Zone`""" self._get() return self._serial @serial.setter def serial(self, value): pass @property def serial_style(self): """The current serial style of this :class:`Zone`""" self._get() return self._serial_style @serial_style.setter def serial_style(self, value): if value not in self.valid_serials: raise DynectInvalidArgumentError('serial_style', value, self.valid_serials) self.__root_soa.serial_style = value @property def status(self): """Convenience property for :class:`Zones`. If a :class:`Zones` is frozen the status will read as `'frozen'`, if the :class:`Zones` is not frozen the status will read as `'active'`. Because the API does not return information about whether or not a :class:`Zones` is frozen there will be a few cases where this status will be `None` in order to avoid guessing what the current status actually is. """ self._get() return self._status @status.setter def status(self, value): pass def freeze(self): """Causes the zone to become frozen. Freezing a zone prevents changes to the zone until it is thawed. """ api_args = {'freeze': True} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) if response['status'] == 'success': self._status = 'frozen' def thaw(self): """Causes the zone to become thawed. Thawing a frozen zone allows changes to again be made to the zone. """ api_args = {'thaw': True} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) if response['status'] == 'success': self._status = 'active' def publish(self, notes=None): """Causes all pending changes to become part of the zone. The serial number increments based on its serial style and the data is pushed out to the nameservers. """ api_args = {'publish': True} if notes: api_args['notes'] = notes self._update(api_args) @property def task(self): """:class:`Task` for most recent system action on this :class:`Zone`. """ if self._task_id: self._task_id.refresh() return self._task_id def get_notes(self, offset=None, limit=None): """Generates a report containing the Zone Notes for this :class:`Zone` :param offset: The starting point at which to retrieve the notes :param limit: The maximum number of notes to be retrieved :return: A :class:`list` of :class:`dict` containing :class:`Zone` Notes """ uri = '/ZoneNoteReport/' api_args = {'zone': self.name} if offset: api_args['offset'] = offset if limit: api_args['limit'] = limit response = DynectSession.get_session().execute(uri, 'POST', api_args) return response['data'] def add_record(self, name=None, record_type='A', *args, **kwargs): """Adds an a record with the provided name and data to this :class:`Zone` :param name: The name of the node where this record will be added :param record_type: The type of record you would like to add. Valid record_type arguments are: 'A', 'AAAA', '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 """ fqdn = name + '.' + self.name + '.' if name else self.name + '.' # noinspection PyCallingNonCallable rec = RECS[record_type](self.name, fqdn, *args, **kwargs) if record_type in self.records: self.records[record_type].append(rec) else: self.records[record_type] = [rec] return rec def add_service(self, name=None, service_type=None, *args, **kwargs): """Add the specified service type to this zone, or to a node under this zone :param name: The name of the :class:`Node` where this service will be attached to or `None` to attach it to the root :class:`Node` of this :class:`Zone` :param service_type: The type of the service you would like to create. Valid service_type arguments are: 'ActiveFailover', 'DDNS', 'DNSSEC', 'DSF', 'GSLB', 'RDNS', 'RTTM', 'HTTPRedirect' :param args: Non-keyword arguments to pass to the Record constructor :param kwargs: Keyword arguments to pass to the Record constructor """ constructors = {'ActiveFailover': ActiveFailover, 'DDNS': DynamicDNS, 'DNSSEC': DNSSEC, 'DSF': TrafficDirector, 'GSLB': GSLB, 'RDNS': ReverseDNS, 'RTTM': RTTM, 'HTTPRedirect': HTTPRedirect} fqdn = self.name + '.' if name: fqdn = name + '.' + fqdn if service_type == 'DNSSEC': # noinspection PyCallingNonCallable service = constructors[service_type](self.name, *args, **kwargs) else: # noinspection PyCallingNonCallable service = constructors[service_type](self.name, fqdn, *args, **kwargs) if service_type in self.services: self.services[service_type].append(service) else: self.services[service_type] = [service] return service def get_all_nodes(self): """Returns a list of Node Objects for all subnodes in Zone (Excluding the Zone itself.) """ api_args = {} uri = '/NodeList/{}/'.format(self._name) response = DynectSession.get_session().execute(uri, 'GET', api_args) nodes = [Node(self._name, fqdn) for fqdn in response['data'] if fqdn != self._name] return nodes def get_node(self, node=None): """Returns all DNS Records for that particular node :param node: The name of the Node you wish to access, or `None` to get the root :class:`Node` of this :class:`Zone` """ if node: fqdn = node + '.' + self.name + '.' else: fqdn = self.name + '.' return Node(self.name, fqdn) 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 :return: A :class:`List` of all the :class:`DNSRecord`'s under this :class:`Zone` """ self.records = {} uri = '/AllRecord/{}/'.format(self._name) 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 != []} records = {} for key, record_list in record_lists.items(): search = key.split('_')[0].upper() try: constructor = RECS[search] except KeyError: constructor = 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._name, fqdn, **record)) records[key] = list_records return 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', 'CERT', 'CNAME', 'DHCID', 'DNAME', 'DNSKEY', 'DS', 'KEY', 'KX', 'LOC', 'IPSECKEY', 'MX', 'NAPTR', 'PTR', 'PX', 'NSAP', 'RP', 'NS', 'SOA', 'SPF', 'SRV', and 'TXT'. :return: A :class:`List` of :class:`DNSRecord`'s """ names = {'A': 'ARecord', 'AAAA': 'AAAARecord', 'ALIAS': 'ALIASRecord', 'CDS': 'CDSRecord', 'CDNSKEY': 'CDNSKEYRecord', 'CERT': 'CERTRecord', 'CSYNC': 'CSYNCRecord', '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'} constructor = RECS[record_type] uri = '/{}/{}/{}/'.format(names[record_type], self._name, 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._name, fqdn, **record)) return records def get_any_records(self): """Retrieve a list of all :class:`DNSRecord`'s associated with this :class:`Zone` """ if self.fqdn is None: return api_args = {'detail': 'Y'} uri = '/ANYRecord/{}/{}/'.format(self._name, 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 != []} records = {} for key, record_list in record_lists.items(): search = key.split('_')[0].upper() try: constructor = RECS[search] except KeyError: constructor = 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._name, self.fqdn, **record)) records[key] = list_records return records def get_all_active_failovers(self): """Retrieve a list of all :class:`ActiveFailover` services associated with this :class:`Zone` :return: A :class:`List` of :class:`ActiveFailover` Services """ uri = '/Failover/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) afos = [] for failover in response['data']: del failover['zone'] del failover['fqdn'] afos.append(ActiveFailover(self._name, self._fqdn, api=False, **failover)) return afos def get_all_ddns(self): """Retrieve a list of all :class:`DDNS` services associated with this :class:`Zone` :return: A :class:`List` of :class:`DDNS` Services """ uri = '/DDNS/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) ddnses = [] for svc in response['data']: del svc['zone'] del svc['fqdn'] ddnses.append( DynamicDNS(self._name, self._fqdn, api=False, **svc)) return ddnses def get_all_httpredirect(self): """Retrieve a list of all :class:`HTTPRedirect` services associated with this :class:`Zone` :return: A :class:`List` of :class:`HTTPRedirect` Services """ uri = '/HTTPRedirect/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) httpredirs = [] for httpredir in response['data']: del httpredir['zone'] del httpredir['fqdn'] httpredirs.append( HTTPRedirect(self._name, self._fqdn, api=False, **httpredir)) return httpredirs def get_all_gslb(self): """Retrieve a list of all :class:`GSLB` services associated with this :class:`Zone` :return: A :class:`List` of :class:`GSLB` Services """ uri = '/GSLB/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) gslbs = [] for gslb_svc in response['data']: del gslb_svc['zone'] del gslb_svc['fqdn'] gslbs.append(GSLB(self._name, self._fqdn, api=False, **gslb_svc)) return gslbs def get_all_rdns(self): """Retrieve a list of all :class:`ReverseDNS` services associated with this :class:`Zone` :return: A :class:`List` of :class:`ReverseDNS` Services """ uri = '/IPTrack/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) rdnses = [] for rdns in response['data']: del rdns['zone'] del rdns['fqdn'] rdnses.append( ReverseDNS(self._name, self._fqdn, api=False, **rdns)) return rdnses def get_all_rttm(self): """Retrieve a list of all :class:`RTTM` services associated with this :class:`Zone` :return: A :class:`List` of :class:`RTTM` Services """ uri = '/RTTM/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) rttms = [] for rttm_svc in response['data']: del rttm_svc['zone'] del rttm_svc['fqdn'] rttms.append(RTTM(self._name, self._fqdn, api=False, **rttm_svc)) return rttms def get_qps(self, start_ts, end_ts=None, breakdown=None, hosts=None, rrecs=None): """Generates a report with information about Queries Per Second (QPS) for this zone :param start_ts: datetime.datetime instance identifying point in time for the QPS report :param end_ts: datetime.datetime instance indicating the end of the data range for the report. Defaults to datetime.datetime.now() :param breakdown: By default, most data is aggregated together. Valid values ('hosts', 'rrecs', 'zones'). :param hosts: List of hosts to include in the report. :param rrecs: List of record types to include in report. :return: A :class:`str` with CSV data """ end_ts = end_ts or datetime.now() api_args = {'start_ts': unix_date(start_ts), 'end_ts': unix_date(end_ts), 'zones': [self.name]} if breakdown is not None: api_args['breakdown'] = breakdown if hosts is not None: api_args['hosts'] = hosts if rrecs is not None: api_args['rrecs'] = rrecs response = DynectSession.get_session().execute('/QPSReport/', 'POST', api_args) return response['data'] def delete(self): """Delete this :class:`Zone` and perform nessecary cleanups""" api_args = {} DynectSession.get_session().execute(self.uri, 'DELETE', api_args) def __eq__(self, other): """Equivalence operations for easily pulling a :class:`Zone` out of a list of :class:`Zone` objects """ if isinstance(other, str): return other == self._name elif isinstance(other, Zone): return other.name == self._name return False def __ne__(self, other): """Non-Equivalence operator""" return not self.__eq__(other) def __str__(self): """str override""" return force_unicode('<Zone>: {}').format(self._name) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())
class GSLBRegionPoolEntry(object): """:class:`GSLBRegionPoolEntry`""" def __init__(self, zone, fqdn, region_code, address, *args, **kwargs): """Create a :class:`GSLBRegionPoolEntry` object :param zone: Zone monitored by this :class:`GSLBRegionPoolEntry` :param fqdn: The fqdn of the specific node which will be monitored by this :class:`GSLBRegionPoolEntry` :param region_code: ISO Region Code for this :class:`GSLBRegionPoolEntry` :param address: The IP address or FQDN of this Node IP :param label: Identifying descriptive information for this :class:`GSLBRegionPoolEntry` :param weight: A number in the range of 1-14 controlling the order in which this :class:`GSLBRegionPoolEntry` will be served :param serve_mode: Sets the behavior of this particular record. Must be one of 'always', 'obey', 'remove', 'no' """ super(GSLBRegionPoolEntry, self).__init__() self.valid_serve_modes = ('always', 'obey', 'remove', 'no') self.valid_weight = range(1, 15) self._zone = zone self._fqdn = fqdn self._region_code = region_code self._address = address self._label = self._weight = self._serve_mode = None self._task_id = None uri = '/GSLBRegionPoolEntry/{}/{}/{}/{}/' self.uri = uri.format(self._zone, self._fqdn, self._region_code, self._address) if len(args) == 0 and len(kwargs) == 0: self._get() else: self._build(kwargs) def _post(self, label=None, weight=None, serve_mode=None): """Create a new :class:`GSLBRegionPoolEntry` on the DynECT System""" self._label = label self._weight = weight self._serve_mode = serve_mode uri = '/GSLBRegionPoolEntry/{}/{}/{}/'.format(self._zone, self._fqdn, self._region_code) api_args = {'address': self._address} if label: api_args['label'] = self._label if weight: if weight not in self.valid_weight: raise DynectInvalidArgumentError('weight', weight, self.valid_weight) api_args['weight'] = self._weight if serve_mode: if serve_mode not in self.valid_serve_modes: raise DynectInvalidArgumentError('serve_mode', serve_mode, self.valid_serve_modes) api_args['serve_mode'] = self._serve_mode response = DynectSession.get_session().execute(uri, 'POST', api_args) self._build(response['data']) def _get(self): """Get an existing :class:`GSLBRegionPoolEntry` object from the DynECT System """ api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args): """Private update method""" response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): """Build the variables in this object by pulling out the data from data """ for key, val in data.items(): if key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) @property def task(self): """:class:`Task` for most recent system action on this :class:`ActiveFailover`.""" if self._task_id: self._task_id.refresh() return self._task_id def sync(self): """Sync this :class:`GSLBRegionPoolEntry` object with the DynECT System """ self._get() @property def zone(self): """Zone monitored by this :class:`GSLBRegionPoolEntry`""" return self._zone @zone.setter def zone(self, value): pass @property def fqdn(self): """The fqdn of the specific node which will be monitored by this :class:`GSLBRegionPoolEntry` """ return self._fqdn @fqdn.setter def fqdn(self, value): pass @property def region_code(self): """ISO Region Code for this :class:`GSLBRegionPoolEntry`""" return self._region_code @region_code.setter def region_code(self, value): pass @property def address(self): """The IP address or FQDN of this Node IP""" return self._address @address.setter def address(self, value): self._address = value api_args = {'new_address': self._address} self._update(api_args) @property def label(self): """Identifying descriptive information for this :class:`GSLBRegionPoolEntry` """ return self._label @label.setter def label(self, value): self._label = value api_args = {'label': self._label} self._update(api_args) @property def weight(self): """A number in the range of 1-14 controlling the order in which this :class:`GSLBRegionPoolEntry` will be served. """ return self._weight @weight.setter def weight(self, new_weight): if new_weight not in self.valid_weight: raise DynectInvalidArgumentError(new_weight, self.valid_weight) self._weight = new_weight api_args = {'weight': self._weight} self._update(api_args) @property def serve_mode(self): """Sets the behavior of this particular record. Must be one of 'always', 'obey', 'remove', or 'no' """ return self._serve_mode @serve_mode.setter def serve_mode(self, new_serve_mode): if new_serve_mode not in self.valid_serve_modes: raise DynectInvalidArgumentError('serve_mode', new_serve_mode, self.valid_serve_modes) self._serve_mode = new_serve_mode api_args = {'serve_mode': self._serve_mode} self._update(api_args) def to_json(self): """Convert this object into a json blob""" output = {'address': self._address} if self._label: output['label'] = self._label if self._weight: output['weight'] = self._weight if self._serve_mode: output['serve_mode'] = self._serve_mode return output def delete(self): """Delete this :class:`GSLBRegionPoolEntry` from the DynECT System""" api_args = {} DynectSession.get_session().execute(self.uri, 'DELETE', api_args) def __str__(self): """str override""" s = force_unicode('<GSLBRegionPoolEntry>: {}') return s.format(self._region_code) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())
class SecondaryZone(object): """A class representing DynECT Secondary zones""" def __init__(self, zone, *args, **kwargs): """Create a :class:`SecondaryZone` object :param zone: The name of this secondary zone :param masters: A list of IPv4 or IPv6 addresses of the master nameserver(s) for this zone. :param contact_nickname: Name of the :class:`Contact` that will receive notifications for this zone :param tsig_key_name: Name of the TSIG key that will be used to sign transfer requests to this zone's master """ super(SecondaryZone, self).__init__() self._zone = self._name = zone self.uri = '/Secondary/{}/'.format(self._zone) self._masters = self._contact_nickname = self._tsig_key_name = None self._task_id = None if 'api' in kwargs: del kwargs['api'] for key, val in kwargs.items(): setattr(self, '_' + key, val) elif len(args) == 0 and len(kwargs) == 0: self._get() else: self._post(*args, **kwargs) def _get(self): """Get a :class:`SecondaryZone` object from the DynECT System""" api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _post(self, masters, contact_nickname=None, tsig_key_name=None): """Create a new :class:`SecondaryZone` object on the DynECT System""" self._masters = masters self._contact_nickname = contact_nickname self._tsig_key_name = tsig_key_name api_args = {'masters': self._masters} if contact_nickname: api_args['contact_nickname'] = self._contact_nickname if tsig_key_name: api_args['tsig_key_name'] = self._tsig_key_name response = DynectSession.get_session().execute(self.uri, 'POST', api_args) self._build(response['data']) def _update(self, api_args, uri=None): """Update this :class:`ActiveFailover`, via the API, with the args in api_args """ if not uri: uri = self.uri response = DynectSession.get_session().execute(uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): """Build the variables in this object by pulling out the data from data """ for key, val in data.items(): if key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) @property def task(self): """:class:`Task` for most recent system action on this :class:`SecondaryZone`. """ if self._task_id: self._task_id.refresh() return self._task_id @property def zone(self): """The name of this :class:`SecondaryZone`""" return self._zone @zone.setter def zone(self, value): pass @property def masters(self): """A list of IPv4 or IPv6 addresses of the master nameserver(s) for this zone. """ self._get() return self._masters @masters.setter def masters(self, value): self._masters = value api_args = {'masters': self._masters} self._update(api_args) @property def contact_nickname(self): """Name of the :class:`Contact` that will receive notifications for this zone """ self._get() return self._contact_nickname @contact_nickname.setter def contact_nickname(self, value): self._contact_nickname = value api_args = {'contact_nickname': self._contact_nickname} self._update(api_args) @property def tsig_key_name(self): """Name of the TSIG key that will be used to sign transfer requests to this zone's master """ self._get() return self._tsig_key_name @tsig_key_name.setter def tsig_key_name(self, value): self._tsig_key_name = value api_args = {'tsig_key_name': self._tsig_key_name} self._update(api_args) def activate(self): """Activates this secondary zone""" api_args = {'activate': True} self._update(api_args) def deactivate(self): """Deactivates this secondary zone""" api_args = {'deactivate': True} self._update(api_args) def retransfer(self): """Retransfers this secondary zone from its original provider into Dyn's Managed DNS """ api_args = {'retransfer': True} self._update(api_args) def delete(self): """Delete this :class:`SecondaryZone`""" api_args = {} uri = '/Zone/{}/'.format(self._zone) DynectSession.get_session().execute(uri, 'DELETE', api_args) @property def active(self): """Reports the status of :class:`SecondaryZone` Y, L or N""" self._get() return self._active @property def serial(self): """Reports the serial of :class:`SecondaryZone`""" api_args = {} uri = '/Zone/{}/'.format(self._zone) self._update(api_args, uri) return self._serial def __str__(self): """str override""" return force_unicode('<SecondaryZone>: {}').format(self._zone) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())
class Zone(object): """A class representing a DynECT Zone""" def __init__(self, name, *args, **kwargs): """Create a :class:`Zone` object. Note: When creating a new :class:`Zone` if no contact is specified the path to a local zone file must be passed to the ``file_name`` param. :param name: the name of the zone to create :param contact: Administrative contact for this zone :param ttl: TTL (in seconds) for records in the zone :param serial_style: The style of the zone's serial. Valid values: increment, epoch, day, minute :param file_name: The path to a valid RFC1035, BIND, or tinydns style Master file. Note: this file must be under 1mb in size. :param master_ip: The IP of the master server from which to fetch zone data for Transferring this :class:`Zone`. Note: This argument is required for performing a valid ZoneTransfer operation. :param timeout: The time, in minutes, to wait for a zone xfer to complete """ super(Zone, self).__init__() self.valid_serials = ('increment', 'epoch', 'day', 'minute') self._name = name self._fqdn = self._name if self._fqdn and not self._fqdn.endswith('.'): self._fqdn += '.' self._contact = self._ttl = self._serial_style = self._serial = None self._zone = self._status = None self.records = {} self._task_id = None self.services = {} self.uri = '/Zone/{}/'.format(self._name) if 'api' in kwargs: del kwargs['api'] for key, val in kwargs.items(): setattr(self, '_' + key, val) self._name = self._zone self.uri = '/Zone/{}/'.format(self._name) elif len(args) == 0 and len(kwargs) == 0: self._get() else: self._post(*args, **kwargs) self._status = 'active' def _post(self, contact=None, ttl=60, serial_style='increment', file_name=None, master_ip=None, timeout=None): """Create a new :class:`Zone` object on the DynECT System""" if contact is None and file_name is None and master_ip is None: raise DynectInvalidArgumentError('contact', None) if file_name is not None: self._post_with_file(file_name) elif master_ip is not None: self._xfer(master_ip, timeout) else: self._contact = contact self._ttl = ttl if serial_style not in self.valid_serials: raise DynectInvalidArgumentError(serial_style, self.valid_serials) self._serial_style = serial_style api_args = { 'zone': self._name, 'rname': self._contact, 'ttl': self._ttl, 'serial_style': self._serial_style } response = DynectSession.get_session().execute( self.uri, 'POST', api_args) self._build(response['data']) def _post_with_file(self, file_name): """Create a :class:`Zone` from a RFC1035 style Master file. A ZoneFile for BIND or tinydns will also be accepted :param file_name: The path to a valid ZoneFile """ full_path = os.path.abspath(file_name) file_size = os.path.getsize(full_path) if file_size > 1048576: raise DynectInvalidArgumentError('Zone File Size', file_size, 'Under 1MB') else: uri = '/ZoneFile/{}/'.format(self.name) f = open(full_path, 'r') content = f.read() f.close() api_args = {'file': content} response = DynectSession.get_session().execute( uri, 'POST', api_args) self.__poll_for_get() self._build(response['data']) def _xfer(self, master_ip, timeout=None): """Create a :class:`Zone` by ZoneTransfer by providing an optional master_ip argument. """ uri = '/ZoneTransfer/{}/'.format(self.name) api_args = {'master_ip': master_ip} response = DynectSession.get_session().execute(uri, 'POST', api_args) self._build(response['data']) time_out = timeout or 10 count = 0 while count < time_out: response = DynectSession.get_session().execute(uri, 'GET', {}) if response['status'] == 'running' and response['message'] == '': sleep(60) count += 1 else: break self._get() def __poll_for_get(self, n_loops=10, xfer=False, xfer_master_ip=None): """For use ONLY by _post_with_file and _xfer. Will wait at MOST ``n_loops * 2`` seconds for a successfull GET API response. If no successfull get is recieved no error will be raised. """ count = 0 got = False while count < n_loops: try: self._get() got = True break except DynectGetError: sleep(2) count += 1 if not got and xfer: uri = '/ZoneTransfer/{}/'.format(self.name) api_args = {} if xfer_master_ip is not None: api_args['master_ip'] = xfer_master_ip response = DynectSession.get_session().execute( uri, 'GET', api_args) error_labels = ['running', 'waiting', 'failed', 'canceled'] ok_labels = ['ready', 'unpublished', 'ok'] if response['data']['status'] in error_labels: raise DynectCreateError(response['msgs']) elif response['data']['status'] in ok_labels: self._get() else: pass # Should never get here def _get(self): """Get an existing :class:`Zone` object from the DynECT System""" api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _build(self, data): """Build the variables in this object by pulling out the data from data """ for key, val in data.items(): if key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) def _update(self, api_args): """Update this :class:`ActiveFailover`, via the API, with the args in api_args """ response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) @property def __root_soa(self): """Return the SOA record associated with this Zone""" return self.get_all_records_by_type('SOA')[0] @property def name(self): """The name of this :class:`Zone`""" return self._name @name.setter def name(self, value): pass @property def fqdn(self): """The name of this :class:`Zone`""" return self._fqdn @fqdn.setter def fqdn(self, value): pass @property def contact(self): """The email address of the primary :class:`Contact` associated with this :class:`Zone` """ self._contact = self.__root_soa.rname return self._contact @contact.setter def contact(self, value): self.__root_soa.rname = value @property def ttl(self): """This :class:`Zone`'s default TTL""" self._ttl = self.__root_soa.ttl return self._ttl @ttl.setter def ttl(self, value): self.__root_soa.ttl = value @property def serial(self): """The current serial of this :class:`Zone`""" self._get() return self._serial @serial.setter def serial(self, value): pass @property def serial_style(self): """The current serial style of this :class:`Zone`""" self._get() return self._serial_style @serial_style.setter def serial_style(self, value): if value not in self.valid_serials: raise DynectInvalidArgumentError('serial_style', value, self.valid_serials) self.__root_soa.serial_style = value @property def status(self): """Convenience property for :class:`Zones`. If a :class:`Zones` is frozen the status will read as `'frozen'`, if the :class:`Zones` is not frozen the status will read as `'active'`. Because the API does not return information about whether or not a :class:`Zones` is frozen there will be a few cases where this status will be `None` in order to avoid guessing what the current status actually is. """ self._get() return self._status @status.setter def status(self, value): pass def freeze(self): """Causes the zone to become frozen. Freezing a zone prevents changes to the zone until it is thawed. """ api_args = {'freeze': True} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) if response['status'] == 'success': self._status = 'frozen' def thaw(self): """Causes the zone to become thawed. Thawing a frozen zone allows changes to again be made to the zone. """ api_args = {'thaw': True} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) if response['status'] == 'success': self._status = 'active' def publish(self, notes=None): """Causes all pending changes to become part of the zone. The serial number increments based on its serial style and the data is pushed out to the nameservers. """ api_args = {'publish': True} if notes: api_args['notes'] = notes self._update(api_args) @property def task(self): """:class:`Task` for most recent system action on this :class:`Zone`. """ if self._task_id: self._task_id.refresh() return self._task_id def get_notes(self, offset=None, limit=None): """Generates a report containing the Zone Notes for this :class:`Zone` :param offset: The starting point at which to retrieve the notes :param limit: The maximum number of notes to be retrieved :return: A :class:`list` of :class:`dict` containing :class:`Zone` Notes """ uri = '/ZoneNoteReport/' api_args = {'zone': self.name} if offset: api_args['offset'] = offset if limit: api_args['limit'] = limit response = DynectSession.get_session().execute(uri, 'POST', api_args) return response['data'] def add_record(self, name=None, record_type='A', *args, **kwargs): """Adds an a record with the provided name and data to this :class:`Zone` :param name: The name of the node where this record will be added :param record_type: The type of record you would like to add. Valid record_type arguments are: 'A', 'AAAA', '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 """ fqdn = name + '.' + self.name + '.' if name else self.name + '.' # noinspection PyCallingNonCallable rec = RECS[record_type](self.name, fqdn, *args, **kwargs) if record_type in self.records: self.records[record_type].append(rec) else: self.records[record_type] = [rec] return rec def add_service(self, name=None, service_type=None, *args, **kwargs): """Add the specified service type to this zone, or to a node under this zone :param name: The name of the :class:`Node` where this service will be attached to or `None` to attach it to the root :class:`Node` of this :class:`Zone` :param service_type: The type of the service you would like to create. Valid service_type arguments are: 'ActiveFailover', 'DDNS', 'DNSSEC', 'DSF', 'GSLB', 'RDNS', 'RTTM', 'HTTPRedirect' :param args: Non-keyword arguments to pass to the Record constructor :param kwargs: Keyword arguments to pass to the Record constructor """ constructors = { 'ActiveFailover': ActiveFailover, 'DDNS': DynamicDNS, 'DNSSEC': DNSSEC, 'DSF': TrafficDirector, 'GSLB': GSLB, 'RDNS': ReverseDNS, 'RTTM': RTTM, 'HTTPRedirect': HTTPRedirect } fqdn = self.name + '.' if name: fqdn = name + '.' + fqdn if service_type == 'DNSSEC': # noinspection PyCallingNonCallable service = constructors[service_type](self.name, *args, **kwargs) else: # noinspection PyCallingNonCallable service = constructors[service_type](self.name, fqdn, *args, **kwargs) if service_type in self.services: self.services[service_type].append(service) else: self.services[service_type] = [service] return service def get_all_nodes(self): """Returns a list of Node Objects for all subnodes in Zone (Excluding the Zone itself.) """ api_args = {} uri = '/NodeList/{}/'.format(self._name) response = DynectSession.get_session().execute(uri, 'GET', api_args) nodes = [ Node(self._name, fqdn) for fqdn in response['data'] if fqdn != self._name ] return nodes def get_node(self, node=None): """Returns all DNS Records for that particular node :param node: The name of the Node you wish to access, or `None` to get the root :class:`Node` of this :class:`Zone` """ if node: fqdn = node + '.' + self.name + '.' else: fqdn = self.name + '.' return Node(self.name, fqdn) 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 :return: A :class:`List` of all the :class:`DNSRecord`'s under this :class:`Zone` """ self.records = {} uri = '/AllRecord/{}/'.format(self._name) 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 != [] } records = {} for key, record_list in record_lists.items(): search = key.split('_')[0].upper() try: constructor = RECS[search] except KeyError: constructor = 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._name, fqdn, **record)) records[key] = list_records return 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', 'CERT', 'CNAME', 'DHCID', 'DNAME', 'DNSKEY', 'DS', 'KEY', 'KX', 'LOC', 'IPSECKEY', 'MX', 'NAPTR', 'PTR', 'PX', 'NSAP', 'RP', 'NS', 'SOA', 'SPF', 'SRV', and 'TXT'. :return: A :class:`List` of :class:`DNSRecord`'s """ names = { 'A': 'ARecord', 'AAAA': 'AAAARecord', 'ALIAS': 'ALIASRecord', 'CDS': 'CDSRecord', 'CDNSKEY': 'CDNSKEYRecord', 'CERT': 'CERTRecord', 'CSYNC': 'CSYNCRecord', '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' } constructor = RECS[record_type] uri = '/{}/{}/{}/'.format(names[record_type], self._name, 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._name, fqdn, **record)) return records def get_any_records(self): """Retrieve a list of all :class:`DNSRecord`'s associated with this :class:`Zone` """ if self.fqdn is None: return api_args = {'detail': 'Y'} uri = '/ANYRecord/{}/{}/'.format(self._name, 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 != [] } records = {} for key, record_list in record_lists.items(): search = key.split('_')[0].upper() try: constructor = RECS[search] except KeyError: constructor = 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._name, self.fqdn, **record)) records[key] = list_records return records def get_all_active_failovers(self): """Retrieve a list of all :class:`ActiveFailover` services associated with this :class:`Zone` :return: A :class:`List` of :class:`ActiveFailover` Services """ uri = '/Failover/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) afos = [] for failover in response['data']: del failover['zone'] del failover['fqdn'] afos.append( ActiveFailover(self._name, self._fqdn, api=False, **failover)) return afos def get_all_ddns(self): """Retrieve a list of all :class:`DDNS` services associated with this :class:`Zone` :return: A :class:`List` of :class:`DDNS` Services """ uri = '/DDNS/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) ddnses = [] for svc in response['data']: del svc['zone'] del svc['fqdn'] ddnses.append(DynamicDNS(self._name, self._fqdn, api=False, **svc)) return ddnses def get_all_httpredirect(self): """Retrieve a list of all :class:`HTTPRedirect` services associated with this :class:`Zone` :return: A :class:`List` of :class:`HTTPRedirect` Services """ uri = '/HTTPRedirect/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) httpredirs = [] for httpredir in response['data']: del httpredir['zone'] del httpredir['fqdn'] httpredirs.append( HTTPRedirect(self._name, self._fqdn, api=False, **httpredir)) return httpredirs def get_all_gslb(self): """Retrieve a list of all :class:`GSLB` services associated with this :class:`Zone` :return: A :class:`List` of :class:`GSLB` Services """ uri = '/GSLB/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) gslbs = [] for gslb_svc in response['data']: del gslb_svc['zone'] del gslb_svc['fqdn'] gslbs.append(GSLB(self._name, self._fqdn, api=False, **gslb_svc)) return gslbs def get_all_rdns(self): """Retrieve a list of all :class:`ReverseDNS` services associated with this :class:`Zone` :return: A :class:`List` of :class:`ReverseDNS` Services """ uri = '/IPTrack/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) rdnses = [] for rdns in response['data']: del rdns['zone'] del rdns['fqdn'] rdnses.append(ReverseDNS(self._name, self._fqdn, api=False, **rdns)) return rdnses def get_all_rttm(self): """Retrieve a list of all :class:`RTTM` services associated with this :class:`Zone` :return: A :class:`List` of :class:`RTTM` Services """ uri = '/RTTM/{}/'.format(self._name) api_args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', api_args) rttms = [] for rttm_svc in response['data']: del rttm_svc['zone'] del rttm_svc['fqdn'] rttms.append(RTTM(self._name, self._fqdn, api=False, **rttm_svc)) return rttms def get_qps(self, start_ts, end_ts=None, breakdown=None, hosts=None, rrecs=None): """Generates a report with information about Queries Per Second (QPS) for this zone :param start_ts: datetime.datetime instance identifying point in time for the QPS report :param end_ts: datetime.datetime instance indicating the end of the data range for the report. Defaults to datetime.datetime.now() :param breakdown: By default, most data is aggregated together. Valid values ('hosts', 'rrecs', 'zones'). :param hosts: List of hosts to include in the report. :param rrecs: List of record types to include in report. :return: A :class:`str` with CSV data """ end_ts = end_ts or datetime.now() api_args = { 'start_ts': unix_date(start_ts), 'end_ts': unix_date(end_ts), 'zones': [self.name] } if breakdown is not None: api_args['breakdown'] = breakdown if hosts is not None: api_args['hosts'] = hosts if rrecs is not None: api_args['rrecs'] = rrecs response = DynectSession.get_session().execute('/QPSReport/', 'POST', api_args) return response['data'] def delete(self): """Delete this :class:`Zone` and perform nessecary cleanups""" api_args = {} DynectSession.get_session().execute(self.uri, 'DELETE', api_args) def __eq__(self, other): """Equivalence operations for easily pulling a :class:`Zone` out of a list of :class:`Zone` objects """ if isinstance(other, str): return other == self._name elif isinstance(other, Zone): return other.name == self._name return False def __ne__(self, other): """Non-Equivalence operator""" return not self.__eq__(other) def __str__(self): """str override""" return force_unicode('<Zone>: {}').format(self._name) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())
class RTTM(object): def __init__(self, zone, fqdn, *args, **kwargs): """Create a :class:`RTTM` object :param auto_recover: Indicates whether or not the service should automatically come out of failover when the IP addresses resume active status or if the service should remain in failover until manually reset. Must be one of 'Y' or 'N' :param ttl: Time To Live in seconds of records in the service. Must be less than 1/2 of the Health Probe's monitoring interval. Must be one of 30, 60, 150, 300, or 450. :param notify_events: A list of the events which trigger notifications. Must be one of 'ip', 'svc', or 'nosrv' :param syslog_server: The Hostname or IP address of a server to receive syslog notifications on monitoring events :param syslog_port: The port where the remote syslog server listens for notifications :param syslog_ident: The ident to use when sending syslog notifications :param syslog_facility: The syslog facility to use when sending syslog notifications. Must be one of kern, user, mail, daemon, auth, syslog, lpr, news, uucp, cron, authpriv, ftp, ntp, security, console, local0, local1, local2, local3, local4, local5, local6, or local7 :param syslog_delivery: The syslog delivery action type. 'all' will deliver notifications no matter what the endpoint state. 'change' (default) will deliver only on change in the detected endpoint state :param region: A list of :class:`RTTMRegion`'s :param monitor: The :class:`Monitor` for this service :param performance_monitor: The performance monitor for the service :param contact_nickname: Name of contact to receive notifications :param syslog_probe_fmt: see below for format: :param syslog_status_fmt: see below for format: :param syslog_rttm_fmt: see below for format: Use the following format for syslog_xxxx_fmt paramaters. %hos hostname %tim current timestamp or monitored interval %reg region code %sta status %ser record serial %rda rdata %sit monitoring site %rti response time %msg message from monitoring %adr address of monitored node %med median value %rts response times (RTTM) :param recovery_delay: number of up status polling intervals to consider service up """ super(RTTM, self).__init__() self.valid_ttls = (30, 60, 150, 300, 450) self.valid_notify_events = ('ip', 'svc', 'nosrv') self.valid_syslog_facilities = ('kern', 'user', 'mail', 'daemon', 'auth', 'syslog', 'lpr', 'news', 'uucp', 'cron', 'authpriv', 'ftp', 'ntp', 'security', 'console', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7') self._zone = zone self._fqdn = fqdn self.uri = '/RTTM/{}/{}/'.format(self._zone, self._fqdn) self._auto_recover = self._ttl = self._notify_events = None self._syslog_server = self._syslog_port = self._syslog_ident = None self._syslog_facility = self._monitor = None self._performance_monitor = self._contact_nickname = None self._active = self._syslog_delivery = self._syslog_probe_fmt = None self._syslog_status_fmt = self._syslog_rttm_fmt = None self._recovery_delay = None self._region = APIList(DynectSession.get_session, 'region') self._task_id = None if 'api' in kwargs: del kwargs['api'] self._build(kwargs) elif len(args) == 0 and len(kwargs) == 0: self._get() else: self._post(*args, **kwargs) self._region.uri = self.uri def _post(self, contact_nickname, performance_monitor, region, ttl=None, auto_recover=None, notify_events=None, syslog_server=None, syslog_port=514, syslog_ident='dynect', syslog_facility='daemon', syslog_delivery='change', syslog_probe_fmt=None, syslog_status_fmt=None, syslog_rttm_fmt=None, recovery_delay=None, monitor=None): """Create a new RTTM Service on the DynECT System""" self._auto_recover = auto_recover self._ttl = ttl self._notify_events = notify_events self._syslog_server = syslog_server self._syslog_port = syslog_port self._syslog_ident = syslog_ident self._syslog_facility = syslog_facility self._syslog_delivery = syslog_delivery self._region += region self._monitor = monitor self._performance_monitor = performance_monitor self._contact_nickname = contact_nickname self._syslog_probe_fmt = syslog_probe_fmt self._syslog_status_fmt = syslog_status_fmt self._syslog_rttm_fmt = syslog_rttm_fmt self._recovery_delay = recovery_delay api_args = {} if auto_recover: if auto_recover not in ('Y', 'N'): raise DynectInvalidArgumentError('auto_recover', auto_recover, ('Y', 'N')) api_args['auto_recover'] = self._auto_recover if ttl: if ttl not in self.valid_ttls: raise DynectInvalidArgumentError('ttl', ttl, self.valid_ttls) api_args['ttl'] = self._ttl if notify_events: for event in notify_events: if event not in self.valid_notify_events: raise DynectInvalidArgumentError('notify_events', event, self.valid_notify_events) api_args['notify_events'] = self._notify_events if syslog_server: api_args['syslog_server'] = self._syslog_server if syslog_port: api_args['syslog_port'] = self._syslog_port if syslog_ident: api_args['syslog_ident'] = self._syslog_ident if syslog_facility: if syslog_facility not in self.valid_syslog_facilities: raise DynectInvalidArgumentError('syslog_facility', syslog_facility, self.valid_syslog_facilities) api_args['syslog_facility'] = self._syslog_facility if syslog_delivery: api_args['syslog_delivery'] = self._syslog_delivery if syslog_probe_fmt: api_args['syslog_probe_fmt'] = self._syslog_probe_fmt if syslog_status_fmt: api_args['syslog_status_fmt'] = self._syslog_status_fmt if syslog_rttm_fmt: api_args['syslog_rttm_fmt'] = self._syslog_rttm_fmt if recovery_delay: api_args['recovery_delay'] = self._recovery_delay if region: api_args['region'] = [reg._json for reg in self._region] if monitor: api_args['monitor'] = self._monitor.to_json() if performance_monitor: mon_args = self._performance_monitor.to_json() api_args['performance_monitor'] = mon_args if contact_nickname: api_args['contact_nickname'] = self._contact_nickname # API expects a CSV string, not a list if isinstance(self.notify_events, list): api_args['notify_events'] = ','.join(self.notify_events) response = DynectSession.get_session().execute(self.uri, 'POST', api_args) self._build(response['data']) def _get(self): """Build an object around an existing DynECT RTTM Service""" api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args): """Perform a PUT api call using this objects data""" response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): """Build the neccesary substructures under this :class:`RTTM`""" for key, val in data.items(): if key == 'region': self._region = APIList(DynectSession.get_session, 'region') for region in val: code = region.pop('region_code', None) pool = region.pop('pool', None) status = region.pop('status', None) r = RTTMRegion(self._zone, self._fqdn, code, pool, **region) r._status = status self._region.append(r) elif key == 'monitor': if self._monitor is not None: self._monitor.zone = self._zone self._monitor.fqdn = self._fqdn else: proto = val.pop('protocol', None) inter = val.pop('interval', None) self._monitor = Monitor(proto, inter, **val) elif key == 'performance_monitor': if self._performance_monitor is not None: self._performance_monitor.zone = self._zone self._performance_monitor.fqdn = self._fqdn else: proto = val.pop('protocol', None) inter = val.pop('interval', None) self._performance_monitor = PerformanceMonitor(proto, inter, **val) elif key == 'notify_events': self._notify_events = [item.strip() for item in val.split(',')] elif key == 'active': self._active = Active(val) elif key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) self._region.uri = self.uri @property def task(self): """:class:`Task` for most recent system action on this :class:`ActiveFailover`.""" if self._task_id: self._task_id.refresh() return self._task_id def get_rrset_report(self, ts): """Generates a report of regional response sets for this RTTM service at a given point in time :param ts: UNIX timestamp identifying point in time for the log report :return: dictionary containing rrset report data """ api_args = {'zone': self._zone, 'fqdn': self._fqdn, 'ts': ts} response = DynectSession.get_session().execute('/RTTMRRSetReport/', 'POST', api_args) return response['data'] def get_log_report(self, start_ts, end_ts=None): """Generates a report with information about changes to an existing RTTM service :param start_ts: datetime.datetime instance identifying point in time for the start of the log report :param end_ts: datetime.datetime instance identifying point in time for the end of the log report. Defaults to datetime.datetime.now() :return: dictionary containing log report data """ end_ts = end_ts or datetime.now() api_args = {'zone': self._zone, 'fqdn': self._fqdn, 'start_ts': unix_date(start_ts), 'end_ts': unix_date(end_ts)} response = DynectSession.get_session().execute('/RTTMLogReport/', 'POST', api_args) return response['data'] def activate(self): """Activate this RTTM Service""" api_args = {'activate': True} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def deactivate(self): """Deactivate this RTTM Service""" api_args = {'deactivate': True} response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def recover(self, recoverip=None, address=None): """Recovers the RTTM service or a specific node IP within the service """ api_args = {'recover': True} if recoverip: api_args['recoverip'] = recoverip api_args['address'] = address response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) @property def active(self): """Returns whether or not this :class:`RTTM` Service is currently active. When setting directly, rather than using activate/deactivate valid arguments are 'Y' or True to activate, or 'N' or False to deactivate. Note: If your service is already active and you try to activate it, nothing will happen. And vice versa for deactivation. :returns: An :class:`Active` object representing the current state of this :class:`ReverseDNS` Service """ return self._active @active.setter def active(self, value): deactivate = ('N', False) activate = ('Y', True) if value in deactivate and self.active: self.deactivate() elif value in activate and not self.active: self.activate() @property def auto_recover(self): """Indicates whether or not the service should automatically come out of failover when the IP addresses resume active status or if the service should remain in failover until manually reset. Must be one of 'Y' or 'N' """ return self._auto_recover @auto_recover.setter def auto_recover(self, value): if value not in ('Y', 'N'): raise DynectInvalidArgumentError('auto_recover', value, ('Y', 'N')) api_args = {'auto_recover': value} self._update(api_args) @property def ttl(self): """Time To Live in seconds of records in the service. Must be less than 1/2 of the Health Probe's monitoring interval. Must be one of 30, 60, 150, 300, or 450. """ return self._ttl @ttl.setter def ttl(self, value): if value not in self.valid_ttls: raise DynectInvalidArgumentError('ttl', value, self.valid_ttls) api_args = {'ttl': value} self._update(api_args) @property def notify_events(self): """A list of events which trigger notifications. Valid values are: 'ip', 'svc', and 'nosrv' """ return self._notify_events @notify_events.setter def notify_events(self, value): for val in value: if val not in self.valid_notify_events: raise DynectInvalidArgumentError('notify_events', val, self.valid_notify_events) value = ','.join(value) api_args = {'notify_events': value} self._update(api_args) @property def status(self): """Status""" self._get() return self._status @property def syslog_server(self): """The Hostname or IP address of a server to receive syslog notifications on monitoring events """ self._get() return self._syslog_server @syslog_server.setter def syslog_server(self, value): api_args = {'syslog_server': value} self._update(api_args) @property def syslog_port(self): """The port where the remote syslog server listens for notifications""" self._get() return self._syslog_port @syslog_port.setter def syslog_port(self, value): api_args = {'syslog_port': value} self._update(api_args) @property def syslog_ident(self): """The ident to use when sending syslog notifications""" self._get() return self._syslog_ident @syslog_ident.setter def syslog_ident(self, value): api_args = {'syslog_ident': value} self._update(api_args) @property def syslog_facility(self): """The syslog facility to use when sending syslog notifications. Must be one of kern, user, mail, daemon, auth, syslog, lpr, news, uucp, cron, authpriv, ftp, ntp, security, console, local0, local1, local2, local3, local4, local5, local6, or local7 """ self._get() return self._syslog_facility @syslog_facility.setter def syslog_facility(self, value): if value not in self.valid_syslog_facilities: raise DynectInvalidArgumentError('syslog_facility', value, self.valid_syslog_facilities) api_args = {'syslog_facility': value} self._update(api_args) @property def syslog_delivery(self): self._get() return self._syslog_delivery @syslog_delivery.setter def syslog_delivery(self, value): api_args = {'syslog_delivery': value} self._update(api_args) @property def syslog_probe_format(self): self._get() return self._syslog_probe_fmt @syslog_probe_format.setter def syslog_probe_format(self, value): api_args = {'syslog_probe_fmt': value} self._update(api_args) @property def syslog_status_format(self): self._get() return self._syslog_status_fmt @syslog_status_format.setter def syslog_status_format(self, value): api_args = {'syslog_status_fmt': value} self._update(api_args) @property def syslog_rttm_format(self): self._get() return self._syslog_rttm_fmt @syslog_rttm_format.setter def syslog_rttm_format(self, value): api_args = {'syslog_rttm_fmt': value} self._update(api_args) @property def recovery_delay(self): self._get() return self._recovery_delay @recovery_delay.setter def recovery_delay(self, value): api_args = {'recovery_delay': value} self._update(api_args) @property def region(self): """A list of :class:`RTTMRegion`'s""" return self._region @region.setter def region(self, value): if isinstance(value, list) and not isinstance(value, APIList): self._region = APIList(DynectSession.get_session, 'region', None, value) elif isinstance(value, APIList): self._region = value self._region.uri = self.uri @property def monitor(self): """The :class:`Monitor` for this service""" return self._monitor @monitor.setter def monitor(self, value): if isinstance(value, Monitor): self._monitor = value api_args = {'monitor': self._monitor.to_json()} self._update(api_args) @property def performance_monitor(self): """The Performance :class:`Monitor` for this service""" return self._performance_monitor @performance_monitor.setter def performance_monitor(self, value): if isinstance(value, Monitor): self._performance_monitor = value api_args = {'performance_monitor': self._performance_monitor.to_json()} self._update(api_args) @property def contact_nickname(self): """The name of contact to receive notifications from this service""" return self._contact_nickname @contact_nickname.setter def contact_nickname(self, value): api_args = {'contact_nickname': value} self._update(api_args) def delete(self): """Delete this RTTM Service""" api_args = {} DynectSession.get_session().execute(self.uri, 'DELETE', api_args) def __str__(self): """str override""" return force_unicode('<RTTM>: {}').format(self._fqdn) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())
class RTTMRegion(object): """docstring for RTTMRegion""" def __init__(self, zone, fqdn, region_code, *args, **kwargs): """Create a :class:`RTTMRegion` object :param region_code: Name of the region :param pool: (*arg) The IP Pool list for this region :param autopopulate: If set to Y, this region will automatically be filled in from the global pool, and any other options passed in for this region will be ignored :param ep: Eligibility Pool - How many records will make it into the eligibility pool. The addresses that get chosen will be those that respond the fastest :param apmc: The minimum amount of IPs that must be in the up state, otherwise the region will be in failover :param epmc: The minimum amount of IPs that must be populated in the EP, otherwise the region will be in failover :param serve_count: How many records will be returned in each DNS response :param failover_mode: How the region should failover. Must be one of 'ip', 'cname', 'region', or 'global' :param failover_data: Dependent upon failover_mode. Must be one of ip', 'cname', 'region', or 'global' """ super(RTTMRegion, self).__init__() self.valid_region_codes = ('US West', 'US Central', 'US East', 'Asia', 'EU West', 'EU Central', 'EU East', 'global') self.valid_modes = ('ip', 'cname', 'region', 'global') self._task_id = None self._zone = zone self._fqdn = fqdn self._autopopulate = self._ep = self._apmc = None self._epmc = self._serve_count = self._failover_mode = None self._failover_data = None if len(args) != 0: pool = args[0] else: pool = None if region_code not in self.valid_region_codes: raise DynectInvalidArgumentError('region_code', region_code, self.valid_region_codes) self._region_code = region_code self._pool = [] self.uri = '/RTTMRegion/{}/{}/{}/'.format(self._zone, self._fqdn, self._region_code) # Backwards Compatability, since we changed the structure of this # Class: if kwargs.get('pool'): warnings.warn("passing pool as keyword argument is\ depreciated! please pass in as 4th argument", DeprecationWarning) pool = kwargs.pop('pool') if not pool and len(kwargs) == 0: self._get() if len(kwargs) > 0: self._build(kwargs) if pool: for poole in pool: if isinstance(poole, dict): rpe = RegionPoolEntry(zone=self._zone, fqdn=self._fqdn, region_code=self._region_code, **poole) self._pool.append(rpe) else: if not poole.zone: poole.zone = self._zone if not poole.fqdn: poole.fqdn = self._fqdn if not poole.region_code: poole.region_code = self._region_code self._pool.append(poole) self._status = None def _post(self): """Create a new :class:`RTTMRegion` on the DynECT System""" uri = '/RTTMRegion/{}/{}/'.format(self._zone, self._fqdn) api_args = {'region_code': self._region_code, 'pool': [poole.to_json() for poole in self._pool]} if self._autopopulate: if self._autopopulate not in ('Y', 'N'): raise DynectInvalidArgumentError('autopopulate', self._autopopulate, ('Y', 'N')) api_args['autopopulate'] = self._autopopulate if self._ep: api_args['ep'] = self._ep if self._apmc: api_args['apmc'] = self._apmc if self._epmc: api_args['epmc'] = self._epmc if self._serve_count: api_args['serve_count'] = self._serve_count if self._failover_mode: if self._failover_mode not in self.valid_modes: raise DynectInvalidArgumentError('failover_mode', self._failover_mode, self.valid_modes) api_args['failover_mode'] = self._failover_mode if self._failover_data: if self._failover_data not in self.valid_modes: raise DynectInvalidArgumentError('failover_data', self._failover_data, self.valid_modes) api_args['failover_data'] = self._failover_data response = DynectSession.get_session().execute(uri, 'POST', api_args) self._build(response['data']) def _get(self): """Get an existing :class:`RTTMRegion` object from the DynECT System""" api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _update(self, api_args): """Private Update method to cut back on redundant code""" response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) def _build(self, data): for key, val in data.items(): if key == 'pool': pass elif key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) @property def task(self): """:class:`Task` for most recent system action on this :class:`ActiveFailover`.""" if self._task_id: self._task_id.refresh() return self._task_id @property def autopopulate(self): """If set to Y, this region will automatically be filled in from the global pool, and any other options passed in for this region will be ignored. Must be either 'Y' or 'N'. """ return self._autopopulate @autopopulate.setter def autopopulate(self, value): self._autopopulate = value api_args = {'autopopulate': self._autopopulate} self._update(api_args) @property def ep(self): """Eligibility Pool - How many records will make it into the eligibility pool. The addresses that get chosen will be those that respond the fastest """ return self._ep @ep.setter def ep(self, value): self._ep = value api_args = {'ep': self._ep} self._update(api_args) @property def apmc(self): """The minimum amount of IPs that must be in the up state, otherwise the region will be in failover. """ return self._apmc @apmc.setter def apmc(self, value): self._apmc = value api_args = {'apmc': self._apmc} self._update(api_args) @property def epmc(self): """The minimum amount of IPs that must be populated in the EP, otherwise the region will be in failover """ return self._epmc @epmc.setter def epmc(self, value): self._epmc = value api_args = {'epmc': self._epmc} self._update(api_args) @property def serve_count(self): """How many records will be returned in each DNS response""" return self._serve_count @serve_count.setter def serve_count(self, value): self._serve_count = value api_args = {'serve_count': self._serve_count} self._update(api_args) @property def failover_mode(self): """How the region should failover. Must be one of 'ip', 'cname', 'region', or 'global' """ return self._failover_mode @failover_mode.setter def failover_mode(self, value): self._failover_mode = value api_args = {'failover_mode': self._failover_mode} self._update(api_args) @property def failover_data(self): """Dependent upon failover_mode. Must be one of ip', 'cname', 'region', or 'global' """ return self._failover_data @failover_data.setter def failover_data(self, value): if value not in self.valid_modes: raise DynectInvalidArgumentError('failover_data', value, self.valid_modes) api_args = {'failover_data': value} self._update(api_args) @property def pool(self): """The IP Pool list for this :class:`RTTMRegion`""" return self._pool @pool.setter def pool(self, value): self._pool = value api_args = {'pool': self._pool} self._update(api_args) @property def status(self): """The current state of the region.""" self._get() return self._status @status.setter def status(self, value): pass @property def _json(self): """Unpack this object and return it as a JSON blob""" json_blob = {'region_code': self._region_code, 'pool': [entry.to_json() for entry in self._pool]} if self._autopopulate: json_blob['autopopulate'] = self._autopopulate if self._ep: json_blob['ep'] = self._ep if self._apmc: json_blob['apmc'] = self._apmc if self._epmc: json_blob['epmc'] = self._epmc if self._serve_count: json_blob['serve_count'] = self._serve_count if self._failover_mode: json_blob['failover_mode'] = self._failover_mode if self._failover_data: json_blob['failover_data'] = self._failover_data return json_blob def delete(self): """Delete an existing :class:`RTTMRegion` object from the DynECT System """ DynectSession.get_session().execute(self.uri, 'DELETE') def __str__(self): """str override""" return force_unicode('<RTTMRegion>: {}').format(self._region_code) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())
class RegionPoolEntry(object): """Creates a new RTTM service region pool entry in the zone/node indicated """ def __init__(self, address, label, weight, serve_mode, **kwargs): """Create a :class:`RegionPoolEntry` object :param address: The IPv4 address or FQDN of this Node IP :param label: A descriptive string identifying this IP :param weight: A number from 1-15 describing how often this record should be served. The higher the number, the more often the address is served :param serve_mode: Sets the behavior of this particular record. Must be one of 'always', 'obey', 'remove', or 'no' """ super(RegionPoolEntry, self).__init__() self.valid_modes = ('always', 'obey', 'remove', 'no') self._address = address self._label = label self._task_id = None self._zone = kwargs.get('zone') self._fqdn = kwargs.get('fqdn') self._region_code = kwargs.get('region_code') if weight not in range(1, 16): raise DynectInvalidArgumentError('weight', weight, '1-15') self._weight = weight if serve_mode not in self.valid_modes: raise DynectInvalidArgumentError('serve_mode', serve_mode, self.valid_modes) self._serve_mode = serve_mode self._log = [] self._build(kwargs) def _update(self, args): """Private method for processing various updates""" uri = '/RTTMRegionPoolEntry/{}/{}/{}/{}/'.format(self._zone, self._fqdn, self._region_code, self._address) response = DynectSession.get_session().execute(uri, 'PUT', args) self._build(response['data']) def _get(self): uri = '/RTTMRegionPoolEntry/{}/{}/{}/{}/'.format(self._zone, self._fqdn, self._region_code, self._address) args = {'detail': 'Y'} response = DynectSession.get_session().execute(uri, 'GET', args) self._build(response['data']) def _build(self, data): """Build the variables in this object by pulling out the data from data """ for key, val in data.items(): if key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) @property def task(self): """:class:`Task` for most recent system action on this :class:`RegionPoolEntry`.""" if self._task_id: self._task_id.refresh() return self._task_id @property def logs(self): self._get() return self._log @logs.setter def logs(self, value): pass @property def address(self): """The IPv4 address or FQDN of this Node IP""" return self._address @address.setter def address(self, new_address): api_args = {'new_address': new_address} self._update(api_args) @property def zone(self): """Zone for this :class:`RegionPoolEntry`, this is stored locally for REST command completion""" return self._zone @zone.setter def zone(self, zone): self._zone = zone @property def fqdn(self): """FQDN for this :class:`RegionPoolEntry`, this is stored locally for REST command completion""" return self._fqdn @fqdn.setter def fqdn(self, fqdn): self._fqdn = fqdn @property def region_code(self): """region_code for this :class:`RegionPoolEntry`, this is stored locally for REST command completion""" return self._region_code @region_code.setter def region_code(self, region_code): self._region_code = region_code @property def label(self): """A descriptive string identifying this IP""" return self._label @label.setter def label(self, value): self._label = value api_args = {'label': self._label} self._update(api_args) @property def weight(self): """A number from 1-15 describing how often this record should be served. The higher the number, the more often the address is served """ return self._weight @weight.setter def weight(self, new_weight): if new_weight < 1 or new_weight > 15: raise DynectInvalidArgumentError('weight', new_weight, '1-15') self._weight = new_weight api_args = {'weight': self._weight} self._update(api_args) @property def serve_mode(self): """Sets the behavior of this particular record""" return self._serve_mode @serve_mode.setter def serve_mode(self, serve_mode): if serve_mode not in self.valid_modes: raise DynectInvalidArgumentError('serve_mode', serve_mode, self.valid_modes) self._serve_mode = serve_mode api_args = {'serve_mode': self._serve_mode} self._update(api_args) def to_json(self): """Return a JSON representation of this RegionPoolEntry""" json_blob = {'address': self._address, 'label': self._label, 'weight': self._weight, 'serve_mode': self._serve_mode} return json_blob def delete(self): """Delete this :class:`RegionPoolEntry`""" uri = '/RTTMRegionPoolEntry/{}/{}/{}/{}/'.format(self._zone, self._fqdn, self._region_code, self._address) DynectSession.get_session().execute(uri, 'DELETE', {}) def __str__(self): """str override""" return force_unicode('<RTTMRegionPoolEntry>: {}').format(self._address) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())
class ActiveFailover(object): """With Active Failover, we monitor your Primary IP. If a failover event is detected, our system auto switches (hot swaps) to your dedicated back-up IP """ def __init__(self, zone, fqdn, *args, **kwargs): """Create a new :class:`ActiveFailover` object :param zone: The zone to attach this :class:`ActiveFailover` service to :param fqdn: The FQDN where this :class:`ActiveFailover` service will be attached :param address: IPv4 Address or FQDN being monitored by this :class:`ActiveFailover` service :param failover_mode: Indicates the target failover resource type. :param failover_data: The IPv4 Address or CNAME data for the failover target :param auto_recover: Indicates whether this service should restore its original state when the source IPs resume online status :param notify_events: A comma separated list of what events trigger notifications :param syslog_server: The Hostname or IP address of a server to receive syslog notifications on monitoring events :param syslog_port: The port where the remote syslog server listens :param syslog_ident: The ident to use when sending syslog notifications :param syslog_facility: The syslog facility to use when sending syslog notifications :param syslog_delivery: The syslog delivery action type. 'all' will deliver notifications no matter what the endpoint state. 'change' (default) will deliver only on change in the detected endpoint state :param monitor: The :class:`HealthMonitor` for this :class:`ActiveFailover` service :param contact_nickname: Name of contact to receive notifications from this :class:`ActiveFailover` service :param ttl: Time To Live in seconds of records in the service. Must be less than 1/2 of the Health Probe's monitoring interval :param syslog_probe_fmt: see below for format: :param syslog_status_fmt: see below for format: Use the following format for syslog_xxxx_fmt paramaters. %hos hostname %tim current timestamp or monitored interval %reg region code %sta status %ser record serial %rda rdata %sit monitoring site %rti response time %msg message from monitoring %adr address of monitored node %med median value %rts response times (RTTM) :param recovery_delay: number of up status polling intervals to consider service up """ super(ActiveFailover, self).__init__() self.valid_notify_events = ('ip', 'svc', 'nosrv') self._zone = zone self._fqdn = fqdn self._address = self._failover_mode = self._failover_data = None self._monitor = self._active = None self._contact_nickname = self._auto_recover = None self._notify_events = self._syslog_server = self._syslog_port = None self._syslog_ident = self._syslog_probe_fmt = None self._syslog_status_fmt = self._syslog_facility = self._ttl = None self._syslog_delivery = self._recovery_delay = None self.uri = '/Failover/{}/{}/'.format(self._zone, self._fqdn) self.api_args = {} if 'api' in kwargs: del kwargs['api'] self._build(kwargs) elif len(args) == 0 and len(kwargs) == 0: self._get() else: self._post(*args, **kwargs) def _get(self): """Build an object around an existing DynECT Active Failover Service""" api_args = {} response = DynectSession.get_session().execute(self.uri, 'GET', api_args) self._build(response['data']) def _post(self, address, failover_mode, failover_data, monitor, contact_nickname, auto_recover=None, notify_events=None, syslog_server=None, syslog_port=None, syslog_ident=None, syslog_facility=None, ttl=None, syslog_probe_fmt=None, syslog_status_fmt=None, syslog_delivery=None, recovery_delay=None): """Create a new Active Failover Service on the DynECT System""" self._address = address self._failover_mode = failover_mode self._failover_data = failover_data self._monitor = monitor self._monitor.zone = self._zone self._monitor.fqdn = self._fqdn self._contact_nickname = contact_nickname self._auto_recover = auto_recover self._notify_events = notify_events self._syslog_server = syslog_server self._syslog_port = syslog_port self._syslog_ident = syslog_ident self._syslog_facility = syslog_facility self._syslog_delivery = syslog_delivery self._syslog_probe_fmt = syslog_probe_fmt self._syslog_status_fmt = syslog_status_fmt self._recovery_delay = recovery_delay self._ttl = ttl self.api_args = {'address': self._address, 'failover_mode': self._failover_mode, 'failover_data': self._failover_data, 'monitor': self.monitor.to_json(), 'contact_nickname': self._contact_nickname} if syslog_probe_fmt: self.api_args['syslog_probe_fmt'] = self._syslog_probe_fmt if syslog_status_fmt: self.api_args['syslog_status_fmt'] = self._syslog_status_fmt if recovery_delay: self.api_args['recovery_delay'] = self._recovery_delay if syslog_facility: self.api_args['syslog_facility'] = self._syslog_facility if syslog_delivery: self.api_args['syslog_delivery'] = self._syslog_delivery if syslog_ident: self.api_args['syslog_ident'] = self._syslog_ident if notify_events: self.api_args['notify_events'] = self._notify_events if syslog_server: self.api_args['syslog_server'] = self._syslog_server if syslog_port: self.api_args['syslog_port'] = self._syslog_port response = DynectSession.get_session().execute(self.uri, 'POST', self.api_args) self._build(response['data']) def _build(self, data): """Build this object from the data returned in an API response""" self._task_id = None for key, val in data.items(): if key == 'monitor': self._monitor = HealthMonitor(**val) elif key == 'active': self._active = Active(val) elif key == "task_id" and not val: self._task_id = None elif key == "task_id": self._task_id = Task(val) else: setattr(self, '_' + key, val) def _update(self, api_args): """Update this :class:`ActiveFailover`, via the API, with the args in api_args """ response = DynectSession.get_session().execute(self.uri, 'PUT', api_args) self._build(response['data']) @property def task(self): """:class:`Task` for most recent system action on this :class:`ActiveFailover`.""" if self._task_id: self._task_id.refresh() return self._task_id @property def zone(self): """The zone to attach this :class:`ActiveFailover` service to""" return self._zone @zone.setter def zone(self, value): pass @property def fqdn(self): """The FQDN where this :class:`ActiveFailover` service will be attached """ return self._fqdn @fqdn.setter def fqdn(self, value): pass @property def active(self): """Return whether or not this :class:`ActiveFailover` service is active. When setting directly, rather than using activate/deactivate valid arguments are 'Y' or True to activate, or 'N' or False to deactivate. Note: If your service is already active and you try to activate it, nothing will happen. And vice versa for deactivation. :returns: An :class:`Active` object representing the current state of this :class:`ActiveFailover` Service """ self._get() return self._active @active.setter def active(self, value): deactivate = ('N', False) activate = ('Y', True) if value in deactivate and self.active: self.deactivate() elif value in activate and not self.active: self.activate() @property def address(self): """IPv4 Address or FQDN being monitored by this :class:`ActiveFailover` service """ return self._address @address.setter def address(self, value): self._address = value api_args = {'address': self._address, 'failover_mode': self._failover_mode, 'failover_data': self._failover_data, 'monitor': self.monitor.to_json(), 'contact_nickname': self._contact_nickname} self._update(api_args) @property def failover_mode(self): """Indicates the target failover resource type.""" return self._failover_mode @failover_mode.setter def failover_mode(self, value): self._failover_mode = value self.api_args['failover_mode'] = self._failover_mode self._update(self.api_args) @property def failover_data(self): """The IPv4 Address or CNAME data for the failover target""" return self._failover_data @failover_data.setter def failover_data(self, value): self._failover_data = value self.api_args['failover_data'] = self._failover_data self._update(self.api_args) @property def monitor(self): """The :class:`HealthMonitor` for this :class:`ActiveFailover` service """ return self._monitor @monitor.setter def monitor(self, value): self._monitor = value self.api_args['monitor'] = self._monitor.to_json() self._update(self.api_args) @property def contact_nickname(self): """Name of contact to receive notifications from this :class:`ActiveFailover` service """ return self._contact_nickname @contact_nickname.setter def contact_nickname(self, value): self._contact_nickname = value self.api_args['contact_nickname'] = self._contact_nickname self._update(self.api_args) @property def auto_recover(self): """Indicates whether this service should restore its original state when the source IPs resume online status """ return self._auto_recover @auto_recover.setter def auto_recover(self, value): self._auto_recover = value api_args = self.api_args api_args['auto_recover'] = self._auto_recover self._update(api_args) @property def notify_events(self): """A comma separated list of what events trigger notifications""" return self._notify_events @notify_events.setter def notify_events(self, value): for val in value: if val not in self.valid_notify_events: raise DynectInvalidArgumentError('notify_events', val, self.valid_notify_events) value = ','.join(value) api_args = self.api_args api_args['notify_events'] = value self._update(api_args) def recover(self): """Recover this :class:`ActiveFailover` service""" api_args = {'recover': 'Y'} self._update(api_args) @property def syslog_server(self): """The Hostname or IP address of a server to receive syslog notifications on monitoring events """ self._get() return self._syslog_server @syslog_server.setter def syslog_server(self, value): self._syslog_server = value api_args = self.api_args api_args['syslog_server'] = self._syslog_server self._update(api_args) @property def syslog_port(self): """The port where the remote syslog server listens""" self._get() return self._syslog_port @syslog_port.setter def syslog_port(self, value): self._syslog_port = value api_args = self.api_args api_args['syslog_port'] = self._syslog_port self._update(api_args) @property def syslog_ident(self): """The ident to use when sending syslog notifications""" self._get() return self._syslog_ident @syslog_ident.setter def syslog_ident(self, value): self._syslog_ident = value api_args = self.api_args api_args['syslog_ident'] = self._syslog_ident self._update(api_args) @property def syslog_facility(self): """The syslog facility to use when sending syslog notifications""" self._get() return self._syslog_facility @syslog_facility.setter def syslog_facility(self, value): self._syslog_facility = value api_args = self.api_args api_args['syslog_facility'] = self._syslog_facility self._update(api_args) @property def syslog_delivery(self): self._get() return self._syslog_delivery @syslog_delivery.setter def syslog_delivery(self, value): api_args = {'syslog_delivery': value} self._update(api_args) @property def syslog_probe_format(self): self._get() return self._syslog_probe_fmt @syslog_probe_format.setter def syslog_probe_format(self, value): api_args = {'syslog_probe_fmt': value} self._update(api_args) @property def syslog_status_format(self): self._get() return self._syslog_status_fmt @syslog_status_format.setter def syslog_status_format(self, value): api_args = {'syslog_status_fmt': value} self._update(api_args) @property def recovery_delay(self): self._get() return self._recovery_delay @recovery_delay.setter def recovery_delay(self, value): api_args = {'recovery_delay': value} self._update(api_args) @property def ttl(self): """Time To Live in seconds of records in the service. Must be less than 1/2 of the Health Probe's monitoring interval """ return self._ttl @ttl.setter def ttl(self, value): self._ttl = value api_args = self.api_args api_args['ttl'] = self._ttl self._update(api_args) def activate(self): """Activate this :class:`ActiveFailover` service""" api_args = {'activate': True} self._update(api_args) def deactivate(self): """Deactivate this :class:`ActiveFailover` service""" api_args = {'deactivate': True} self._update(api_args) def delete(self): """Delete this :class:`ActiveFailover` service from the Dynect System """ api_args = {} DynectSession.get_session().execute(self.uri, 'DELETE', api_args) def __str__(self): """str override""" return force_unicode('<ActiveFailover>: {}').format(self._fqdn) __repr__ = __unicode__ = __str__ def __bytes__(self): """bytes override""" return bytes(self.__str__())