# Copyright 2015 Google Inc. All rights reserved. # # 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. """Define API ResourceRecordSets.""" import six from gcloud._helpers import _rfc3339_to_datetime from gcloud.exceptions import NotFound from gcloud.dns.resource_record_set import ResourceRecordSet class Changes(object): """Changes are bundled additions / deletions of DNS resource records. Changes are owned by a :class:`gcloud.dns.zone.ManagedZone` instance. See: https://cloud.google.com/dns/api/v1/changes :type zone: :class:`gcloud.dns.zone.ManagedZone` :param zone: A zone which holds one or more record sets. """ def __init__(self, zone): self.zone = zone self._properties = {} self._additions = self._deletions = () @classmethod def from_api_repr(cls, resource, zone): """Factory: construct a change set given its API representation :type resource: dict :param resource: change set representation returned from the API :type zone: :class:`gcloud.dns.zone.ManagedZone` :param zone: A zone which holds zero or more change sets. :rtype: :class:`gcloud.dns.changes.Changes` :returns: RRS parsed from ``resource``. """ changes = cls(zone=zone) changes._set_properties(resource) return changes def _set_properties(self, resource): """Helper method for :meth:`from_api_repr`, :meth:`create`, etc. :type resource: dict :param resource: change set representation returned from the API """ resource = resource.copy() self._additions = tuple([ ResourceRecordSet.from_api_repr(added_res, self.zone) for added_res in resource.pop('additions', ())]) self._deletions = tuple([ ResourceRecordSet.from_api_repr(added_res, self.zone) for added_res in resource.pop('deletions', ())]) self._properties = resource @property def path(self): """URL path for change set APIs. :rtype: string :returns: the path based on project, zone, and change set names. """ return '/projects/%s/managedZones/%s/changes/%s' % ( self.zone.project, self.zone.name, self.name) @property def name(self): """Name of the change set. :rtype: string or ``NoneType`` :returns: Name, as set by the back-end, or None. """ return self._properties.get('id') @name.setter def name(self, value): """Update name of the change set. :type value: string :param value: New name for the changeset. """ if not isinstance(value, six.string_types): raise ValueError("Pass a string") self._properties['id'] = value @property def status(self): """Status of the change set. :rtype: string or ``NoneType`` :returns: Status, as set by the back-end, or None. """ return self._properties.get('status') @property def started(self): """Time when the change set was started. :rtype: ``datetime.datetime`` or ``NoneType`` :returns: Time, as set by the back-end, or None. """ stamp = self._properties.get('startTime') if stamp is not None: return _rfc3339_to_datetime(stamp) @property def additions(self): """Resource record sets to be added to the zone. :rtype: sequence of :class:`gcloud.dns.resource_record_set.ResourceRecordSet`. :returns: record sets appended via :meth:`add_record_set` """ return self._additions @property def deletions(self): """Resource record sets to be deleted from the zone. :rtype: sequence of :class:`gcloud.dns.resource_record_set.ResourceRecordSet`. :returns: record sets appended via :meth:`delete_record_set` """ return self._deletions def add_record_set(self, record_set): """Append a record set to the 'additions' for the change set. :type record_set: :class:`gcloud.dns.resource_record_set.ResourceRecordSet` :param record_set: the record set to append :raises: ``ValueError`` if ``record_set`` is not of the required type. """ if not isinstance(record_set, ResourceRecordSet): raise ValueError("Pass a ResourceRecordSet") self._additions += (record_set,) def delete_record_set(self, record_set): """Append a record set to the 'deletions' for the change set. :type record_set: :class:`gcloud.dns.resource_record_set.ResourceRecordSet` :param record_set: the record set to append :raises: ``ValueError`` if ``record_set`` is not of the required type. """ if not isinstance(record_set, ResourceRecordSet): raise ValueError("Pass a ResourceRecordSet") self._deletions += (record_set,) def _require_client(self, client): """Check client or verify over-ride. :type client: :class:`gcloud.dns.client.Client` or ``NoneType`` :param client: the client to use. If not passed, falls back to the ``client`` stored on the current zone. :rtype: :class:`gcloud.dns.client.Client` :returns: The client passed in or the currently bound client. """ if client is None: client = self.zone._client return client def _build_resource(self): """Generate a resource for ``create``.""" additions = [{ 'name': added.name, 'type': added.record_type, 'ttl': str(added.ttl), 'rrdatas': added.rrdatas, } for added in self.additions] deletions = [{ 'name': deleted.name, 'type': deleted.record_type, 'ttl': str(deleted.ttl), 'rrdatas': deleted.rrdatas, } for deleted in self.deletions] return { 'additions': additions, 'deletions': deletions, } def create(self, client=None): """API call: create the change set via a POST request See: https://cloud.google.com/dns/api/v1/changes/create :type client: :class:`gcloud.dns.client.Client` or ``NoneType`` :param client: the client to use. If not passed, falls back to the ``client`` stored on the current zone. """ if len(self.additions) == 0 and len(self.deletions) == 0: raise ValueError("No record sets added or deleted") client = self._require_client(client) path = '/projects/%s/managedZones/%s/changes' % ( self.zone.project, self.zone.name) api_response = client.connection.api_request( method='POST', path=path, data=self._build_resource()) self._set_properties(api_response) def exists(self, client=None): """API call: test for the existence of the change set via a GET request See https://cloud.google.com/dns/api/v1/changes/get :type client: :class:`gcloud.dns.client.Client` or ``NoneType`` :param client: the client to use. If not passed, falls back to the ``client`` stored on the current zone. """ client = self._require_client(client) try: client.connection.api_request(method='GET', path=self.path, query_params={'fields': 'id'}) except NotFound: return False else: return True def reload(self, client=None): """API call: refresh zone properties via a GET request See https://cloud.google.com/dns/api/v1/changes/get :type client: :class:`gcloud.dns.client.Client` or ``NoneType`` :param client: the client to use. If not passed, falls back to the ``client`` stored on the current zone. """ client = self._require_client(client) api_response = client.connection.api_request( method='GET', path=self.path) self._set_properties(api_response)