コード例 #1
0
ファイル: tsigkey.py プロジェクト: bopopescu/OpenStack-Stein
class TsigKey(base.DictObjectMixin, base.PersistentObjectMixin,
              base.DesignateObject):
    def __init__(self, *args, **kwargs):
        super(TsigKey, self).__init__(*args, **kwargs)

    fields = {
        'name': fields.StringFields(nullable=False, maxLength=160),
        'algorithm': fields.EnumField(nullable=False,
            valid_values=[
                'hmac-md5',
                'hmac-sha1',
                'hmac-sha224',
                'hmac-sha256',
                'hmac-sha384',
                'hmac-sha512'
            ]
        ),
        'secret': fields.StringFields(maxLength=160),
        'scope': fields.EnumField(nullable=False,
            valid_values=['POOL', 'ZONE']
        ),
        'resource_id': fields.UUIDFields(nullable=False)
    }

    STRING_KEYS = [
        'id', 'name', 'algorithm', 'scope', 'resource_id'
    ]
コード例 #2
0
class FloatingIP(base.DictObjectMixin, base.PersistentObjectMixin,
                 base.DesignateObject):
    fields = {
        "address":
        fields.IPV4AddressField(nullable=True),
        "description":
        fields.StringFields(nullable=True, maxLength=160),
        "ptrdname":
        fields.DomainField(nullable=True),
        "ttl":
        fields.IntegerFields(nullable=True, minimum=1, maximum=2147483647),
        "region":
        fields.StringFields(nullable=True),
        "action":
        fields.EnumField(['CREATE', 'DELETE', 'UPDATE', 'NONE'],
                         nullable=True),
        "status":
        fields.EnumField(['ACTIVE', 'PENDING', 'ERROR'], nullable=True)
    }

    STRING_KEYS = ['key', 'address', 'ptrdname']

    @property
    def key(self):
        return '%s:%s' % (self.region, self.id)
コード例 #3
0
class ZoneTransferRequest(
        base.DictObjectMixin,
        base.PersistentObjectMixin,
        base.DesignateObject,
):
    fields = {
        'key':
        fields.StringFields(nullable=True, maxLength=160),
        'zone_id':
        fields.UUIDFields(nullable=True),
        'description':
        fields.StringFields(nullable=True, maxLength=160),
        'tenant_id':
        fields.StringFields(nullable=True),
        'target_tenant_id':
        fields.StringFields(nullable=True),
        'status':
        fields.EnumField(
            nullable=True,
            valid_values=["ACTIVE", "PENDING", "DELETED", "ERROR",
                          "COMPLETE"]),
        'zone_name':
        fields.StringFields(nullable=True, maxLength=255),
    }

    STRING_KEYS = ['id', 'zone_id', 'zone_name', 'target_tenant_id']
コード例 #4
0
class Blacklist(base.DictObjectMixin, base.PersistentObjectMixin,
                base.DesignateObject):
    fields = {
        'pattern': fields.StringFields(maxLength=255),
        'description': fields.StringFields(maxLength=160, nullable=True),
    }

    STRING_KEYS = ['id', 'pattern']
コード例 #5
0
class ZoneAttribute(base.DictObjectMixin, base.PersistentObjectMixin,
                    base.DesignateObject):
    fields = {
        'zone_id': fields.UUIDFields(nullable=True),
        'key': fields.StringFields(maxLength=50, nullable=False),
        'value': fields.StringFields(maxLength=50, nullable=False)
    }

    STRING_KEYS = ['id', 'key', 'value', 'zone_id']
コード例 #6
0
class PoolAttribute(base.DictObjectMixin, base.PersistentObjectMixin,
                    base.DesignateObject):
    fields = {
        'pool_id': fields.UUIDFields(nullable=True),
        'key': fields.StringFields(maxLength=50),
        'value': fields.StringFields(maxLength=50)
    }

    STRING_KEYS = ['id', 'key', 'value', 'pool_id']
コード例 #7
0
ファイル: record.py プロジェクト: bopopescu/OpenStack-Stein
class Record(base.DesignateObject, base.PersistentObjectMixin,
             base.DictObjectMixin):
    def __init__(self, *args, **kwargs):
        super(Record, self).__init__(*args, **kwargs)

    fields = {
        'shard':
        fields.IntegerFields(nullable=True, minimum=0, maximum=4095),
        'data':
        fields.AnyField(nullable=True),
        'zone_id':
        fields.UUIDFields(nullable=True),
        'managed':
        fields.BooleanField(nullable=True),
        'managed_resource_type':
        fields.StringFields(nullable=True, maxLength=160),
        'managed_resource_id':
        fields.UUIDFields(nullable=True),
        'managed_plugin_name':
        fields.StringFields(nullable=True, maxLength=160),
        'managed_plugin_type':
        fields.StringFields(nullable=True, maxLength=160),
        'hash':
        fields.StringFields(nullable=True, maxLength=32),
        'description':
        fields.StringFields(nullable=True, maxLength=160),
        'status':
        fields.EnumField(
            valid_values=['ACTIVE', 'PENDING', 'ERROR', 'DELETED'],
            nullable=True),
        'tenant_id':
        fields.StringFields(nullable=True),
        'recordset_id':
        fields.UUIDFields(nullable=True),
        'managed_tenant_id':
        fields.StringFields(nullable=True),
        'managed_resource_region':
        fields.StringFields(nullable=True, maxLength=160),
        'managed_extra':
        fields.StringFields(nullable=True, maxLength=160),
        'action':
        fields.EnumField(valid_values=['CREATE', 'DELETE', 'UPDATE', 'NONE'],
                         nullable=True),
        'serial':
        fields.IntegerFields(nullable=True, minimum=1, maximum=4294967295),
    }

    @classmethod
    def get_recordset_schema_changes(cls):
        # This is to allow record types to override the validation on a
        # recordset
        return {}

    STRING_KEYS = ['id', 'recordset_id', 'data']

    def __str__(self):
        record = self.to_dict()
        record['data'] = record['data'][:35]
        return (self._make_obj_str(self.STRING_KEYS) % record)
コード例 #8
0
class SPF(Record):
    """
    SPF Resource Record Type
    Defined in: RFC4408
    """
    fields = {'txt_data': fields.StringFields()}

    def _to_string(self):
        return self.txt_data

    def _from_string(self, value):
        if (not value.startswith('"') and not value.endswith('"')):
            # value with spaces should be quoted as per RFC1035 5.1
            for element in value:
                if element.isspace():
                    err = ("Empty spaces are not allowed in SPF record, "
                           "unless wrapped in double quotes.")
                    raise InvalidObject(err)
        else:
            # quotes within value should be escaped with backslash
            strip_value = value.strip('"')
            for index, char in enumerate(strip_value):
                if char == '"':
                    if strip_value[index - 1] != "\\":
                        err = ("Quotation marks should be escaped with "
                               "backslash.")
                        raise InvalidObject(err)

        self.txt_data = value

    # The record type is defined in the RFC. This will be used when the record
    # is sent by mini-dns.
    RECORD_TYPE = 99
コード例 #9
0
class MX(Record):
    """
    MX Resource Record Type
    Defined in: RFC1035
    """
    fields = {
        'priority': fields.IntegerFields(minimum=0, maximum=65535),
        'exchange': fields.StringFields(maxLength=255),
    }

    def _to_string(self):
        return '%(priority)s %(exchange)s' % self

    def _from_string(self, value):
        priority, exchange = value.split(' ')

        if repr(int(priority)) != priority:
            raise ValueError('Value is not an integer')

        self.priority = int(priority)
        self.exchange = exchange

    # The record type is defined in the RFC. This will be used when the record
    # is sent by mini-dns.
    RECORD_TYPE = 15
コード例 #10
0
ファイル: rbac.py プロジェクト: phvalguima/designate
class RBACBaseObject(base.DictObjectMixin, base.PersistentObjectMixin,
                     base.DesignateObject):

    # not to confuse action field as all other Designate Objects
    # rbac_action is intended for allowed/denied actions
    # such as access_as_shared
    fields = {
        'id': fields.UUIDFields(nullable=False),
        'project_id': fields.UUIDFields(nullable=False),
        'object_id': fields.UUIDFields(nullable=True),
        'target_tenant': fields.UUIDFields(nullable=True),
        'rbac_action': fields.StringFields(maxLength=255),
    }

    fields_no_update = ['id', 'project_id', 'object_id']

    STRING_KEYS = [
        'id', 'project_id', 'object_id', 'target_tenant', 'rbac_action'
    ]

    @classmethod
    def get_projects(cls,
                     context,
                     object_id=None,
                     action=None,
                     target_tenant=None):
        ## TODO: update this method as it was done with:
        ## https://github.com/openstack/neutron/blob/master/neutron/objects/rbac.py#L44
        return None
コード例 #11
0
class ZoneImport(base.DictObjectMixin, base.PersistentObjectMixin,
                 base.DesignateObject):
    fields = {
        'status':
        fields.EnumField(
            nullable=True,
            valid_values=["ACTIVE", "PENDING", "DELETED", "ERROR",
                          "COMPLETE"]),
        'task_type':
        fields.EnumField(nullable=True, valid_values=["IMPORT"]),
        'tenant_id':
        fields.StringFields(nullable=True),
        'message':
        fields.StringFields(nullable=True, maxLength=160),
        'zone_id':
        fields.UUIDFields(nullable=True)
    }
コード例 #12
0
class PoolTargetOption(base.DictObjectMixin, base.PersistentObjectMixin,
                       base.DesignateObject):
    fields = {
        'pool_target_id': fields.UUIDFields(nullable=True),
        'key': fields.StringFields(maxLength=255),
        'value': fields.AnyField(),
    }

    STRING_KEYS = ['id', 'key', 'value', 'pool_target_id']
コード例 #13
0
ファイル: base.py プロジェクト: tucows/designate
class SoftDeleteObjectMixin(object):
    """
    Mixin class for Soft-Deleted objects.

    This adds the fields that we use in common for all soft-deleted objects.
    """
    fields = {
        'deleted': fields.StringFields(nullable=True),
        'deleted_at': fields.DateTimeField(nullable=True),
    }
コード例 #14
0
class ZoneTransferAccept(base.DictObjectMixin, base.PersistentObjectMixin,
                         base.DesignateObject):
    fields = {
        'zone_transfer_request_id':
        fields.UUIDFields(nullable=True),
        'tenant_id':
        fields.StringFields(nullable=True),
        'status':
        fields.EnumField(
            nullable=True,
            valid_values=["ACTIVE", "PENDING", "DELETED", "ERROR",
                          "COMPLETE"]),
        'key':
        fields.StringFields(maxLength=160),
        'zone_id':
        fields.UUIDFields(nullable=True),
    }

    STRING_KEYS = ['id', 'zone_id', 'tenant_id', 'zone_transfer_request_id']
コード例 #15
0
class Tld(base.DictObjectMixin, base.PersistentObjectMixin,
          base.DesignateObject):
    def __init__(self, *args, **kwargs):
        super(Tld, self).__init__(*args, **kwargs)

    fields = {
        'name': fields.TldField(maxLength=255),
        'description': fields.StringFields(nullable=True, maxLength=160)
    }

    STRING_KEYS = ['id', 'name']
コード例 #16
0
class ServiceStatus(base.DesignateObject, base.DictObjectMixin,
                    base.PersistentObjectMixin):

    def __init__(self, *args, **kwargs):
        super(ServiceStatus, self).__init__(*args, **kwargs)

    fields = {
        "service_name": fields.StringFields(),
        "hostname": fields.StringFields(nullable=True),
        "heartbeated_at": fields.DateTimeField(nullable=True),
        "status": fields.EnumField(nullable=True, valid_values=[
            "UP", "DOWN", "WARNING"
        ]),
        "stats": fields.BaseObjectField(nullable=True),
        "capabilities": fields.BaseObjectField(nullable=True),
    }

    STRING_KEYS = [
        'service_name', 'hostname', 'status'
    ]
コード例 #17
0
class PoolTarget(base.DictObjectMixin, base.PersistentObjectMixin,
                 base.DesignateObject):
    fields = {
        'pool_id': fields.UUIDFields(nullable=True),
        'type': fields.AnyField(nullable=True),
        'tsigkey_id': fields.UUIDFields(nullable=True),
        'description': fields.StringFields(maxLength=160, nullable=True),
        'masters': fields.ObjectFields('PoolTargetMasterList'),
        'options': fields.ObjectFields('PoolTargetOptionList'),
        'backend': fields.AnyField(nullable=True),
    }

    STRING_KEYS = ['id', 'type', 'pool_id']
コード例 #18
0
class SPF(Record):
    """
    SPF Resource Record Type
    Defined in: RFC4408
    """
    fields = {'txt_data': fields.StringFields()}

    def _to_string(self):
        return self.txt_data

    def _from_string(self, value):
        self.txt_data = value

    # The record type is defined in the RFC. This will be used when the record
    # is sent by mini-dns.
    RECORD_TYPE = 99
コード例 #19
0
class ZoneMaster(base.DesignateObject, base.DictObjectMixin,
                 base.PersistentObjectMixin, base.SoftDeleteObjectMixin):
    def __init__(self, *args, **kwargs):
        super(ZoneMaster, self).__init__(*args, **kwargs)

    fields = {
        'zone_id': fields.UUIDFields(nullable=True),
        'host': fields.StringFields(),
        'port': fields.IntegerFields(minimum=1, maximum=65535)
    }

    def to_data(self):
        return "{}:{}".format(self.host, self.port)

    @classmethod
    def from_data(cls, data):
        host, port = utils.split_host_port(data)
        dict_data = {"host": host, "port": port}
        return cls(**dict_data)
コード例 #20
0
ファイル: recordset.py プロジェクト: binaryflesh/designate
class RecordSet(base.DesignateObject, base.DictObjectMixin,
                base.PersistentObjectMixin):
    def __init__(self, *args, **kwargs):
        super(RecordSet, self).__init__(*args, **kwargs)

    @property
    def action(self):
        # Return action as UPDATE if present. CREATE and DELETE are returned
        # if they are the only ones.
        action = 'NONE'
        actions = {'CREATE': 0, 'DELETE': 0, 'UPDATE': 0, 'NONE': 0}
        for record in self.records:
            actions[record.action] += 1

        if actions['CREATE'] != 0 and actions['UPDATE'] == 0 and \
                        actions['DELETE'] == 0 and actions['NONE'] == 0:  # noqa
            action = 'CREATE'
        elif actions['DELETE'] != 0 and actions['UPDATE'] == 0 and \
                        actions['CREATE'] == 0 and actions['NONE'] == 0:  # noqa
            action = 'DELETE'
        elif actions['UPDATE'] != 0 or actions['CREATE'] != 0 or \
                        actions['DELETE'] != 0:  # noqa
            action = 'UPDATE'
        return action

    @property
    def managed(self):
        managed = False
        for record in self.records:
            if record.managed:
                return True
        return managed

    @property
    def status(self):
        # Return the worst status in order of ERROR, PENDING, ACTIVE, DELETED.
        status = None
        statuses = {
            'ERROR': 0,
            'PENDING': 1,
            'ACTIVE': 2,
            'DELETED': 3,
        }
        for record in self.records:
            if not status or statuses[record.status] < statuses[status]:
                status = record.status
        return status or 'ACTIVE'

    fields = {
        'shard': fields.IntegerFields(nullable=True, minimum=0, maximum=4095),
        'tenant_id': fields.StringFields(nullable=True, read_only=True),
        'zone_id': fields.UUIDFields(nullable=True, read_only=True),
        'zone_name': fields.DomainField(nullable=True, maxLength=255),
        'name': fields.HostField(maxLength=255, nullable=True),
        'type': fields.StringFields(nullable=True, read_only=True),
        'ttl': fields.IntegerFields(nullable=True,
                                    minimum=1,
                                    maximum=2147483647),
        'description': fields.StringFields(nullable=True, maxLength=160),
        'records': fields.PolymorphicObjectField('RecordList', nullable=True),
    }

    def _validate_fail(self, errors, msg):
        e = ValidationError()
        e.path = ['recordset', 'type']
        e.validator = 'value'
        e.validator_value = [self.type]
        e.message = msg
        # Add it to the list for later
        errors.append(e)
        raise exceptions.InvalidObject(
            "Provided object does not match "
            "schema",
            errors=errors,
            object=self)

    def validate(self):

        LOG.debug("Validating '%(name)s' object with values: %(values)r", {
            'name': self.obj_name(),
            'values': self.to_dict(),
        })
        LOG.debug(list(self.records))

        errors = ValidationErrorList()

        # Get the right classes (e.g. A for Recordsets with type: 'A')
        try:
            record_list_cls = self.obj_cls_from_name('%sList' % self.type)
            record_cls = self.obj_cls_from_name(self.type)
        except (KeyError, ovo_exc.UnsupportedObjectError) as e:
            err_msg = ("'%(type)s' is not a valid record type" % {
                'type': self.type
            })
            self._validate_fail(errors, err_msg)

        if self.type not in cfg.CONF.supported_record_type:
            err_msg = ("'%(type)s' is not a supported record type" % {
                'type': self.type
            })
            self._validate_fail(errors, err_msg)

        # Get any rules that the record type imposes on the record
        changes = record_cls.get_recordset_schema_changes()
        old_fields = {}
        if changes:
            LOG.debug("Record %s is overriding the RecordSet schema with: %s",
                      record_cls.obj_name(), changes)
            old_fields = deepcopy(self.FIELDS)
            self.FIELDS = utils.deep_dict_merge(self.FIELDS, changes)

        error_indexes = []
        # Copy these for safekeeping
        old_records = deepcopy(self.records)

        # Blank the records for this object with the right list type
        self.records = record_list_cls()

        i = 0

        for record in old_records:
            record_obj = record_cls()
            try:
                record_obj._from_string(record.data)
            # The _from_string() method will throw a ValueError if there is not
            # enough data blobs
            except ValueError as e:
                # Something broke in the _from_string() method
                # Fake a correct looking ValidationError() object
                e = ValidationError()
                e.path = ['records', i]
                e.validator = 'format'
                e.validator_value = [self.type]
                e.message = ("'%(data)s' is not a '%(type)s' Record" % {
                    'data': record.data,
                    'type': self.type
                })
                # Add it to the list for later
                errors.append(e)
                error_indexes.append(i)

            except TypeError as e:
                e = ValidationError()
                e.path = ['records', i]
                e.validator = 'format'
                e.validator_value = [self.type]
                e.message = ("'%(data)s' is not a '%(type)s' Record" % {
                    'data': record.data,
                    'type': self.type
                })
                # Add it to the list for later
                errors.append(e)
                error_indexes.append(i)

            except AttributeError as e:
                e = ValidationError()
                e.path = ['records', i]
                e.validator = 'format'
                e.validator_value = [self.type]
                e.message = ("'%(data)s' is not a '%(type)s' Record" % {
                    'data': record.data,
                    'type': self.type
                })
                # Add it to the list for later
                errors.append(e)
                error_indexes.append(i)

            except Exception as e:
                error_message = ('Provided object is not valid. Got a %s error'
                                 ' with message %s' %
                                 (type(e).__name__, six.text_type(e)))
                raise exceptions.InvalidObject(error_message)

            else:
                # Seems to have loaded right - add it to be validated by
                # JSONSchema
                self.records.append(record_obj)
            i += 1

        try:
            # Run the actual validate code
            super(RecordSet, self).validate()

        except exceptions.InvalidObject as e:
            raise e
        else:
            # If JSONSchema passes, but we found parsing errors,
            # raise an exception
            if len(errors) > 0:
                LOG.debug(
                    "Error Validating '%(name)s' object with values: "
                    "%(values)r", {
                        'name': self.obj_name(),
                        'values': self.to_dict(),
                    })
                raise exceptions.InvalidObject(
                    "Provided object does not match "
                    "schema",
                    errors=errors,
                    object=self)
        finally:
            if old_fields:
                self.FIELDS = old_fields

        # Send in the traditional Record objects to central / storage
        self.records = old_records

    STRING_KEYS = ['id', 'type', 'name', 'zone_id']
コード例 #21
0
class Zone(base.DesignateObject, base.DictObjectMixin,
           base.PersistentObjectMixin, base.SoftDeleteObjectMixin):
    def __init__(self, *args, **kwargs):
        super(Zone, self).__init__(*args, **kwargs)

    fields = {
        'shard': fields.IntegerFields(nullable=True, minimum=0, maximum=4095),
        'tenant_id': fields.StringFields(nullable=True, read_only=False),
        'name': fields.DomainField(maxLength=255),
        'email': fields.EmailField(maxLength=255, nullable=True),
        'ttl': fields.IntegerFields(nullable=True, minimum=1,
                                    maximum=2147483647),
        'refresh': fields.IntegerFields(nullable=True, minimum=0,
                                        maximum=2147483647, read_only=False),
        'retry': fields.IntegerFields(nullable=True, minimum=0,
                                      maximum=2147483647, read_only=False),
        'expire': fields.IntegerFields(nullable=True, minimum=0,
                                       maximum=2147483647, read_only=False),
        'minimum': fields.IntegerFields(nullable=True, minimum=0,
                                        maximum=2147483647, read_only=False),
        'parent_zone_id': fields.UUIDFields(nullable=True, read_only=False),
        'serial': fields.IntegerFields(nullable=True, minimum=0,
                                       maximum=4294967295, read_only=False),
        'description': fields.StringFields(nullable=True, maxLength=160),
        'status': fields.EnumField(nullable=True, read_only=False,
                                   valid_values=[
                                       'ACTIVE', 'PENDING', 'ERROR',
                                       'DELETED', 'SUCCESS', 'NO_ZONE']

                                   ),
        'action': fields.EnumField(nullable=True,
                                   valid_values=[
                                       'CREATE', 'DELETE', 'UPDATE', 'NONE']
                                   ),
        'pool_id': fields.UUIDFields(nullable=True, read_only=False),
        'recordsets': fields.ObjectField('RecordSetList', nullable=True),
        'attributes': fields.ObjectField('ZoneAttributeList', nullable=True),
        'masters': fields.ObjectField('ZoneMasterList', nullable=True),
        'type': fields.EnumField(nullable=True,
                                 valid_values=['SECONDARY', 'PRIMARY'],
                                 read_only=False
                                 ),
        'transferred_at': fields.DateTimeField(nullable=True, read_only=False),
        'delayed_notify': fields.BooleanField(nullable=True),
    }

    STRING_KEYS = [
        'id', 'type', 'name', 'pool_id', 'serial', 'action', 'status'
    ]

    def get_master_by_ip(self, host):
        """
        Utility to get the master by it's ip for this zone.
        """
        for srv in self.masters:
            srv_host, _ = utils.split_host_port(srv.to_data())
            if host == srv_host:
                return srv
        return False

    def _raise(self, errors):
        if len(errors) != 0:
            raise exceptions.InvalidObject(
                "Provided object does not match "
                "schema", errors=errors, object=self)

    def __hash__(self):
        return hash(self.id)

    def validate(self):
        errors = ValidationErrorList()

        if self.type == 'PRIMARY':
            if self.obj_attr_is_set('masters') and len(self.masters) != 0:
                e = ValidationError()
                e.path = ['type']
                e.validator = 'maxItems'
                e.validator_value = ['masters']
                e.message = "'masters' has more items than allowed"
                errors.append(e)
            if self.email is None:
                e = ValidationError()
                e.path = ['type']
                e.validator = 'required'
                e.validator_value = 'email'
                e.message = "'email' is a required property"
                errors.append(e)
            self._raise(errors)

        try:
            if self.type == 'SECONDARY':
                if self.masters is None or len(self.masters) == 0:
                    e = ValidationError()
                    e.path = ['type']
                    e.validator = 'required'
                    e.validator_value = ['masters']
                    e.message = "'masters' is a required property"
                    errors.append(e)

                for i in ['email', 'ttl']:
                    if i in self.obj_what_changed():
                        e = ValidationError()
                        e.path = ['type']
                        e.validator = 'not_allowed'
                        e.validator_value = i
                        e.message = "'%s' can't be specified when type is " \
                                    "SECONDARY" % i
                        errors.append(e)
                self._raise(errors)

            super(Zone, self).validate()
        except exceptions.RelationNotLoaded as ex:
            errors = ValidationErrorList()
            e = ValidationError()
            e.path = ['type']
            e.validator = 'required'
            e.validator_value = [ex.relation]
            e.message = "'%s' is a required property" % ex.relation
            errors.append(e)
            self._raise(errors)
コード例 #22
0
ファイル: pool.py プロジェクト: bopopescu/OpenStack-Stein
class Pool(base.DictObjectMixin, base.PersistentObjectMixin,
           base.DesignateObject):
    fields = {
        'name': fields.StringFields(maxLength=50),
        'description': fields.StringFields(nullable=True, maxLength=160),
        'tenant_id': fields.StringFields(maxLength=36, nullable=True),
        'provisioner': fields.StringFields(nullable=True, maxLength=160),
        'attributes': fields.ObjectFields('PoolAttributeList', nullable=True),
        'ns_records': fields.ObjectFields('PoolNsRecordList', nullable=True),
        'nameservers': fields.ObjectFields('PoolNameserverList',
                                           nullable=True),
        'targets': fields.ObjectFields('PoolTargetList', nullable=True),
        'also_notifies': fields.ObjectFields('PoolAlsoNotifyList',
                                             nullable=True),
    }

    @classmethod
    def from_config(cls, CONF, pool_id):
        pool_target_ids = CONF['pool:%s' % pool_id].targets
        pool_nameserver_ids = CONF['pool:%s' % pool_id].nameservers
        pool_also_notifies = CONF['pool:%s' % pool_id].also_notifies

        # Build Base Pool
        pool = {
            'id': pool_id,
            'description': 'Pool built from configuration on %s' % CONF.host,
            'targets': [],
            'nameservers': [],
            'also_notifies': [],
        }

        # Build Pool Also Notifies
        for pool_also_notify in pool_also_notifies:
            host, port = utils.split_host_port(pool_also_notify)
            pool['also_notifies'].append({
                'host': host,
                'port': port,
            })

        # Build Pool Targets
        for pool_target_id in pool_target_ids:
            pool_target_group = 'pool_target:%s' % pool_target_id

            pool_target = {
                'id': pool_target_id,
                'type': CONF[pool_target_group].type,
                'masters': [],
                'options': [],
            }

            # Build Pool Target Masters
            for pool_target_master in CONF[pool_target_group].masters:
                host, port = utils.split_host_port(pool_target_master)
                pool_target['masters'].append({
                    'host': host,
                    'port': port,
                })

            # Build Pool Target Options
            for k, v in CONF[pool_target_group].options.items():
                pool_target['options'].append({
                    'key': k,
                    'value': v,
                })

            pool['targets'].append(pool_target)

        # Build Pool Nameservers
        for pool_nameserver_id in pool_nameserver_ids:
            pool_nameserver_group = 'pool_nameserver:%s' % pool_nameserver_id

            pool_nameserver = {
                'id': pool_nameserver_id,
                'host': CONF[pool_nameserver_group].host,
                'port': CONF[pool_nameserver_group].port,
            }

            pool['nameservers'].append(pool_nameserver)

        return cls.from_dict(pool)

    STRING_KEYS = [
        'id', 'name'
    ]