diff --git a/designate/objects/adapters/yaml/base.py b/designate/objects/adapters/yaml/base.py index 4b65a7207..a659b80bc 100644 --- a/designate/objects/adapters/yaml/base.py +++ b/designate/objects/adapters/yaml/base.py @@ -51,16 +51,16 @@ class YAMLAdapter(base.DesignateAdapter): obj_key = key # Check if this item is a relation (another DesignateObject that # will need to be converted itself - if object.FIELDS.get(obj_key, {}).get('relation'): + if hasattr(object.FIELDS.get(obj_key, {}), 'objname'): # Get a adapter for the nested object # Get the class the object is and get its adapter, then set # the item in the dict to the output r_obj[key] = cls.get_object_adapter( cls.ADAPTER_FORMAT, - object.FIELDS[obj_key].get('relation_cls')).render( - cls.ADAPTER_FORMAT, obj, *args, **kwargs) - elif object.FIELDS.get( - obj_key, {}).get('schema', {}).get('type') == 'integer': + object.FIELDS[obj_key].objname).render( + cls.ADAPTER_FORMAT, obj, *args, **kwargs) + elif all(hasattr(object.FIELDS.get(obj_key, {}), attr) + for attr in ['min', 'max']): r_obj[key] = int(obj) elif obj is not None: # Just attach the damn item if there is no weird edge cases diff --git a/designate/objects/fields.py b/designate/objects/fields.py index 716e5edcb..77d221e62 100644 --- a/designate/objects/fields.py +++ b/designate/objects/fields.py @@ -288,3 +288,20 @@ class BaseObject(ovoo_fields.FieldType): class BaseObjectField(ovoo_fields.AutoTypedField): AUTO_TYPE = BaseObject() + + +class IPOrHost(IPV4AndV6AddressField): + def __init__(self, nullable=False, read_only=False, + default=ovoo_fields.UnspecifiedDefault): + super(IPOrHost, self).__init__(nullable=nullable, + default=default, read_only=read_only) + + def coerce(self, obj, attr, value): + try: + value = super(IPOrHost, self).coerce(obj, attr, value) + except ValueError: + if not re.match(StringFields.RE_ZONENAME, value): + raise ValueError("%s is not IP address or host name" % value) + # we use this field as a string, not need a netaddr.IPAdress + # as oslo.versionedobjects is using + return str(value) diff --git a/designate/objects/ovo_base.py b/designate/objects/ovo_base.py index 5cb265bd5..ec2e5273f 100644 --- a/designate/objects/ovo_base.py +++ b/designate/objects/ovo_base.py @@ -22,6 +22,8 @@ from oslo_versionedobjects.base import VersionedObjectDictCompat as DictObjectMi from designate.i18n import _ from designate.i18n import _LE from designate.objects import fields +from designate.objects.validation_error import ValidationError +from designate.objects.validation_error import ValidationErrorList from designate import exceptions LOG = logging.getLogger(__name__) @@ -209,20 +211,31 @@ class DesignateObject(base.VersionedObject): def validate(self): self.fields = self.FIELDS - try: - for name in self.fields: - field = self.fields[name] - if self.obj_attr_is_set(name): - value = getattr(self, name) # Check relation - field.coerce(self, name, value) # Check value - if isinstance(value, base.ObjectListBase): - for obj in value: - obj.validate() - elif not field.nullable: - # Check required is True ~ nullable is False - raise exceptions.InvalidObject - except Exception: - raise exceptions.InvalidObject + for name in self.fields: + field = self.fields[name] + if self.obj_attr_is_set(name): + value = getattr(self, name) # Check relation + if isinstance(value, ListObjectMixin): + for obj in value.objects: + obj.validate() + else: + try: + field.coerce(self, name, value) # Check value + except Exception as e: + raise exceptions.InvalidObject( + "{} is invalid".format(name)) + elif not field.nullable: + # Check required is True ~ nullable is False + errors = ValidationErrorList() + e = ValidationError() + e.path = ['records', 0] + e.validator = 'required' + e.validator_value = [name] + e.message = "'%s' is a required property" % name + errors.append(e) + raise exceptions.InvalidObject( + "Provided object does not match " + "schema", errors=errors, object=self) class ListObjectMixin(base.ObjectListBase): diff --git a/designate/objects/pool.py b/designate/objects/pool.py index 677976dc4..4dc12dc23 100644 --- a/designate/objects/pool.py +++ b/designate/objects/pool.py @@ -14,64 +14,25 @@ # License for the specific language governing permissions and limitations # under the License. from designate import utils -from designate.objects import base +from designate.objects import ovo_base as base +from designate.objects import fields +@base.DesignateRegistry.register class Pool(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): - FIELDS = { - 'name': { - 'schema': { - 'type': 'string', - 'description': 'Pool name', - 'maxLength': 50, - }, - 'immutable': True, - 'required': True - }, - 'description': { - 'schema': { - 'type': ['string', 'null'], - 'description': 'Description for the pool', - 'maxLength': 160 - } - }, - 'tenant_id': { - 'schema': { - 'type': ['string', 'null'], - 'description': 'Project identifier', - 'maxLength': 36, - }, - 'immutable': True - }, - 'provisioner': { - 'schema': { - 'type': ['string', 'null'], - 'description': 'Provisioner used for this pool', - 'maxLength': 160 - } - }, - 'attributes': { - 'relation': True, - 'relation_cls': 'PoolAttributeList', - }, - 'ns_records': { - 'relation': True, - 'relation_cls': 'PoolNsRecordList', - 'required': True - }, - 'nameservers': { - 'relation': True, - 'relation_cls': 'PoolNameserverList' - }, - 'targets': { - 'relation': True, - 'relation_cls': 'PoolTargetList' - }, - 'also_notifies': { - 'relation': True, - 'relation_cls': 'PoolAlsoNotifyList' - }, + 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 @@ -144,9 +105,14 @@ class Pool(base.DictObjectMixin, base.PersistentObjectMixin, ] +@base.DesignateRegistry.register class PoolList(base.ListObjectMixin, base.DesignateObject): LIST_ITEM_TYPE = Pool + fields = { + 'objects': fields.ListOfObjectsField('Pool'), + } + def __contains__(self, pool): for p in self.objects: if p.id == pool.id: diff --git a/designate/objects/pool_also_notify.py b/designate/objects/pool_also_notify.py index 60ab5d981..8e5c18025 100644 --- a/designate/objects/pool_also_notify.py +++ b/designate/objects/pool_also_notify.py @@ -12,34 +12,17 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from designate.objects import base +from designate.objects import ovo_base as base +from designate.objects import fields +@base.DesignateRegistry.register class PoolAlsoNotify(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): - FIELDS = { - 'pool_id': { - 'schema': { - 'type': 'string', - 'description': 'Pool identifier', - 'format': 'uuid', - }, - }, - 'host': { - 'schema': { - 'type': 'string', - 'format': 'ip-or-host', - 'required': True, - }, - }, - 'port': { - 'schema': { - 'type': 'integer', - 'minimum': 1, - 'maximum': 65535, - 'required': True, - }, - } + fields = { + 'pool_id': fields.UUIDFields(nullable=True), + 'host': fields.IPOrHost(), + 'port': fields.IntegerFields(minimum=1, maximum=65535), } STRING_KEYS = [ @@ -47,5 +30,9 @@ class PoolAlsoNotify(base.DictObjectMixin, base.PersistentObjectMixin, ] +@base.DesignateRegistry.register class PoolAlsoNotifyList(base.ListObjectMixin, base.DesignateObject): LIST_ITEM_TYPE = PoolAlsoNotify + fields = { + 'objects': fields.ListOfObjectsField('PoolAlsoNotify'), + } diff --git a/designate/objects/pool_attribute.py b/designate/objects/pool_attribute.py index 4bbcead66..9b2b70ad3 100644 --- a/designate/objects/pool_attribute.py +++ b/designate/objects/pool_attribute.py @@ -12,33 +12,18 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from designate.objects import base +from designate.objects import ovo_base as base +from designate.objects import fields +@base.DesignateRegistry.register class PoolAttribute(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): - FIELDS = { - 'pool_id': { - 'schema': { - 'type': 'string', - 'description': 'Pool identifier', - 'format': 'uuid', - }, - }, - 'key': { - 'schema': { - 'type': 'string', - 'maxLength': 50, - }, - 'required': True, - }, - 'value': { - 'schema': { - 'type': 'string', - 'maxLength': 50, - }, - 'required': True - } + fields = { + 'pool_id': fields.UUIDFields(nullable=True), + 'key': fields.StringFields(maxLength=50), + 'value': fields.StringFields(maxLength=50) + } STRING_KEYS = [ @@ -46,5 +31,9 @@ class PoolAttribute(base.DictObjectMixin, base.PersistentObjectMixin, ] +@base.DesignateRegistry.register class PoolAttributeList(base.AttributeListObjectMixin, base.DesignateObject): LIST_ITEM_TYPE = PoolAttribute + fields = { + 'objects': fields.ListOfObjectsField('PoolAttribute'), + } diff --git a/designate/objects/pool_manager_status.py b/designate/objects/pool_manager_status.py index 65af45a48..5373782b4 100644 --- a/designate/objects/pool_manager_status.py +++ b/designate/objects/pool_manager_status.py @@ -13,44 +13,21 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from designate.objects import base +from designate.objects import ovo_base as base +from designate.objects import fields +@base.DesignateRegistry.register class PoolManagerStatus(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): - FIELDS = { - 'nameserver_id': { - 'schema': { - 'type': 'string', - 'format': 'uuid', - }, - 'required': True - }, - 'zone_id': { - 'schema': { - 'type': 'string', - 'format': 'uuid', - }, - 'required': True}, - 'status': { - 'schema': { - 'type': ['string', 'null'], - 'enum': ['ACTIVE', 'PENDING', 'ERROR'], - }, - }, - 'serial_number': { - 'schema': { - 'type': 'integer', - 'minimum': 0, - 'maximum': 4294967295, - }, - }, - 'action': { - 'schema': { - 'type': 'string', - 'enum': ['CREATE', 'DELETE', 'UPDATE', 'NONE'], - }, - } + fields = { + 'nameserver_id': fields.UUIDFields(), + 'zone_id': fields.UUIDFields(), + 'status': fields.EnumField(['ACTIVE', 'PENDING', 'ERROR', + 'SUCCESS', 'COMPLETE'], nullable=True), + 'serial_number': fields.IntegerFields(minimum=0, maximum=4294967295), + 'action': fields.EnumField(['CREATE', 'DELETE', + 'UPDATE', 'NONE'], nullable=True), } STRING_KEYS = [ @@ -58,5 +35,9 @@ class PoolManagerStatus(base.DictObjectMixin, base.PersistentObjectMixin, ] +@base.DesignateRegistry.register class PoolManagerStatusList(base.ListObjectMixin, base.DesignateObject): LIST_ITEM_TYPE = PoolManagerStatus + fields = { + 'objects': fields.ListOfObjectsField('PoolManagerStatus'), + } diff --git a/designate/objects/pool_nameserver.py b/designate/objects/pool_nameserver.py index 5eaef2572..e11151efb 100644 --- a/designate/objects/pool_nameserver.py +++ b/designate/objects/pool_nameserver.py @@ -12,34 +12,17 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from designate.objects import base +from designate.objects import ovo_base as base +from designate.objects import fields +@base.DesignateRegistry.register class PoolNameserver(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): - FIELDS = { - 'pool_id': { - 'schema': { - 'type': 'string', - 'description': 'Pool identifier', - 'format': 'uuid', - }, - }, - 'host': { - 'schema': { - 'type': 'string', - 'format': 'ip-or-host', - 'required': True, - }, - }, - 'port': { - 'schema': { - 'type': 'integer', - 'minimum': 1, - 'maximum': 65535, - 'required': True, - }, - } + fields = { + 'pool_id': fields.UUIDFields(nullable=True), + 'host': fields.IPOrHost(), + 'port': fields.IntegerFields(minimum=1, maximum=65535), } STRING_KEYS = [ @@ -47,5 +30,9 @@ class PoolNameserver(base.DictObjectMixin, base.PersistentObjectMixin, ] +@base.DesignateRegistry.register class PoolNameserverList(base.ListObjectMixin, base.DesignateObject): LIST_ITEM_TYPE = PoolNameserver + fields = { + 'objects': fields.ListOfObjectsField('PoolNameserver'), + } diff --git a/designate/objects/pool_ns_record.py b/designate/objects/pool_ns_record.py index 26904a622..c6b1954b7 100644 --- a/designate/objects/pool_ns_record.py +++ b/designate/objects/pool_ns_record.py @@ -12,38 +12,18 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from designate.objects import base +from designate.objects import ovo_base as base +from designate.objects import fields +@base.DesignateRegistry.register class PoolNsRecord(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): - FIELDS = { - 'pool_id': { - 'schema': { - 'type': 'string', - 'description': 'Pool identifier', - 'format': 'uuid', - }, - }, - 'priority': { - 'schema': { - 'type': 'integer', - 'description': 'NS Record Priority Order', - 'minimum': 1, - 'maximum': 10000 - }, - 'required': True - }, - 'hostname': { - 'schema': { - 'type': 'string', - 'description': 'NS Record Hostname', - 'format': 'domainname', - 'maxLength': 255, - }, - 'immutable': True, - 'required': True - } + fields = { + 'pool_id': fields.UUIDFields(nullable=True), + 'priority': fields.IntegerFields(minimum=1, maximum=10000), + 'hostname': fields.DomainField(maxLength=255), + } STRING_KEYS = [ @@ -51,5 +31,9 @@ class PoolNsRecord(base.DictObjectMixin, base.PersistentObjectMixin, ] +@base.DesignateRegistry.register class PoolNsRecordList(base.ListObjectMixin, base.DesignateObject): LIST_ITEM_TYPE = PoolNsRecord + fields = { + 'objects': fields.ListOfObjectsField('PoolNsRecord'), + } diff --git a/designate/objects/pool_target.py b/designate/objects/pool_target.py index 828898c57..8d8b50290 100644 --- a/designate/objects/pool_target.py +++ b/designate/objects/pool_target.py @@ -12,43 +12,21 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from designate.objects import base +from designate.objects import ovo_base as base +from designate.objects import fields +@base.DesignateRegistry.register class PoolTarget(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): - FIELDS = { - 'pool_id': { - 'schema': { - 'type': 'string', - 'description': 'Pool identifier', - 'format': 'uuid', - }, - }, - 'type': {}, - 'tsigkey_id': { - 'schema': { - 'type': ['string', 'null'], - 'description': 'TSIG identifier', - 'format': 'uuid', - }, - }, - 'description': { - 'schema': { - 'type': ['string', 'null'], - 'description': 'Description for the pool', - 'maxLength': 160 - } - }, - 'masters': { - 'relation': True, - 'relation_cls': 'PoolTargetMasterList' - }, - 'options': { - 'relation': True, - 'relation_cls': 'PoolTargetOptionList' - }, - 'backend': {} + 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 = [ @@ -56,5 +34,9 @@ class PoolTarget(base.DictObjectMixin, base.PersistentObjectMixin, ] +@base.DesignateRegistry.register class PoolTargetList(base.ListObjectMixin, base.DesignateObject): LIST_ITEM_TYPE = PoolTarget + fields = { + 'objects': fields.ListOfObjectsField('PoolTarget'), + } diff --git a/designate/objects/pool_target_master.py b/designate/objects/pool_target_master.py index f26d30104..957aff747 100644 --- a/designate/objects/pool_target_master.py +++ b/designate/objects/pool_target_master.py @@ -12,34 +12,17 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from designate.objects import base +from designate.objects import ovo_base as base +from designate.objects import fields +@base.DesignateRegistry.register class PoolTargetMaster(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): - FIELDS = { - 'pool_target_id': { - 'schema': { - 'type': 'string', - 'description': 'Pool Target identifier', - 'format': 'uuid', - }, - }, - 'host': { - 'schema': { - 'type': 'string', - 'format': 'ip-or-host', - 'required': True, - }, - }, - 'port': { - 'schema': { - 'type': 'integer', - 'minimum': 1, - 'maximum': 65535, - 'required': True, - }, - } + fields = { + 'pool_target_id': fields.UUIDFields(nullable=True), + 'host': fields.IPOrHost(), + 'port': fields.IntegerFields(minimum=1, maximum=65535) } STRING_KEYS = [ @@ -47,5 +30,9 @@ class PoolTargetMaster(base.DictObjectMixin, base.PersistentObjectMixin, ] +@base.DesignateRegistry.register class PoolTargetMasterList(base.ListObjectMixin, base.DesignateObject): LIST_ITEM_TYPE = PoolTargetMaster + fields = { + 'objects': fields.ListOfObjectsField('PoolTargetMaster'), + } diff --git a/designate/objects/pool_target_option.py b/designate/objects/pool_target_option.py index 5d19bb09c..b349b7ea6 100644 --- a/designate/objects/pool_target_option.py +++ b/designate/objects/pool_target_option.py @@ -12,33 +12,17 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from designate.objects import base +from designate.objects import ovo_base as base +from designate.objects import fields +@base.DesignateRegistry.register class PoolTargetOption(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): - FIELDS = { - 'pool_target_id': { - 'schema': { - 'type': 'string', - 'description': 'Pool Target identifier', - 'format': 'uuid', - }, - }, - 'key': { - 'schema': { - 'type': 'string', - 'maxLength': 255, - }, - 'required': True, - }, - 'value': { - 'schema': { - 'type': 'string', - 'maxLength': 255, - }, - 'required': True - } + fields = { + 'pool_target_id': fields.UUIDFields(nullable=True), + 'key': fields.StringFields(maxLength=255), + 'value': fields.AnyField(), } STRING_KEYS = [ @@ -46,6 +30,10 @@ class PoolTargetOption(base.DictObjectMixin, base.PersistentObjectMixin, ] +@base.DesignateRegistry.register class PoolTargetOptionList(base.AttributeListObjectMixin, base.DesignateObject): LIST_ITEM_TYPE = PoolTargetOption + fields = { + 'objects': fields.ListOfObjectsField('PoolTargetOption'), + } diff --git a/designate/objects/zone_export.py b/designate/objects/zone_export.py index a2e5afcd8..9c1651bb0 100644 --- a/designate/objects/zone_export.py +++ b/designate/objects/zone_export.py @@ -21,11 +21,11 @@ from designate.objects import fields class ZoneExport(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): fields = { - 'status': fields.EnumField( + 'status': fields.EnumField(nullable=True, valid_values=["ACTIVE", "PENDING", "DELETED", "ERROR", "COMPLETE"] ), - 'task_type': fields.EnumField( + 'task_type': fields.EnumField(nullable=True, valid_values=["EXPORT"] ), 'tenant_id': fields.StringFields(nullable=True), diff --git a/designate/objects/zone_import.py b/designate/objects/zone_import.py index ed91f9466..7b7b1babf 100644 --- a/designate/objects/zone_import.py +++ b/designate/objects/zone_import.py @@ -21,11 +21,11 @@ from designate.objects import fields class ZoneImport(base.DictObjectMixin, base.PersistentObjectMixin, base.DesignateObject): fields = { - 'status': fields.EnumField( + 'status': fields.EnumField(nullable=True, valid_values=["ACTIVE", "PENDING", "DELETED", "ERROR", "COMPLETE"] ), - 'task_type': fields.EnumField( + 'task_type': fields.EnumField(nullable=True, valid_values=["IMPORT"] ), 'tenant_id': fields.StringFields(nullable=True), diff --git a/designate/tests/test_api/test_v2/test_zones.py b/designate/tests/test_api/test_v2/test_zones.py index f8731187f..79f235a1c 100644 --- a/designate/tests/test_api/test_v2/test_zones.py +++ b/designate/tests/test_api/test_v2/test_zones.py @@ -519,9 +519,14 @@ class ApiV2ZonesTest(ApiV2TestCase): def test_update_secondary(self): # Create a zone - fixture = self.get_zone_fixture('SECONDARY', 0) - - zone = objects.Zone(**fixture) + zone = objects.Zone( + name='example.com.', + type='SECONDARY', + masters=objects.ZoneMasterList.from_list([ + {'host': '1.0.0.0', 'port': 69}, + {'host': '2.0.0.0', 'port': 69} + ]) + ) zone.email = cfg.CONF['service:central'].managed_resource_email # Create a zone diff --git a/designate/tests/test_pool_manager/test_service.py b/designate/tests/test_pool_manager/test_service.py index 05379bf5e..4afbdcb95 100644 --- a/designate/tests/test_pool_manager/test_service.py +++ b/designate/tests/test_pool_manager/test_service.py @@ -350,7 +350,9 @@ class PoolManagerServiceNoopTest(PoolManagerTestCase): zone = self._build_zone('example.org.', 'UPDATE', 'PENDING') self.service._update_zone_on_target = Mock(return_value=True) self.service._update_zone_on_also_notify = Mock() - self.service.pool.also_notifies = ['bogus'] + self.service.pool.also_notifies = objects.PoolAlsoNotifyList( + objects=[objects.PoolAlsoNotify(host='1.0.0.0', port=1)] + ) self.service._exceed_or_meet_threshold = Mock(return_value=True) # cache.retrieve will throw exceptions.PoolManagerStatusNotFound diff --git a/designate/tests/unit/test_pool.py b/designate/tests/unit/test_pool.py index 03a13ad0a..c8e6ebe2a 100644 --- a/designate/tests/unit/test_pool.py +++ b/designate/tests/unit/test_pool.py @@ -50,11 +50,11 @@ mock_conf = RoObject(**{ also_notifies=['1.0.0.0:1', '2.0.0.0:2'] ), 'pool_nameserver:169ca3fc-5924-4a44-8c1f-7efbe52fbd59': RoObject( - host='pool_host_1', + host='pool_host_1.example.', port=123 ), 'pool_nameserver:269ca3fc-5924-4a44-8c1f-7efbe52fbd59': RoObject( - host='pool_host_2', + host='pool_host_2.example.', port=456 ), 'pool_target:1588652b-50e7-46b9-b688-a9bad40a873e': RoObject( @@ -91,10 +91,10 @@ class poolTest(oslotest.base.BaseTestCase): [('host', '2.0.0.0'), ('port', 2)]]), ('description', 'Pool built from configuration on foohost'), # noqa ('id', '769ca3fc-5924-4a44-8c1f-7efbe52fbd59'), - ('nameservers', [[('host', 'pool_host_1'), + ('nameservers', [[('host', 'pool_host_1.example.'), ('id', '169ca3fc-5924-4a44-8c1f-7efbe52fbd59'), # noqa ('port', 123)], - [('host', 'pool_host_2'), + [('host', 'pool_host_2.example.'), ('id', '269ca3fc-5924-4a44-8c1f-7efbe52fbd59'), # noqa ('port', 456)]]), ('targets', [[('id', '1588652b-50e7-46b9-b688-a9bad40a873e'), # noqa