diff --git a/doc/source/user/guides/dns.rst b/doc/source/user/guides/dns.rst new file mode 100644 index 000000000..f2ba4fbcf --- /dev/null +++ b/doc/source/user/guides/dns.rst @@ -0,0 +1,18 @@ +Using OpenStack DNS +=================== + +Before working with the DNS service, you'll need to create a connection +to your OpenStack cloud by following the :doc:`connect` user guide. This will +provide you with the ``conn`` variable used in the examples below. + +.. TODO(gtema): Implement this guide + +List Zones +---------- + +.. literalinclude:: ../examples/dns/list.py + :pyobject: list_zones + +Full example: `dns resource list`_ + +.. _dns resource list: http://git.openstack.org/cgit/openstack/openstacksdk/tree/examples/dns/list.py diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst index 0dcbb3a50..4fa8a02f6 100644 --- a/doc/source/user/index.rst +++ b/doc/source/user/index.rst @@ -39,6 +39,7 @@ approach, this is where you'll want to begin. Clustering Compute Database + DNS Identity Image Key Manager @@ -98,6 +99,7 @@ control which services can be used. Clustering Compute Database + DNS Identity v2 Identity v3 Image v1 @@ -130,6 +132,7 @@ The following services have exposed *Resource* classes. Clustering Compute Database + DNS Identity Image Key Management diff --git a/doc/source/user/proxies/dns.rst b/doc/source/user/proxies/dns.rst new file mode 100644 index 000000000..91a63de2b --- /dev/null +++ b/doc/source/user/proxies/dns.rst @@ -0,0 +1,81 @@ +DNS API +======= + +For details on how to use dns, see :doc:`/user/guides/dns` + +.. automodule:: openstack.dns.v2._proxy + +The DNS Class +------------- + +The dns high-level interface is available through the ``dns`` +member of a :class:`~openstack.connection.Connection` object. The +``dns`` member will only be added if the service is detected. + +DNS Zone Operations +^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.dns.v2._proxy.Proxy + + .. automethod:: openstack.dns.v2._proxy.Proxy.create_zone + .. automethod:: openstack.dns.v2._proxy.Proxy.delete_zone + .. automethod:: openstack.dns.v2._proxy.Proxy.get_zone + .. automethod:: openstack.dns.v2._proxy.Proxy.find_zone + .. automethod:: openstack.dns.v2._proxy.Proxy.zones + .. automethod:: openstack.dns.v2._proxy.Proxy.abandon_zone + .. automethod:: openstack.dns.v2._proxy.Proxy.xfr_zone + +Recordset Operations +^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.dns.v2._proxy.Proxy + + .. automethod:: openstack.dns.v2._proxy.Proxy.create_recordset + .. automethod:: openstack.dns.v2._proxy.Proxy.update_recordset + .. automethod:: openstack.dns.v2._proxy.Proxy.get_recordset + .. automethod:: openstack.dns.v2._proxy.Proxy.delete_recordset + .. automethod:: openstack.dns.v2._proxy.Proxy.recordsets + +Zone Import Operations +^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.dns.v2._proxy.Proxy + + .. automethod:: openstack.dns.v2._proxy.Proxy.zone_imports + .. automethod:: openstack.dns.v2._proxy.Proxy.create_zone_import + .. automethod:: openstack.dns.v2._proxy.Proxy.get_zone_import + .. automethod:: openstack.dns.v2._proxy.Proxy.delete_zone_import + +Zone Export Operations +^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.dns.v2._proxy.Proxy + + .. automethod:: openstack.dns.v2._proxy.Proxy.zone_exports + .. automethod:: openstack.dns.v2._proxy.Proxy.create_zone_export + .. automethod:: openstack.dns.v2._proxy.Proxy.get_zone_export + .. automethod:: openstack.dns.v2._proxy.Proxy.get_zone_export_text + .. automethod:: openstack.dns.v2._proxy.Proxy.delete_zone_export + +FloatingIP Operations +^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.dns.v2._proxy.Proxy + + .. automethod:: openstack.dns.v2._proxy.Proxy.floating_ips + .. automethod:: openstack.dns.v2._proxy.Proxy.get_floating_ip + .. automethod:: openstack.dns.v2._proxy.Proxy.update_floating_ip + +Zone Transfer Operations +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.dns.v2._proxy.Proxy + + .. automethod:: openstack.dns.v2._proxy.Proxy.zone_transfer_requests + .. automethod:: openstack.dns.v2._proxy.Proxy.get_zone_transfer_request + .. automethod:: openstack.dns.v2._proxy.Proxy.create_zone_transfer_request + .. automethod:: openstack.dns.v2._proxy.Proxy.update_zone_transfer_request + .. automethod:: openstack.dns.v2._proxy.Proxy.delete_zone_transfer_request + .. automethod:: openstack.dns.v2._proxy.Proxy.zone_transfer_accepts + .. automethod:: openstack.dns.v2._proxy.Proxy.get_zone_transfer_accept + .. automethod:: openstack.dns.v2._proxy.Proxy.create_zone_transfer_accept diff --git a/doc/source/user/resources/dns/index.rst b/doc/source/user/resources/dns/index.rst new file mode 100644 index 000000000..a8d0c9360 --- /dev/null +++ b/doc/source/user/resources/dns/index.rst @@ -0,0 +1,12 @@ +DNS Resources +============= + +.. toctree:: + :maxdepth: 1 + + v2/zone + v2/zone_transfer + v2/zone_export + v2/zone_import + v2/floating_ip + v2/recordset diff --git a/doc/source/user/resources/dns/v2/floating_ip.rst b/doc/source/user/resources/dns/v2/floating_ip.rst new file mode 100644 index 000000000..d616e71a9 --- /dev/null +++ b/doc/source/user/resources/dns/v2/floating_ip.rst @@ -0,0 +1,12 @@ +openstack.dns.v2.floating_ip +============================ + +.. automodule:: openstack.dns.v2.floating_ip + +The FloatingIP Class +-------------------- + +The ``DNS`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.dns.v2.floating_ip.FloatingIP + :members: diff --git a/doc/source/user/resources/dns/v2/recordset.rst b/doc/source/user/resources/dns/v2/recordset.rst new file mode 100644 index 000000000..c02302f2d --- /dev/null +++ b/doc/source/user/resources/dns/v2/recordset.rst @@ -0,0 +1,12 @@ +openstack.dns.v2.recordset +========================== + +.. automodule:: openstack.dns.v2.recordset + +The Recordset Class +------------------- + +The ``DNS`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.dns.v2.recordset.Recordset + :members: diff --git a/doc/source/user/resources/dns/v2/zone.rst b/doc/source/user/resources/dns/v2/zone.rst new file mode 100644 index 000000000..634bd8f3f --- /dev/null +++ b/doc/source/user/resources/dns/v2/zone.rst @@ -0,0 +1,12 @@ +openstack.dns.v2.zone +============================== + +.. automodule:: openstack.dns.v2.zone + +The Zone Class +-------------- + +The ``DNS`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.dns.v2.zone.Zone + :members: diff --git a/doc/source/user/resources/dns/v2/zone_export.rst b/doc/source/user/resources/dns/v2/zone_export.rst new file mode 100644 index 000000000..2c2baa3ee --- /dev/null +++ b/doc/source/user/resources/dns/v2/zone_export.rst @@ -0,0 +1,12 @@ +openstack.dns.v2.zone_export +============================ + +.. automodule:: openstack.dns.v2.zone_export + +The ZoneExport Class +-------------------- + +The ``DNS`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.dns.v2.zone_export.ZoneExport + :members: diff --git a/doc/source/user/resources/dns/v2/zone_import.rst b/doc/source/user/resources/dns/v2/zone_import.rst new file mode 100644 index 000000000..5836f539d --- /dev/null +++ b/doc/source/user/resources/dns/v2/zone_import.rst @@ -0,0 +1,12 @@ +openstack.dns.v2.zone_import +============================ + +.. automodule:: openstack.dns.v2.zone_import + +The ZoneImport Class +-------------------- + +The ``DNS`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.dns.v2.zone_import.ZoneImport + :members: diff --git a/doc/source/user/resources/dns/v2/zone_transfer.rst b/doc/source/user/resources/dns/v2/zone_transfer.rst new file mode 100644 index 000000000..9f5c2c4c4 --- /dev/null +++ b/doc/source/user/resources/dns/v2/zone_transfer.rst @@ -0,0 +1,20 @@ +openstack.dns.v2.zone_transfer +============================== + +.. automodule:: openstack.dns.v2.zone_transfer + +The ZoneTransferRequest Class +----------------------------- + +The ``DNS`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.dns.v2.zone_transfer.ZoneTransferRequest + :members: + +The ZoneTransferAccept Class +---------------------------- + +The ``DNS`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.dns.v2.zone_transfer.ZoneTransferAccept + :members: diff --git a/examples/dns/__init__.py b/examples/dns/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/examples/dns/list.py b/examples/dns/list.py new file mode 100644 index 000000000..47024801e --- /dev/null +++ b/examples/dns/list.py @@ -0,0 +1,24 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +List resources from the DNS service. + +For a full guide see TODO(gtema):link to docs on developer.openstack.org +""" + + +def list_zones(conn): + print("List Zones:") + + for zone in conn.dns.zones(): + print(zone) diff --git a/openstack/dns/__init__.py b/openstack/dns/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/dns/dns_service.py b/openstack/dns/dns_service.py new file mode 100644 index 000000000..6fa162b57 --- /dev/null +++ b/openstack/dns/dns_service.py @@ -0,0 +1,22 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack.dns.v2 import _proxy +from openstack import service_description + + +class DnsService(service_description.ServiceDescription): + """The DNS service.""" + + supported_versions = { + '2': _proxy.Proxy, + } diff --git a/openstack/dns/v2/__init__.py b/openstack/dns/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/dns/v2/_proxy.py b/openstack/dns/v2/_proxy.py new file mode 100644 index 000000000..7e36b0c5e --- /dev/null +++ b/openstack/dns/v2/_proxy.py @@ -0,0 +1,494 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack import proxy +from openstack.dns.v2 import recordset as _rs +from openstack.dns.v2 import zone as _zone +from openstack.dns.v2 import zone_import as _zone_import +from openstack.dns.v2 import zone_export as _zone_export +from openstack.dns.v2 import zone_transfer as _zone_transfer +from openstack.dns.v2 import floating_ip as _fip + + +class Proxy(proxy.Proxy): + + # ======== Zones ======== + def zones(self, **query): + """Retrieve a generator of zones + + :param dict query: Optional query parameters to be sent to limit the + resources being returned. + + * `name`: Zone Name field. + * `type`: Zone Type field. + * `email`: Zone email field. + * `status`: Status of the zone. + * `ttl`: TTL field filter.abs + * `description`: Zone description field filter. + + :returns: A generator of zone + :class:`~openstack.dns.v2.zone.Zone` instances. + """ + return self._list(_zone.Zone, **query) + + def create_zone(self, **attrs): + """Create a new zone from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.zone.Zone`, + comprised of the properties on the Zone class. + :returns: The results of zone creation. + :rtype: :class:`~openstack.dns.v2.zone.Zone` + """ + return self._create(_zone.Zone, prepend_key=False, **attrs) + + def get_zone(self, zone): + """Get a zone + + :param zone: The value can be the ID of a zone + or a :class:`~openstack.dns.v2.zone.Zone` instance. + :returns: Zone instance. + :rtype: :class:`~openstack.dns.v2.zone.Zone` + """ + return self._get(_zone.Zone, zone) + + def delete_zone(self, zone, ignore_missing=True): + """Delete a zone + + :param zone: The value can be the ID of a zone + or a :class:`~openstack.dns.v2.zone.Zone` instance. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the zone does not exist. + When set to ``True``, no exception will be set when attempting to + delete a nonexistent zone. + + :returns: Zone been deleted + :rtype: :class:`~openstack.dns.v2.zone.Zone` + """ + return self._delete(_zone.Zone, zone, ignore_missing=ignore_missing) + + def update_zone(self, zone, **attrs): + """Update zone attributes + + :param zone: The id or an instance of + :class:`~openstack.dns.v2.zone.Zone`. + :param dict attrs: attributes for update on + :class:`~openstack.dns.v2.zone.Zone`. + + :rtype: :class:`~openstack.dns.v2.zone.Zone` + """ + return self._update(_zone.Zone, zone, **attrs) + + def find_zone(self, name_or_id, ignore_missing=True): + """Find a single zone + + :param name_or_id: The name or ID of a zone + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised + when the zone does not exist. + When set to ``True``, no exception will be set when attempting + to delete a nonexistent zone. + + :returns: :class:`~openstack.dns.v2.zone.Zone` + """ + return self._find(_zone.Zone, name_or_id, + ignore_missing=ignore_missing) + + def abandon_zone(self, zone, **attrs): + """Abandon Zone + + :param zone: The value can be the ID of a zone to be abandoned + or a :class:`~openstack.dns.v2.zone_export.ZoneExport` instance. + + :returns: None + """ + zone = self._get_resource(_zone.Zone, zone) + + return zone.abandon(self) + + def xfr_zone(self, zone, **attrs): + """Trigger update of secondary Zone + + :param zone: The value can be the ID of a zone to be abandoned + or a :class:`~openstack.dns.v2.zone_export.ZoneExport` instance. + + :returns: None + """ + zone = self._get_resource(_zone.Zone, zone) + return zone.xfr(self) + + # ======== Recordsets ======== + def recordsets(self, zone=None, **query): + """Retrieve a generator of recordsets + + :param zone: The optional value can be the ID of a zone + or a :class:`~openstack.dns.v2.zone.Zone` instance. If it is not + given all recordsets for all zones of the tenant would be + retrieved + :param dict query: Optional query parameters to be sent to limit the + resources being returned. + + * `name`: Recordset Name field. + * `type`: Type field. + * `status`: Status of the recordset. + * `ttl`: TTL field filter. + * `description`: Recordset description field filter. + + :returns: A generator of zone + (:class:`~openstack.dns.v2.recordset.Recordset`) instances + """ + base_path = None + if not zone: + base_path = '/recordsets' + else: + zone = self._get_resource(_zone.Zone, zone) + query.update({'zone_id': zone.id}) + return self._list(_rs.Recordset, base_path=base_path, **query) + + def create_recordset(self, zone, **attrs): + """Create a new recordset in the zone + + :param zone: The value can be the ID of a zone + or a :class:`~openstack.dns.v2.zone.Zone` instance. + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.recordset.Recordset`, + comprised of the properties on the Recordset class. + :returns: The results of zone creation + :rtype: :class:`~openstack.dns.v2.recordset.Recordset` + """ + zone = self._get_resource(_zone.Zone, zone) + attrs.update({'zone_id': zone.id}) + return self._create(_rs.Recordset, prepend_key=False, **attrs) + + def update_recordset(self, recordset, **attrs): + """Update Recordset attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.recordset.Recordset`, + comprised of the properties on the Recordset class. + :returns: The results of zone creation + :rtype: :class:`~openstack.dns.v2.recordset.Recordset` + """ + return self._update(_rs.Recordset, recordset, **attrs) + + def get_recordset(self, recordset, zone): + """Get a recordset + + :param zone: The value can be the ID of a zone + or a :class:`~openstack.dns.v2.zone.Zone` instance. + :param recordset: The value can be the ID of a recordset + or a :class:`~openstack.dns.v2.recordset.Recordset` instance. + :returns: Recordset instance + :rtype: :class:`~openstack.dns.v2.recordset.Recordset` + """ + zone = self._get_resource(_zone.Zone, zone) + return self._get(_rs.Recordset, recordset, zone_id=zone.id) + + def delete_recordset(self, recordset, zone=None, ignore_missing=True): + """Delete a zone + + :param recordset: The value can be the ID of a recordset + or a :class:`~openstack.dns.v2.recordset.Recordset` + instance. + :param zone: The value can be the ID of a zone + or a :class:`~openstack.dns.v2.zone.Zone` instance. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the zone does not exist. When set to ``True``, no exception will + be set when attempting to delete a nonexistent zone. + + :returns: Recordset instance been deleted + :rtype: :class:`~openstack.dns.v2.recordset.Recordset` + """ + if zone: + zone = self._get_resource(_zone.Zone, zone) + recordset = self._get( + _rs.Recordset, recordset, zone_id=zone.id) + return self._delete(_rs.Recordset, recordset, + ignore_missing=ignore_missing) + + # ======== Zone Imports ======== + def zone_imports(self, **query): + """Retrieve a generator of zone imports + + :param dict query: Optional query parameters to be sent to limit the + resources being returned. + + * `zone_id`: Zone I field. + * `message`: Message field. + * `status`: Status of the zone import record. + + :returns: A generator of zone + :class:`~openstack.dns.v2.zone_import.ZoneImport` instances. + """ + return self._list(_zone_import.ZoneImport, **query) + + def create_zone_import(self, **attrs): + """Create a new zone import from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.zone_import.ZoneImport`, + comprised of the properties on the ZoneImport class. + :returns: The results of zone creation. + :rtype: :class:`~openstack.dns.v2.zone_import.ZoneImport` + """ + return self._create(_zone_import.ZoneImport, prepend_key=False, + **attrs) + + def get_zone_import(self, zone_import): + """Get a zone import record + + :param zone: The value can be the ID of a zone import + or a :class:`~openstack.dns.v2.zone_import.ZoneImport` instance. + :returns: ZoneImport instance. + :rtype: :class:`~openstack.dns.v2.zone_import.ZoneImport` + """ + return self._get(_zone_import.ZoneImport, zone_import) + + def delete_zone_import(self, zone_import, ignore_missing=True): + """Delete a zone import + + :param zone_import: The value can be the ID of a zone import + or a :class:`~openstack.dns.v2.zone_import.ZoneImport` instance. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the zone does not exist. + When set to ``True``, no exception will be set when attempting to + delete a nonexistent zone. + + :returns: None + """ + return self._delete(_zone_import.ZoneImport, zone_import, + ignore_missing=ignore_missing) + + # ======== Zone Exports ======== + def zone_exports(self, **query): + """Retrieve a generator of zone exports + + :param dict query: Optional query parameters to be sent to limit the + resources being returned. + + * `zone_id`: Zone I field. + * `message`: Message field. + * `status`: Status of the zone import record. + + :returns: A generator of zone + :class:`~openstack.dns.v2.zone_export.ZoneExport` instances. + """ + return self._list(_zone_export.ZoneExport, **query) + + def create_zone_export(self, zone, **attrs): + """Create a new zone export from attributes + + :param zone: The value can be the ID of a zone to be exported + or a :class:`~openstack.dns.v2.zone_export.ZoneExport` instance. + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.zone_export.ZoneExport`, + comprised of the properties on the ZoneExport class. + :returns: The results of zone creation. + :rtype: :class:`~openstack.dns.v2.zone_export.ZoneExport` + """ + zone = self._get_resource(_zone.Zone, zone) + return self._create(_zone_export.ZoneExport, + base_path='/zones/%(zone_id)s/tasks/export', + prepend_key=False, + zone_id=zone.id, + **attrs) + + def get_zone_export(self, zone_export): + """Get a zone export record + + :param zone: The value can be the ID of a zone import + or a :class:`~openstack.dns.v2.zone_export.ZoneExport` instance. + :returns: ZoneExport instance. + :rtype: :class:`~openstack.dns.v2.zone_export.ZoneExport` + """ + return self._get(_zone_export.ZoneExport, zone_export) + + def get_zone_export_text(self, zone_export): + """Get a zone export record as text + + :param zone: The value can be the ID of a zone import + or a :class:`~openstack.dns.v2.zone_export.ZoneExport` instance. + :returns: ZoneExport instance. + :rtype: :class:`~openstack.dns.v2.zone_export.ZoneExport` + """ + return self._get(_zone_export.ZoneExport, zone_export, + base_path='/zones/tasks/export/%(id)s/export') + + def delete_zone_export(self, zone_export, ignore_missing=True): + """Delete a zone export + + :param zone_export: The value can be the ID of a zone import + or a :class:`~openstack.dns.v2.zone_export.ZoneExport` instance. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the zone does not exist. + When set to ``True``, no exception will be set when attempting to + delete a nonexistent zone. + + :returns: None + """ + return self._delete(_zone_export.ZoneExport, zone_export, + ignore_missing=ignore_missing) + + # ======== FloatingIPs ======== + def floating_ips(self, **query): + """Retrieve a generator of recordsets + + :param dict query: Optional query parameters to be sent to limit the + resources being returned. + + * `name`: Recordset Name field. + * `type`: Type field. + * `status`: Status of the recordset. + * `ttl`: TTL field filter. + * `description`: Recordset description field filter. + + :returns: A generator of floatingips + (:class:`~openstack.dns.v2.floating_ip.FloatingIP`) instances + """ + return self._list(_fip.FloatingIP, **query) + + def get_floating_ip(self, floating_ip): + """Get a Floating IP + + :param floating_ip: The value can be the ID of a floating ip + or a :class:`~openstack.dns.v2.floating_ip.FloatingIP` instance. + The ID is in format "region_name:floatingip_id" + :returns: FloatingIP instance. + :rtype: :class:`~openstack.dns.v2.floating_ip.FloatingIP` + """ + return self._get(_fip.FloatingIP, floating_ip) + + def update_floating_ip(self, floating_ip, **attrs): + """Update floating ip attributes + + :param floating_ip: The id or an instance of + :class:`~openstack.dns.v2.fip.FloatingIP`. + :param dict attrs: attributes for update on + :class:`~openstack.dns.v2.fip.FloatingIP`. + + :rtype: :class:`~openstack.dns.v2.fip.FloatingIP` + """ + return self._update(_fip.FloatingIP, floating_ip, **attrs) + + # ======== Zone Transfer ======== + def zone_transfer_requests(self, **query): + """Retrieve a generator of zone transfer requests + + :param dict query: Optional query parameters to be sent to limit the + resources being returned. + + * `status`: Status of the recordset. + + :returns: A generator of transfer requests + (:class:`~openstack.dns.v2.zone_transfer.ZoneTransferRequest`) + instances + """ + return self._list(_zone_transfer.ZoneTransferRequest, **query) + + def get_zone_transfer_request(self, request): + """Get a ZoneTransfer Request info + + :param request: The value can be the ID of a transfer request + or a :class:`~openstack.dns.v2.zone_transfer.ZoneTransferRequest` + instance. + :returns: Zone transfer request instance. + :rtype: :class:`~openstack.dns.v2.zone_transfer.ZoneTransferRequest` + """ + return self._get(_zone_transfer.ZoneTransferRequest, request) + + def create_zone_transfer_request(self, zone, **attrs): + """Create a new ZoneTransfer Request from attributes + + :param zone: The value can be the ID of a zone to be transferred + or a :class:`~openstack.dns.v2.zone_export.ZoneExport` instance. + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.zone_transfer.ZoneTransferRequest`, + comprised of the properties on the ZoneTransferRequest class. + :returns: The results of zone transfer request creation. + :rtype: :class:`~openstack.dns.v2.zone_transfer.ZoneTransferRequest` + """ + zone = self._get_resource(_zone.Zone, zone) + return self._create( + _zone_transfer.ZoneTransferRequest, + base_path='/zones/%(zone_id)s/tasks/transfer_requests', + prepend_key=False, + zone_id=zone.id, + **attrs) + + def update_zone_transfer_request(self, request, **attrs): + """Update ZoneTransfer Request attributes + + :param floating_ip: The id or an instance of + :class:`~openstack.dns.v2.zone_transfer.ZoneTransferRequest`. + :param dict attrs: attributes for update on + :class:`~openstack.dns.v2.zone_transfer.ZoneTransferRequest`. + + :rtype: :class:`~openstack.dns.v2.zone_transfer.ZoneTransferRequest` + """ + return self._update(_zone_transfer.ZoneTransferRequest, + request, **attrs) + + def delete_zone_transfer_request(self, request, ignore_missing=True): + """Delete a ZoneTransfer Request + + :param request: The value can be the ID of a zone transfer request + or a :class:`~openstack.dns.v2.zone_transfer.ZoneTransferRequest` + instance. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the zone does not exist. + When set to ``True``, no exception will be set when attempting to + delete a nonexistent zone. + + :returns: None + """ + return self._delete(_zone_transfer.ZoneTransferRequest, request, + ignore_missing=ignore_missing) + + def zone_transfer_accepts(self, **query): + """Retrieve a generator of zone transfer accepts + + :param dict query: Optional query parameters to be sent to limit the + resources being returned. + + * `status`: Status of the recordset. + + :returns: A generator of transfer accepts + (:class:`~openstack.dns.v2.zone_transfer.ZoneTransferAccept`) + instances + """ + return self._list(_zone_transfer.ZoneTransferAccept, **query) + + def get_zone_transfer_accept(self, accept): + """Get a ZoneTransfer Accept info + + :param request: The value can be the ID of a transfer accept + or a :class:`~openstack.dns.v2.zone_transfer.ZoneTransferAccept` + instance. + :returns: Zone transfer request instance. + :rtype: :class:`~openstack.dns.v2.zone_transfer.ZoneTransferAccept` + """ + return self._get(_zone_transfer.ZoneTransferAccept, accept) + + def create_zone_transfer_accept(self, **attrs): + """Create a new ZoneTransfer Accept from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.zone_transfer.ZoneTransferAccept`, + comprised of the properties on the ZoneTransferAccept class. + :returns: The results of zone transfer request creation. + :rtype: :class:`~openstack.dns.v2.zone_transfer.ZoneTransferAccept` + """ + return self._create(_zone_transfer.ZoneTransferAccept, **attrs) diff --git a/openstack/dns/v2/floating_ip.py b/openstack/dns/v2/floating_ip.py new file mode 100644 index 000000000..f6b4eec97 --- /dev/null +++ b/openstack/dns/v2/floating_ip.py @@ -0,0 +1,40 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack import exceptions +from openstack import resource + + +class FloatingIP(resource.Resource): + """DNS Floating IP Resource""" + resource_key = '' + resources_key = 'floatingips' + base_path = '/reverse/floatingips' + + # capabilities + allow_fetch = True + allow_commit = True + allow_list = True + commit_method = "PATCH" + + #: Properties + #: current action in progress on the resource + action = resource.Body('action') + #: The floatingip address for this PTR record + address = resource.Body('address') + #: Description for this PTR record + description = resource.Body('description') + #: Domain name for this PTR record + ptrdname = resource.Body('ptrdname') + #: status of the resource + status = resource.Body('status') + #: Time to live for this PTR record + ttl = resource.Body('ttl', type=int) diff --git a/openstack/dns/v2/recordset.py b/openstack/dns/v2/recordset.py new file mode 100644 index 000000000..949b25d7e --- /dev/null +++ b/openstack/dns/v2/recordset.py @@ -0,0 +1,64 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack import exceptions +from openstack import resource + + +class Recordset(resource.Resource): + """DNS Recordset Resource""" + resource_key = 'recordset' + resources_key = 'recordsets' + base_path = '/zones/%(zone_id)s/recordsets' + + # capabilities + allow_create = True + allow_fetch = True + allow_commit = True + allow_delete = True + allow_list = True + + _query_mapping = resource.QueryParameters( + 'name', 'type', 'ttl', 'data', 'status', 'description', + 'limit', 'marker') + + #: Properties + #: current action in progress on the resource + action = resource.Body('action') + #: Timestamp when the zone was created + created_at = resource.Body('create_at') + #: Recordset description + description = resource.Body('description') + #: Links contains a `self` pertaining to this zone or a `next` pertaining + #: to next page + links = resource.Body('links', type=dict) + #: DNS Name of the recordset + name = resource.Body('name') + #: ID of the project which the recordset belongs to + project_id = resource.Body('project_id') + #: DNS record value list + records = resource.Body('records', type=list) + #: Recordset status + #: Valid values include: `PENDING_CREATE`, `ACTIVE`,`PENDING_DELETE`, + #: `ERROR` + status = resource.Body('status') + #: Time to live, default 300, available value 300-2147483647 (seconds) + ttl = resource.Body('ttl', type=int) + #: DNS type of the recordset + #: Valid values include `A`, `AAAA`, `MX`, `CNAME`, `TXT`, `NS`, + #: `SSHFP`, `SPF`, `SRV`, `PTR` + type = resource.Body('type') + #: Timestamp when the zone was last updated + updated_at = resource.Body('updated_at') + #: The id of the Zone which this recordset belongs to + zone_id = resource.URI('zone_id') + #: The name of the Zone which this recordset belongs to + zone_name = resource.Body('zone_name') diff --git a/openstack/dns/v2/zone.py b/openstack/dns/v2/zone.py new file mode 100644 index 000000000..737792bcf --- /dev/null +++ b/openstack/dns/v2/zone.py @@ -0,0 +1,96 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack import exceptions +from openstack import resource +from openstack import utils + + +class Zone(resource.Resource): + """DNS ZONE Resource""" + resources_key = 'zones' + base_path = '/zones' + + # capabilities + allow_create = True + allow_fetch = True + allow_commit = True + allow_delete = True + allow_list = True + + commit_method = "PATCH" + + _query_mapping = resource.QueryParameters( + 'name', 'type', 'email', 'status', 'description', 'ttl', + 'limit', 'marker' + ) + + #: Properties + #: current action in progress on the resource + action = resource.Body('action') + #: Attributes + #: Key:Value pairs of information about this zone, and the pool the user + #: would like to place the zone in. This information can be used by the + #: scheduler to place zones on the correct pool. + attributes = resource.Body('attributes', type=dict) + #: Timestamp when the zone was created + created_at = resource.Body('created_at') + #: Zone description + #: *Type: str* + description = resource.Body('description') + #: The administrator email of this zone + #: *Type: str* + email = resource.Body('email') + #: Links contains a `self` pertaining to this zone or a `next` pertaining + #: to next page + links = resource.Body('links', type=dict) + #: The master list for slaver server to fetch DNS + masters = resource.Body('masters', type=list) + #: Zone name + name = resource.Body('name') + #: The pool which manages the zone, assigned by system + pool_id = resource.Body('pool_id') + #: The project id which the zone belongs to + project_id = resource.Body('project_id') + #: Serial number in the SOA record set in the zone, + #: which identifies the change on the primary DNS server + #: *Type: int* + serial = resource.Body('serial', type=int) + #: Zone status + #: Valid values include `PENDING_CREATE`, `ACTIVE`, + #: `PENDING_DELETE`, `ERROR` + status = resource.Body('status') + #: SOA TTL time, unit is seconds, default 300, TTL range 300-2147483647 + #: *Type: int* + ttl = resource.Body('ttl', type=int) + #: Zone type, + #: Valid values include `PRIMARY`, `SECONDARY` + #: *Type: str* + type = resource.Body('type') + #: Timestamp when the zone was last updated + updated_at = resource.Body('updated_at') + + def _action(self, session, action, body): + """Preform actions given the message body. + + """ + url = utils.urljoin(self.base_path, self.id, 'tasks', action) + response = session.post( + url, + json=body) + exceptions.raise_from_response(response) + return response + + def abandon(self, session): + self._action(session, 'abandon', None) + + def xfr(self, session): + self._action(session, 'xfr', None) diff --git a/openstack/dns/v2/zone_export.py b/openstack/dns/v2/zone_export.py new file mode 100644 index 000000000..6cd5c9232 --- /dev/null +++ b/openstack/dns/v2/zone_export.py @@ -0,0 +1,86 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack import exceptions +from openstack import resource + + +class ZoneExport(resource.Resource): + """DNS Zone Exports Resource""" + resource_key = '' + resources_key = 'exports' + base_path = '/zones/tasks/export' + + # capabilities + allow_create = True + allow_fetch = True + allow_delete = True + allow_list = True + + _query_mapping = resource.QueryParameters( + 'zone_id', 'message', 'status' + ) + + #: Properties + #: Timestamp when the zone was created + created_at = resource.Body('created_at') + #: Links contains a `self` pertaining to this zone or a `next` pertaining + #: to next page + links = resource.Body('links', type=dict) + #: Message + message = resource.Body('message') + #: Returns the total_count of resources matching this filter + metadata = resource.Body('metadata', type=list) + #: The project id which the zone belongs to + project_id = resource.Body('project_id') + #: Current status of the zone export + status = resource.Body('status') + #: Timestamp when the zone was last updated + updated_at = resource.Body('updated_at') + #: Version of the resource + version = resource.Body('version', type=int) + #: ID for the zone that was created by this export + zone_id = resource.Body('zone_id') + + def create(self, session, prepend_key=True, base_path=None): + """Create a remote resource based on this instance. + + :param session: The session to use for making this request. + :type session: :class:`~keystoneauth1.adapter.Adapter` + :param prepend_key: A boolean indicating whether the resource_key + should be prepended in a resource creation + request. Default to True. + :param str base_path: Base part of the URI for creating resources, if + different from + :data:`~openstack.resource.Resource.base_path`. + :return: This :class:`Resource` instance. + :raises: :exc:`~openstack.exceptions.MethodNotSupported` if + :data:`Resource.allow_create` is not set to ``True``. + """ + if not self.allow_create: + raise exceptions.MethodNotSupported(self, "create") + + session = self._get_session(session) + microversion = self._get_microversion_for(session, 'create') + # Create ZoneExport requires empty body + # skip _prepare_request completely, since we need just empty body + request = resource._Request( + self.base_path, + None, + None + ) + response = session.post(request.url, + json=request.body, headers=request.headers, + microversion=microversion) + + self.microversion = microversion + self._translate_response(response) + return self diff --git a/openstack/dns/v2/zone_import.py b/openstack/dns/v2/zone_import.py new file mode 100644 index 000000000..5a18bdcc7 --- /dev/null +++ b/openstack/dns/v2/zone_import.py @@ -0,0 +1,86 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack import exceptions +from openstack import resource + + +class ZoneImport(resource.Resource): + """DNS Zone Import Resource""" + resource_key = '' + resources_key = 'imports' + base_path = '/zones/tasks/import' + + # capabilities + allow_create = True + allow_fetch = True + allow_delete = True + allow_list = True + + _query_mapping = resource.QueryParameters( + 'zone_id', 'message', 'status' + ) + + #: Properties + #: Timestamp when the zone was created + created_at = resource.Body('created_at') + #: Links contains a `self` pertaining to this zone or a `next` pertaining + #: to next page + links = resource.Body('links', type=dict) + #: Message + message = resource.Body('message') + #: Returns the total_count of resources matching this filter + metadata = resource.Body('metadata', type=list) + #: The project id which the zone belongs to + project_id = resource.Body('project_id') + #: Current status of the zone import + status = resource.Body('status') + #: Timestamp when the zone was last updated + updated_at = resource.Body('updated_at') + #: Version of the resource + version = resource.Body('version', type=int) + #: ID for the zone that was created by this import + zone_id = resource.Body('zone_id') + + def create(self, session, prepend_key=True, base_path=None): + """Create a remote resource based on this instance. + + :param session: The session to use for making this request. + :type session: :class:`~keystoneauth1.adapter.Adapter` + :param prepend_key: A boolean indicating whether the resource_key + should be prepended in a resource creation + request. Default to True. + :param str base_path: Base part of the URI for creating resources, if + different from + :data:`~openstack.resource.Resource.base_path`. + :return: This :class:`Resource` instance. + :raises: :exc:`~openstack.exceptions.MethodNotSupported` if + :data:`Resource.allow_create` is not set to ``True``. + """ + if not self.allow_create: + raise exceptions.MethodNotSupported(self, "create") + + session = self._get_session(session) + microversion = self._get_microversion_for(session, 'create') + # Create ZoneImport requires empty body and 'text/dns' as content-type + # skip _prepare_request completely, since we need just empty body + request = resource._Request( + self.base_path, + None, + {'content-type': 'text/dns'} + ) + response = session.post(request.url, + json=request.body, headers=request.headers, + microversion=microversion) + + self.microversion = microversion + self._translate_response(response) + return self diff --git a/openstack/dns/v2/zone_transfer.py b/openstack/dns/v2/zone_transfer.py new file mode 100644 index 000000000..2bd95fe3c --- /dev/null +++ b/openstack/dns/v2/zone_transfer.py @@ -0,0 +1,72 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack import resource + + +class ZoneTransferBase(resource.Resource): + """DNS Zone Transfer Request/Accept Base Resource""" + + _query_mapping = resource.QueryParameters( + 'status' + ) + + #: Properties + #: Timestamp when the resource was created + created_at = resource.Body('created_at') + #: Key that is used as part of the zone transfer accept process. + #: This is only shown to the creator, and must be communicated out of band. + key = resource.Body('key') + #: The project id which the zone belongs to + project_id = resource.Body('project_id') + #: Current status of the zone import + status = resource.Body('status') + #: Timestamp when the resource was last updated + updated_at = resource.Body('updated_at') + #: Version of the resource + version = resource.Body('version', type=int) + #: ID for the zone that is being exported + zone_id = resource.Body('zone_id') + + +class ZoneTransferRequest(ZoneTransferBase): + """DNS Zone Transfer Request Resource""" + base_path = '/zones/tasks/transfer_requests' + resources_key = 'transfer_requests' + + # capabilities + allow_create = True + allow_fetch = True + allow_delete = True + allow_list = True + allow_commit = True + + #: Description + description = resource.Body('description') + #: A project ID that the request will be limited to. + #: No other project will be allowed to accept this request. + target_project_id = resource.Body('target_project_id') + #: Name for the zone that is being exported + zone_name = resource.Body('zone_name') + + +class ZoneTransferAccept(ZoneTransferBase): + """DNS Zone Transfer Accept Resource""" + base_path = '/zones/tasks/transfer_accepts' + resources_key = 'transfer_accepts' + + # capabilities + allow_create = True + allow_fetch = True + allow_list = True + + #: Name for the zone that is being exported + zone_transfer_request_id = resource.Body('zone_transfer_request_id') diff --git a/openstack/tests/functional/dns/__init__.py b/openstack/tests/functional/dns/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/functional/dns/v2/__init__.py b/openstack/tests/functional/dns/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/functional/dns/v2/test_zone.py b/openstack/tests/functional/dns/v2/test_zone.py new file mode 100644 index 000000000..2d4527cc8 --- /dev/null +++ b/openstack/tests/functional/dns/v2/test_zone.py @@ -0,0 +1,52 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import random + +from openstack import connection +from openstack.tests.functional import base + + +class TestZone(base.BaseFunctionalTest): + + def setUp(self): + super(TestZone, self).setUp() + self.require_service('dns') + + self.conn = connection.from_config(cloud_name=base.TEST_CLOUD_NAME) + + # Note: zone deletion is not an immediate operation, so each time + # chose a new zone name for a test + # getUniqueString is not guaranteed to return unique string between + # different tests of the same class. + self.ZONE_NAME = 'example-{0}.org.'.format(random.randint(1, 100)) + + self.zone = self.conn.dns.create_zone( + name=self.ZONE_NAME, + email='joe@example.org', + type='PRIMARY', + ttl=7200, + description='example zone' + ) + self.addCleanup(self.conn.dns.delete_zone, self.zone) + + def tearDown(self): + if self.zone: + self.conn.dns.delete_zone(self.zone) + super(TestZone, self).tearDown() + + def test_get_zone(self): + zone = self.conn.dns.get_zone(self.zone) + self.assertEqual(self.zone, zone) + + def test_list_zones(self): + names = [f.name for f in self.conn.dns.zones()] + self.assertIn(self.ZONE_NAME, names) diff --git a/openstack/tests/unit/dns/__init__.py b/openstack/tests/unit/dns/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/unit/dns/v2/__init__.py b/openstack/tests/unit/dns/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/unit/dns/v2/test_floating_ip.py b/openstack/tests/unit/dns/v2/test_floating_ip.py new file mode 100644 index 000000000..412e453c9 --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_floating_ip.py @@ -0,0 +1,56 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack.tests.unit import base + +from openstack.dns.v2 import floating_ip as fip + + +IDENTIFIER = 'RegionOne:id' +EXAMPLE = { + 'status': 'PENDING', + 'ptrdname': 'smtp.example.com.', + 'description': 'This is a floating ip for 127.0.0.1', + 'links': { + 'self': 'dummylink/reverse/floatingips/RegionOne:id' + }, + 'ttl': 600, + 'address': '172.24.4.10', + 'action': 'CREATE', + 'id': IDENTIFIER +} + + +class TestFloatingIP(base.TestCase): + + def test_basic(self): + sot = fip.FloatingIP() + self.assertEqual('', sot.resource_key) + self.assertEqual('floatingips', sot.resources_key) + self.assertEqual('/reverse/floatingips', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertFalse(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_commit) + self.assertFalse(sot.allow_delete) + + self.assertEqual('PATCH', sot.commit_method) + + def test_make_it(self): + sot = fip.FloatingIP(**EXAMPLE) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE['ptrdname'], sot.ptrdname) + self.assertEqual(EXAMPLE['description'], sot.description) + self.assertEqual(EXAMPLE['ttl'], sot.ttl) + self.assertEqual(EXAMPLE['address'], sot.address) + self.assertEqual(EXAMPLE['action'], sot.action) + self.assertEqual(EXAMPLE['status'], sot.status) diff --git a/openstack/tests/unit/dns/v2/test_proxy.py b/openstack/tests/unit/dns/v2/test_proxy.py new file mode 100644 index 000000000..9814ea59d --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_proxy.py @@ -0,0 +1,197 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack.dns.v2 import _proxy +from openstack.dns.v2 import zone +from openstack.dns.v2 import zone_import +from openstack.dns.v2 import zone_export +from openstack.dns.v2 import zone_transfer +from openstack.dns.v2 import recordset +from openstack.dns.v2 import floating_ip +from openstack.tests.unit import test_proxy_base + + +class TestDnsProxy(test_proxy_base.TestProxyBase): + def setUp(self): + super(TestDnsProxy, self).setUp() + self.proxy = _proxy.Proxy(self.session) + + +class TestDnsZone(TestDnsProxy): + def test_zone_create(self): + self.verify_create(self.proxy.create_zone, zone.Zone, + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id', + 'prepend_key': False}) + + def test_zone_delete(self): + self.verify_delete(self.proxy.delete_zone, + zone.Zone, True) + + def test_zone_find(self): + self.verify_find(self.proxy.find_zone, zone.Zone) + + def test_zone_get(self): + self.verify_get(self.proxy.get_zone, zone.Zone) + + def test_zones(self): + self.verify_list(self.proxy.zones, zone.Zone) + + def test_zone_update(self): + self.verify_update(self.proxy.update_zone, zone.Zone) + + def test_zone_abandon(self): + self._verify("openstack.dns.v2.zone.Zone.abandon", + self.proxy.abandon_zone, + method_args=[{'zone': 'id'}]) + + def test_zone_xfr(self): + self._verify("openstack.dns.v2.zone.Zone.xfr", + self.proxy.xfr_zone, + method_args=[{'zone': 'id'}]) + + +class TestDnsRecordset(TestDnsProxy): + def test_recordset_create(self): + self.verify_create(self.proxy.create_recordset, recordset.Recordset, + method_kwargs={'zone': 'id'}, + expected_kwargs={'zone_id': 'id', + 'prepend_key': False}) + + def test_recordset_delete(self): + self.verify_delete(self.proxy.delete_recordset, + recordset.Recordset, True) + + def test_recordset_update(self): + self.verify_update(self.proxy.update_recordset, recordset.Recordset) + + def test_recordset_get(self): + self.verify_get(self.proxy.get_recordset, recordset.Recordset, + method_kwargs={'zone': 'zid'}, + expected_kwargs={'zone_id': 'zid'} + ) + + def test_recordsets(self): + self.verify_list(self.proxy.recordsets, recordset.Recordset, + base_path='/recordsets') + + def test_recordsets_zone(self): + self.verify_list(self.proxy.recordsets, recordset.Recordset, + method_kwargs={'zone': 'zid'}, + expected_kwargs={'zone_id': 'zid'}) + + +class TestDnsFloatIP(TestDnsProxy): + def test_floating_ips(self): + self.verify_list(self.proxy.floating_ips, floating_ip.FloatingIP) + + def test_floating_ip_get(self): + self.verify_get(self.proxy.get_floating_ip, floating_ip.FloatingIP) + + def test_floating_ip_update(self): + self.verify_update(self.proxy.update_floating_ip, + floating_ip.FloatingIP) + + def test_zone_create(self): + self.verify_create(self.proxy.create_zone, zone.Zone, + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id', + 'prepend_key': False}) + + +class TestDnsZoneImport(TestDnsProxy): + def test_zone_import_delete(self): + self.verify_delete(self.proxy.delete_zone_import, + zone_import.ZoneImport, True) + + def test_zone_import_get(self): + self.verify_get(self.proxy.get_zone_import, zone_import.ZoneImport) + + def test_zone_imports(self): + self.verify_list(self.proxy.zone_imports, zone_import.ZoneImport) + + def test_zone_import_create(self): + self.verify_create(self.proxy.create_zone_import, + zone_import.ZoneImport, + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id', + 'prepend_key': False}) + + +class TestDnsZoneExport(TestDnsProxy): + def test_zone_export_delete(self): + self.verify_delete(self.proxy.delete_zone_export, + zone_export.ZoneExport, True) + + def test_zone_export_get(self): + self.verify_get(self.proxy.get_zone_export, zone_export.ZoneExport) + + def test_zone_export_get_text(self): + self.verify_get(self.proxy.get_zone_export_text, + zone_export.ZoneExport, + value=[{'id': 'zone_export_id_value'}], + expected_kwargs={ + 'base_path': '/zones/tasks/export/%(id)s/export' + }) + + def test_zone_exports(self): + self.verify_list(self.proxy.zone_exports, zone_export.ZoneExport) + + def test_zone_export_create(self): + self.verify_create(self.proxy.create_zone_export, + zone_export.ZoneExport, + method_args=[{'id': 'zone_id_value'}], + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id', + 'zone_id': 'zone_id_value', + 'prepend_key': False}) + + +class TestDnsZoneTransferRequest(TestDnsProxy): + def test_zone_transfer_request_delete(self): + self.verify_delete(self.proxy.delete_zone_transfer_request, + zone_transfer.ZoneTransferRequest, True) + + def test_zone_transfer_request_get(self): + self.verify_get(self.proxy.get_zone_transfer_request, + zone_transfer.ZoneTransferRequest) + + def test_zone_transfer_requests(self): + self.verify_list(self.proxy.zone_transfer_requests, + zone_transfer.ZoneTransferRequest) + + def test_zone_transfer_request_create(self): + self.verify_create(self.proxy.create_zone_transfer_request, + zone_transfer.ZoneTransferRequest, + method_args=[{'id': 'zone_id_value'}], + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id', + 'zone_id': 'zone_id_value', + 'prepend_key': False}) + + def test_zone_transfer_request_update(self): + self.verify_update(self.proxy.update_zone_transfer_request, + zone_transfer.ZoneTransferRequest) + + +class TestDnsZoneTransferAccept(TestDnsProxy): + def test_zone_transfer_accept_get(self): + self.verify_get(self.proxy.get_zone_transfer_accept, + zone_transfer.ZoneTransferAccept) + + def test_zone_transfer_accepts(self): + self.verify_list(self.proxy.zone_transfer_accepts, + zone_transfer.ZoneTransferAccept) + + def test_zone_transfer_accept_create(self): + self.verify_create(self.proxy.create_zone_transfer_accept, + zone_transfer.ZoneTransferAccept) diff --git a/openstack/tests/unit/dns/v2/test_recordset.py b/openstack/tests/unit/dns/v2/test_recordset.py new file mode 100644 index 000000000..700a5148a --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_recordset.py @@ -0,0 +1,69 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack.tests.unit import base + +from openstack.dns.v2 import recordset + + +IDENTIFIER = 'NAME' +EXAMPLE = { + 'description': 'This is an example record set.', + 'updated_at': None, + 'records': [ + '10.1.0.2' + ], + 'ttl': 3600, + 'id': IDENTIFIER, + 'name': 'example.org.', + 'project_id': '4335d1f0-f793-11e2-b778-0800200c9a66', + 'zone_id': '2150b1bf-dee2-4221-9d85-11f7886fb15f', + 'zone_name': 'example.com.', + 'created_at': '2014-10-24T19:59:44.000000', + 'version': 1, + 'type': 'A', + 'status': 'ACTIVE', + 'action': 'NONE' +} + + +class TestRecordset(base.TestCase): + + def test_basic(self): + sot = recordset.Recordset() + self.assertEqual('recordset', sot.resource_key) + self.assertEqual('recordsets', sot.resources_key) + self.assertEqual('/zones/%(zone_id)s/recordsets', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_commit) + self.assertTrue(sot.allow_delete) + + self.assertDictEqual({'data': 'data', + 'description': 'description', + 'limit': 'limit', + 'marker': 'marker', + 'name': 'name', + 'status': 'status', + 'ttl': 'ttl', + 'type': 'type'}, + sot._query_mapping._mapping) + + def test_make_it(self): + sot = recordset.Recordset(**EXAMPLE) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE['description'], sot.description) + self.assertEqual(EXAMPLE['ttl'], sot.ttl) + self.assertEqual(EXAMPLE['type'], sot.type) + self.assertEqual(EXAMPLE['name'], sot.name) + self.assertEqual(EXAMPLE['status'], sot.status) diff --git a/openstack/tests/unit/dns/v2/test_zone.py b/openstack/tests/unit/dns/v2/test_zone.py new file mode 100644 index 000000000..5b2daafdf --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_zone.py @@ -0,0 +1,87 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 keystoneauth1 import adapter +import mock + +from openstack.tests.unit import base + +from openstack.dns.v2 import zone + + +IDENTIFIER = 'NAME' +EXAMPLE = { + 'attributes': { + 'tier': 'gold', 'ha': 'true' + }, + 'id': IDENTIFIER, + 'name': 'test.org', + 'email': 'joe@example.org', + 'type': 'PRIMARY', + 'ttl': 7200, + 'description': 'This is an example zone.', + 'status': 'ACTIVE' +} + + +class TestZone(base.TestCase): + + def setUp(self): + super(TestZone, self).setUp() + self.resp = mock.Mock() + self.resp.body = None + self.resp.json = mock.Mock(return_value=self.resp.body) + self.resp.status_code = 200 + self.sess = mock.Mock(spec=adapter.Adapter) + self.sess.post = mock.Mock(return_value=self.resp) + self.sess.default_microversion = None + + def test_basic(self): + sot = zone.Zone() + self.assertEqual(None, sot.resource_key) + self.assertEqual('zones', sot.resources_key) + self.assertEqual('/zones', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_commit) + self.assertTrue(sot.allow_delete) + + self.assertEqual('PATCH', sot.commit_method) + + self.assertDictEqual({'description': 'description', + 'email': 'email', + 'limit': 'limit', + 'marker': 'marker', + 'name': 'name', + 'status': 'status', + 'ttl': 'ttl', + 'type': 'type'}, + sot._query_mapping._mapping) + + def test_make_it(self): + sot = zone.Zone(**EXAMPLE) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE['email'], sot.email) + self.assertEqual(EXAMPLE['description'], sot.description) + self.assertEqual(EXAMPLE['ttl'], sot.ttl) + self.assertEqual(EXAMPLE['type'], sot.type) + self.assertEqual(EXAMPLE['name'], sot.name) + self.assertEqual(EXAMPLE['status'], sot.status) + + def test_abandon(self): + sot = zone.Zone(**EXAMPLE) + self.assertIsNone(sot.abandon(self.sess)) + self.sess.post.assert_called_with( + 'zones/NAME/tasks/abandon', + json=None + ) diff --git a/openstack/tests/unit/dns/v2/test_zone_export.py b/openstack/tests/unit/dns/v2/test_zone_export.py new file mode 100644 index 000000000..5b7876298 --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_zone_export.py @@ -0,0 +1,80 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import mock +from keystoneauth1 import adapter +from openstack.tests.unit import base + +from openstack.dns.v2 import zone_export + + +IDENTIFIER = '074e805e-fe87-4cbb-b10b-21a06e215d41' +EXAMPLE = { + 'status': 'COMPLETE', + 'zone_id': '6625198b-d67d-47dc-8d29-f90bd60f3ac4', + 'links': { + 'self': 'http://127.0.0.1:9001/v2/zones/tasks/exports/074e805e-f', + 'href': 'http://127.0.0.1:9001/v2/zones/6625198b-d67d-' + }, + 'created_at': '2015-05-08T15:43:42.000000', + 'updated_at': '2015-05-08T15:43:43.000000', + 'version': 2, + 'location': 'designate://v2/zones/tasks/exports/8ec17fe1/export', + 'message': 'example.com. exported', + 'project_id': 'noauth-project', + 'id': IDENTIFIER +} + + +@mock.patch.object(zone_export.ZoneExport, '_translate_response', mock.Mock()) +class TestZoneExport(base.TestCase): + + def test_basic(self): + sot = zone_export.ZoneExport() + self.assertEqual('', sot.resource_key) + self.assertEqual('exports', sot.resources_key) + self.assertEqual('/zones/tasks/export', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertFalse(sot.allow_commit) + self.assertTrue(sot.allow_delete) + + self.assertDictEqual({'limit': 'limit', + 'marker': 'marker', + 'message': 'message', + 'status': 'status', + 'zone_id': 'zone_id'}, + sot._query_mapping._mapping) + + def test_make_it(self): + sot = zone_export.ZoneExport(**EXAMPLE) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE['created_at'], sot.created_at) + self.assertEqual(EXAMPLE['updated_at'], sot.updated_at) + self.assertEqual(EXAMPLE['version'], sot.version) + self.assertEqual(EXAMPLE['message'], sot.message) + self.assertEqual(EXAMPLE['project_id'], sot.project_id) + self.assertEqual(EXAMPLE['status'], sot.status) + self.assertEqual(EXAMPLE['zone_id'], sot.zone_id) + + def test_create(self): + sot = zone_export.ZoneExport() + response = mock.Mock() + response.json = mock.Mock(return_value='') + self.session = mock.Mock(spec=adapter.Adapter) + self.session.default_microversion = '1.1' + + sot.create(self.session) + self.session.post.assert_called_once_with( + mock.ANY, json=None, + headers=None, + microversion=self.session.default_microversion) diff --git a/openstack/tests/unit/dns/v2/test_zone_import.py b/openstack/tests/unit/dns/v2/test_zone_import.py new file mode 100644 index 000000000..808830338 --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_zone_import.py @@ -0,0 +1,79 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import mock +from keystoneauth1 import adapter +from openstack.tests.unit import base + +from openstack.dns.v2 import zone_import + + +IDENTIFIER = '074e805e-fe87-4cbb-b10b-21a06e215d41' +EXAMPLE = { + 'status': 'COMPLETE', + 'zone_id': '6625198b-d67d-47dc-8d29-f90bd60f3ac4', + 'links': { + 'self': 'http://127.0.0.1:9001/v2/zones/tasks/imports/074e805e-f', + 'href': 'http://127.0.0.1:9001/v2/zones/6625198b-d67d-' + }, + 'created_at': '2015-05-08T15:43:42.000000', + 'updated_at': '2015-05-08T15:43:43.000000', + 'version': 2, + 'message': 'example.com. imported', + 'project_id': 'noauth-project', + 'id': IDENTIFIER +} + + +@mock.patch.object(zone_import.ZoneImport, '_translate_response', mock.Mock()) +class TestZoneImport(base.TestCase): + + def test_basic(self): + sot = zone_import.ZoneImport() + self.assertEqual('', sot.resource_key) + self.assertEqual('imports', sot.resources_key) + self.assertEqual('/zones/tasks/import', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertFalse(sot.allow_commit) + self.assertTrue(sot.allow_delete) + + self.assertDictEqual({'limit': 'limit', + 'marker': 'marker', + 'message': 'message', + 'status': 'status', + 'zone_id': 'zone_id'}, + sot._query_mapping._mapping) + + def test_make_it(self): + sot = zone_import.ZoneImport(**EXAMPLE) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE['created_at'], sot.created_at) + self.assertEqual(EXAMPLE['updated_at'], sot.updated_at) + self.assertEqual(EXAMPLE['version'], sot.version) + self.assertEqual(EXAMPLE['message'], sot.message) + self.assertEqual(EXAMPLE['project_id'], sot.project_id) + self.assertEqual(EXAMPLE['status'], sot.status) + self.assertEqual(EXAMPLE['zone_id'], sot.zone_id) + + def test_create(self): + sot = zone_import.ZoneImport() + response = mock.Mock() + response.json = mock.Mock(return_value='') + self.session = mock.Mock(spec=adapter.Adapter) + self.session.default_microversion = '1.1' + + sot.create(self.session) + self.session.post.assert_called_once_with( + mock.ANY, json=None, + headers={'content-type': 'text/dns'}, + microversion=self.session.default_microversion) diff --git a/openstack/tests/unit/dns/v2/test_zone_transfer.py b/openstack/tests/unit/dns/v2/test_zone_transfer.py new file mode 100644 index 000000000..7064834fe --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_zone_transfer.py @@ -0,0 +1,103 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 openstack.tests.unit import base + +from openstack.dns.v2 import zone_transfer + + +IDENTIFIER = '074e805e-fe87-4cbb-b10b-21a06e215d41' +EXAMPLE_REQUEST = { + 'created_at': '2014-07-17T20:34:40.882579', + 'description': 'some description', + 'id': IDENTIFIER, + 'key': '9Z2R50Y0', + 'project_id': '1', + 'status': 'ACTIVE', + 'target_project_id': '123456', + 'updated_at': None, + 'zone_id': '6b78734a-aef1-45cd-9708-8eb3c2d26ff8', + 'zone_name': 'qa.dev.example.com.', +} +EXAMPLE_ACCEPT = { + 'status': 'COMPLETE', + 'zone_id': 'b4542f5a-f1ea-4ec1-b850-52db9dc3f465', + 'created_at': '2016-06-22 06:13:55', + 'updated_at': 'null', + 'key': 'FUGXMZ5N', + 'project_id': '2e43de7ce3504a8fb90a45382532c37e', + 'id': IDENTIFIER, + 'zone_transfer_request_id': '794fdf58-6e1d-41da-8b2d-16b6d10c8827' +} + + +class TestZoneTransferRequest(base.TestCase): + + def test_basic(self): + sot = zone_transfer.ZoneTransferRequest() + # self.assertEqual('', sot.resource_key) + self.assertEqual('transfer_requests', sot.resources_key) + self.assertEqual('/zones/tasks/transfer_requests', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_commit) + self.assertTrue(sot.allow_delete) + + self.assertDictEqual({'limit': 'limit', + 'marker': 'marker', + 'status': 'status'}, + sot._query_mapping._mapping) + + def test_make_it(self): + sot = zone_transfer.ZoneTransferRequest(**EXAMPLE_REQUEST) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE_REQUEST['created_at'], sot.created_at) + self.assertEqual(EXAMPLE_REQUEST['updated_at'], sot.updated_at) + self.assertEqual(EXAMPLE_REQUEST['description'], sot.description) + self.assertEqual(EXAMPLE_REQUEST['key'], sot.key) + self.assertEqual(EXAMPLE_REQUEST['project_id'], sot.project_id) + self.assertEqual(EXAMPLE_REQUEST['status'], sot.status) + self.assertEqual(EXAMPLE_REQUEST['target_project_id'], + sot.target_project_id) + self.assertEqual(EXAMPLE_REQUEST['zone_id'], sot.zone_id) + self.assertEqual(EXAMPLE_REQUEST['zone_name'], sot.zone_name) + + +class TestZoneTransferAccept(base.TestCase): + + def test_basic(self): + sot = zone_transfer.ZoneTransferAccept() + # self.assertEqual('', sot.resource_key) + self.assertEqual('transfer_accepts', sot.resources_key) + self.assertEqual('/zones/tasks/transfer_accepts', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertFalse(sot.allow_commit) + self.assertFalse(sot.allow_delete) + + self.assertDictEqual({'limit': 'limit', + 'marker': 'marker', + 'status': 'status'}, + sot._query_mapping._mapping) + + def test_make_it(self): + sot = zone_transfer.ZoneTransferAccept(**EXAMPLE_ACCEPT) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE_ACCEPT['created_at'], sot.created_at) + self.assertEqual(EXAMPLE_ACCEPT['updated_at'], sot.updated_at) + self.assertEqual(EXAMPLE_ACCEPT['key'], sot.key) + self.assertEqual(EXAMPLE_ACCEPT['project_id'], sot.project_id) + self.assertEqual(EXAMPLE_ACCEPT['status'], sot.status) + self.assertEqual(EXAMPLE_ACCEPT['zone_id'], sot.zone_id) + self.assertEqual(EXAMPLE_ACCEPT['zone_transfer_request_id'], + sot.zone_transfer_request_id) diff --git a/openstack/tests/unit/test_proxy_base.py b/openstack/tests/unit/test_proxy_base.py index 7b4bf6cdc..e8e1eb834 100644 --- a/openstack/tests/unit/test_proxy_base.py +++ b/openstack/tests/unit/test_proxy_base.py @@ -207,6 +207,7 @@ class TestProxyBase(base.TestCase): if 'paginated' in kwargs: expected_kwargs.update({"paginated": kwargs.pop('paginated')}) method_kwargs = kwargs.pop("method_kwargs", {}) + expected_kwargs["base_path"] = kwargs.pop("base_path", None) self._verify2(mock_method, test_method, method_kwargs=method_kwargs, expected_args=[resource_type], diff --git a/releasenotes/notes/add-dns-606cc018e01d40fa.yaml b/releasenotes/notes/add-dns-606cc018e01d40fa.yaml new file mode 100644 index 000000000..dcaab35dc --- /dev/null +++ b/releasenotes/notes/add-dns-606cc018e01d40fa.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds support for `dns + `_ service.