Add v1 quotas and records for Designate

Clients are refactored to put common initialization in the
new DesignateClient base class.

Change-Id: Icf760442156789a386abeb128d8e71979805e04c
This commit is contained in:
Paul Glass 2014-03-13 11:12:29 -05:00
parent 5e1a70b4b6
commit 87f4ddc194
13 changed files with 441 additions and 16 deletions

View File

@ -0,0 +1,29 @@
"""
Copyright 2014 Rackspace
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 cafe.engine.http.client import AutoMarshallingHTTPClient
class DesignateClient(AutoMarshallingHTTPClient):
def __init__(self, url, serialize_format, deserialize_format):
super(DesignateClient, self).__init__(serialize_format,
deserialize_format)
self.url = url.rstrip('/')
self.default_headers['Content-Type'] = 'application/{0}'.format(
self.serialize_format)
self.default_headers['Accept'] = 'application/{0}'.format(
self.deserialize_format)

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
"""
from cafe.engine.clients.rest import AutoMarshallingRestClient
from cloudcafe.designate.client import DesignateClient
from cloudcafe.designate.v1.domain_api.models.requests import DomainRequest
from cloudcafe.designate.v1.domain_api.models.responses import (
DomainResponse, DomainListResponse)
@ -22,17 +22,12 @@ from cloudcafe.designate.v1.server_api.models.responses import (
ServerResponse, ServerListResponse)
class DomainAPIClient(AutoMarshallingRestClient):
class DomainAPIClient(DesignateClient):
def __init__(self, url, serialize_format=None,
deserialize_format=None):
super(DomainAPIClient, self).__init__(serialize_format,
super(DomainAPIClient, self).__init__(url, serialize_format,
deserialize_format)
self.url = url.rstrip('/')
self.default_headers['Content-Type'] = 'application/{0}'.format(
self.serialize_format)
self.default_headers['Accept'] = 'application/{0}'.format(
self.serialize_format)
def _get_domains_url(self):
return "{0}/domains".format(self.url)

View File

@ -0,0 +1,15 @@
"""
Copyright 2013 Rackspace
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.
"""

View File

@ -0,0 +1,57 @@
"""
Copyright 2013 Rackspace
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 cloudcafe.designate.client import DesignateClient
from cloudcafe.designate.v1.quotas_api.models.requests import QuotasRequest
from cloudcafe.designate.v1.quotas_api.models.responses import QuotasResponse
class QuotasAPIClient(DesignateClient):
def __init__(self, url, serialize_format=None, deserialize_format=None):
super(QuotasAPIClient, self).__init__(url, serialize_format,
deserialize_format)
def _get_quotas_url(self):
return "{0}/quotas".format(self.url)
def _get_quota_url(self, tenant_id):
return "{0}/{1}".format(self._get_quotas_url(), tenant_id)
def update_quotas(self, tenant_id=None, domains=None,
recordset_records=None, domain_records=None,
domain_recordsets=None, **requestslib_kwargs):
"""PUT /quotas/{tenantID}"""
quota_req = QuotasRequest(domains=domains,
recordset_records=recordset_records,
domain_records=domain_records,
domain_recordsets=domain_recordsets)
url = self._get_quota_url(tenant_id)
return self.request('PUT', url, response_entity_type=QuotasResponse,
request_entity=quota_req,
requestslib_kwargs=requestslib_kwargs)
def get_quotas(self, tenant_id, **requestslib_kwargs):
"""GET /quotas/{tenantID}"""
url = self._get_quota_url(tenant_id)
return self.request('GET', url, response_entity_type=QuotasResponse,
requestslib_kwargs=requestslib_kwargs)
def delete_quotas(self, tenant_id, **requestslib_kwargs):
"""DELETE /quotas/{tenantID}"""
url = self._get_quota_url(tenant_id)
return self.request('DELETE', url, response_entity_type=QuotasResponse,
requestslib_kwargs=requestslib_kwargs)

View File

@ -0,0 +1,15 @@
"""
Copyright 2013 Rackspace
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.
"""

View File

@ -0,0 +1,38 @@
"""
Copyright 2013 Rackspace
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 json
from cafe.engine.models.base import AutoMarshallingModel
class QuotasRequest(AutoMarshallingModel):
def __init__(self, domains=None, recordset_records=None,
domain_records=None, domain_recordsets=None):
self.domains = domains
self.recordset_records = recordset_records
self.domain_records = domain_records
self.domain_recordsets = domain_recordsets
def _obj_to_json(self):
return json.dumps(self._obj_to_dict())
def _obj_to_dict(self):
return {"domain_records": self.domain_records,
"domain_recordsets": self.domain_recordsets,
"domains": self.domains,
"recordset_records": self.recordset_records}

View File

@ -0,0 +1,37 @@
"""
Copyright 2013 Rackspace
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 json
from cafe.engine.models.base import AutoMarshallingModel
class QuotasResponse(AutoMarshallingModel):
def __init__(self, domains=None, recordset_records=None,
domain_records=None, domain_recordsets=None):
self.domains = domains
self.recordset_records = recordset_records
self.domain_records = domain_records
self.domain_recordsets = domain_recordsets
@classmethod
def _json_to_obj(cls, serialized_str):
response_json = json.loads(serialized_str)
return QuotasResponse(response_json.get('domains'),
response_json.get('recordset_records'),
response_json.get('domains_records'),
response_json.get('domains_recordsets'))

View File

@ -0,0 +1,15 @@
"""
Copyright 2013 Rackspace
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.
"""

View File

@ -0,0 +1,79 @@
"""
Copyright 2013 Rackspace
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 cloudcafe.designate.client import DesignateClient
from cloudcafe.designate.v1.record_api.models.requests import RecordRequest
from cloudcafe.designate.v1.record_api.models.responses import (
RecordResponse, RecordListResponse)
class RecordsAPIClient(DesignateClient):
def __init__(self, url, serialize_format=None,
deserialize_format=None):
super(RecordsAPIClient, self).__init__(url, serialize_format,
deserialize_format)
def _get_domain_url(self, domain_id):
return "{0}/domains/{1}".format(self.url, domain_id)
def _get_records_url(self, domain_id):
return "{0}/records".format(self._get_domain_url(domain_id))
def _get_record_url(self, domain_id, record_id):
return "{0}/{1}".format(self._get_records_url(domain_id), record_id)
def create_record(self, name=None, type=None,
data=None, priority=None,
domain_id=None, requestslib_kwargs=None):
"""POST /domains/{domainID}/records"""
record_req = RecordRequest(name=name, data=data, record_type=type,
priority=priority)
url = self._get_records_url(domain_id)
return self.request('POST', url, request_entity=record_req,
response_entity_type=RecordResponse,
requestslib_kwargs=requestslib_kwargs)
def list_records(self, domain_id, requestslib_kwargs=None):
"""GET /domains/{domainID}/records"""
url = self._get_records_url(domain_id)
return self.request('GET', url,
response_entity_type=RecordListResponse,
requestslib_kwargs=requestslib_kwargs)
def get_record(self, domain_id, record_id, requestslib_kwargs=None):
"""GET /domains/{domainID}/records/{recordID}"""
url = self._get_record_url(domain_id, record_id)
return self.request('GET', url, response_entity_type=RecordResponse,
requestslib_kwargs=requestslib_kwargs)
def update_record(self, domain_id=None, name=None, type=None, data=None,
priority=None, record_id=None, requestslib_kwargs=None):
"""PUT /domains/{domainID}/records/{record_id}"""
record_req = RecordRequest(name=name, data=data,
record_type=type, priority=priority)
url = self._get_record_url(domain_id, record_id)
return self.request('PUT', url,
response_entity_type=RecordResponse,
request_entity=record_req,
requestslib_kwargs=requestslib_kwargs)
def delete_record(self, domain_id, record_id, requestslib_kwargs=None):
"""DELETE /domains/{domainID}/records/{recordID}"""
url = self._get_record_url(domain_id, record_id)
return self.request('DELETE', url,
response_entity_type=RecordResponse,
requestslib_kwargs=requestslib_kwargs)

View File

@ -0,0 +1,15 @@
"""
Copyright 2013 Rackspace
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.
"""

View File

@ -0,0 +1,49 @@
"""
Copyright 2013 Rackspace
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 json
from cafe.engine.models.base import AutoMarshallingModel
class RecordRequest(AutoMarshallingModel):
def __init__(self, name=None, record_type=None, data=None, ttl=None,
priority=None):
self.name = name
self.type = record_type
self.data = data
self.ttl = ttl
self.priority = priority
def _obj_to_json(self):
records = self._obj_to_dict()
return json.dumps(records)
def _obj_to_dict(self):
"""
{ "name": "www.example.com.",
"type": "A",
"data": "192.0.2.3" }
"""
records = {
"name": self.name,
"type": self.type,
"data": self.data,
"ttl": self.ttl,
"priority": self.priority,
}
return self._remove_empty_values(records)

View File

@ -0,0 +1,86 @@
"""
Copyright 2013 Rackspace
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 json
from cafe.engine.models.base import AutoMarshallingModel
from cafe.engine.models.base import AutoMarshallingListModel
class RecordResponse(AutoMarshallingModel):
def __init__(self, id=None, name=None, type=None, created_at=None,
updated_at=None, domain_id=None, tenant_id=None, ttl=None,
priority=None, data=None, description=None, version=None):
self.id = id
self.name = name
self.type = type
self.ttl = ttl
self.created_at = created_at
self.updated_at = updated_at
self.data = data
self.domain_id = domain_id
self.tenant_id = tenant_id
self.priority = priority
self.description = description
self.version = version
@classmethod
def _from_dict(cls, record_dict):
"""
{
"id": "2e32e609-3a4f-45ba-bdef-e50eacd345ad",
"name": "www.example.com.",
"type": "A",
"created_at": "2012-11-02T19:56:26.366792",
"updated_at": null,
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
"ttl": null,
"priority": null,
"data": "192.0.2.3",
"description": null
}
"""
return RecordResponse(id = record_dict.get("id"),
name = record_dict.get("name"),
type = record_dict.get("type"),
ttl = record_dict.get("ttl"),
created_at = record_dict.get("created_at"),
updated_at = record_dict.get("updated_at"),
data = record_dict.get("data"),
domain_id = record_dict.get("domain_id"),
tenant_id = record_dict.get("tenant_id"),
priority = record_dict.get("priority"),
description = record_dict.get("description"),
version = record_dict.get("version"))
@classmethod
def _json_to_obj(cls, serialized_str):
response_json = json.loads(serialized_str)
return cls._from_dict(response_json)
class RecordListResponse(AutoMarshallingListModel):
@classmethod
def _json_to_obj(cls, serialized_str):
response_json = json.loads(serialized_str)
return cls._list_to_obj(response_json.get("records"))
@classmethod
def _list_to_obj(cls, record_list):
return RecordListResponse(
[RecordResponse._from_dict(record) for record in record_list])

View File

@ -14,23 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
"""
from cafe.engine.clients.rest import AutoMarshallingRestClient
from cloudcafe.designate.client import DesignateClient
from cloudcafe.designate.v1.server_api.models.requests import ServerRequest
from cloudcafe.designate.v1.server_api.models.responses import \
ServerResponse, ServerListResponse
class ServerAPIClient(AutoMarshallingRestClient):
class ServerAPIClient(DesignateClient):
def __init__(self, url, serialize_format=None,
deserialize_format=None):
super(ServerAPIClient, self).__init__(serialize_format,
super(ServerAPIClient, self).__init__(url, serialize_format,
deserialize_format)
self.url = url.rstrip('/')
self.default_headers['Content-Type'] = 'application/{0}'.format(
self.serialize_format)
self.default_headers['Accept'] = 'application/{0}'.format(
self.serialize_format)
def _get_servers_url(self):
return "{0}/servers".format(self.url)