-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* api.api.BaseAPI holds the common operations * api.compute.APIv2, api.identity_v2, api.identity_v3 and api.object_store.APIv1 represent the versioned REST API interfaces * Changes to OSC include adding the API object to the existing client objects, although in time these could replace the client objects. * The existing Object-Store commands in OSC have been moved to openstackclient.api * A couple of Compute commands have been added to demonstrate the CRUD operations * An Identity command was implemented for both v2 and v3 to work out versioning * Tests for the API-layer are included Change-Id: If0bf70adc18ceb5782237fc7f4f8d8cf1936a4c7
- Loading branch information
Dean Troyer
committed
Sep 15, 2014
1 parent
ae957b1
commit 2bfc9e1
Showing
31 changed files
with
1,495 additions
and
1,011 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,236 @@ | |||
# 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. | |||
# | |||
|
|||
"""Base API Library""" | |||
|
|||
import simplejson as json | |||
|
|||
from keystoneclient.openstack.common.apiclient \ | |||
import exceptions as ksc_exceptions | |||
|
|||
|
|||
class BaseAPI(object): | |||
"""Base API""" | |||
|
|||
def __init__( | |||
self, | |||
session=None, | |||
service_type=None, | |||
endpoint=None, | |||
**kwargs | |||
): | |||
"""Base object that contains some common API objects and methods | |||
:param Session session: | |||
The default session to be used for making the HTTP API calls. | |||
:param string service_type: | |||
API name, i.e. ``identity`` or ``compute`` | |||
:param string endpoint: | |||
The URL from the Service Catalog to be used as the base for API | |||
requests on this API. | |||
""" | |||
|
|||
super(BaseAPI, self).__init__() | |||
|
|||
# a requests.Session-style interface | |||
self.session = session | |||
self.service_type = service_type | |||
self.endpoint = endpoint | |||
|
|||
if self.endpoint is None: | |||
# TODO(dtroyer): look it up via session | |||
pass | |||
|
|||
def _request(self, method, url, session=None, **kwargs): | |||
"""Perform call into session | |||
All API calls are funneled through this method to provide a common | |||
place to finalize the passed URL and other things. | |||
:param string method: | |||
The HTTP method name, i.e. ``GET``, ``PUT``, etc | |||
:param string url: | |||
The API-specific portion of the URL path | |||
:param Session session: | |||
HTTP client session | |||
:param kwargs: | |||
keyword arguments passed to requests.request(). | |||
:return: the requests.Response object | |||
""" | |||
|
|||
if not session: | |||
session = self.session | |||
if not session: | |||
# TODO(dtroyer): sort this | |||
raise Exception | |||
|
|||
if self.endpoint: | |||
url = '/'.join([self.endpoint.rstrip('/'), url.lstrip('/')]) | |||
|
|||
# Why is ksc session backwards??? | |||
ret = session.request(url, method, **kwargs) | |||
# print("%s %s return: %s\n%s" % (method, url, ret.status_code, ret._content)) | |||
return ret | |||
|
|||
# The basic action methods all take a Session and return dict/lists | |||
|
|||
def create( | |||
self, | |||
url, | |||
session=None, | |||
**params | |||
): | |||
"""Create a new resource | |||
:param string url: | |||
The API-specific portion of the URL path | |||
:param Session session: | |||
HTTP client session | |||
""" | |||
|
|||
ret = self._request('PUT', url, session=session, **params) | |||
# Should this move into _requests()? | |||
try: | |||
return ret.json() | |||
except json.JSONDecodeError: | |||
pass | |||
return ret | |||
|
|||
def delete( | |||
self, | |||
url, | |||
session=None, | |||
**params | |||
): | |||
"""Delete a resource | |||
:param string url: | |||
The API-specific portion of the URL path | |||
:param Session session: | |||
HTTP client session | |||
""" | |||
|
|||
return self._request('DELETE', url, **params) | |||
|
|||
def list( | |||
self, | |||
url, | |||
session=None, | |||
body=None, | |||
**params | |||
): | |||
"""Return a list of resources | |||
:param string url: | |||
The API-specific portion of the URL path | |||
:param Session session: | |||
HTTP client session | |||
:param body: data that will be encoded as JSON and passed in POST | |||
request (GET will be sent by default) | |||
""" | |||
|
|||
if body: | |||
return self._request( | |||
'POST', | |||
url, | |||
# service=self.service_type, | |||
json=body, | |||
params=params, | |||
).json() | |||
else: | |||
return self._request( | |||
'GET', | |||
url, | |||
# service=self.service_type, | |||
params=params, | |||
).json() | |||
|
|||
# Layered actions built on top of the basic action methods do not | |||
# explicitly take a Session but may still be passed on kwargs | |||
|
|||
def find_bulk( | |||
self, | |||
url, | |||
**kwargs | |||
): | |||
"""Bulk load and filter locally | |||
:param string url: | |||
The API-specific portion of the URL path | |||
:param kwargs: dict of AVPs to match - logical AND | |||
:returns: list of resource dicts | |||
""" | |||
|
|||
items = self.list(url) | |||
if type(items) == dict: | |||
# strip off the enclosing dict | |||
key = list(items.keys())[0] | |||
items = items[key] | |||
|
|||
ret = [] | |||
for o in items: | |||
try: | |||
if all(o[attr] == kwargs[attr] for attr in kwargs.keys()): | |||
ret.append(o) | |||
except KeyError: | |||
continue | |||
|
|||
return ret | |||
|
|||
def find_one( | |||
self, | |||
url, | |||
**kwargs | |||
): | |||
"""Find a resource by name or ID | |||
:param string url: | |||
The API-specific portion of the URL path | |||
:returns: list of resource dicts | |||
""" | |||
|
|||
bulk_list = self.find_bulk(url, **kwargs) | |||
num_bulk = len(bulk_list) | |||
if num_bulk == 0: | |||
msg = "none found" | |||
raise ksc_exceptions.NotFound(msg) | |||
elif num_bulk > 1: | |||
msg = "many found" | |||
raise RuntimeError(msg) | |||
return bulk_list[0] | |||
|
|||
def find( | |||
self, | |||
url, | |||
attr=None, | |||
search=None, | |||
): | |||
"""Find a single resource by name or ID | |||
:param string url: | |||
The API-specific portion of the URL path | |||
:param attr: name of attribute for secondary search | |||
:param search: search expression | |||
""" | |||
|
|||
try: | |||
ret = self._request('GET', "/%s/%s" % (url, search)).json() | |||
except ksc_exceptions.NotFound: | |||
kwargs = {attr: search} | |||
try: | |||
ret = self.find_one("/%s/detail" % (url), **kwargs) | |||
except ksc_exceptions.NotFound: | |||
msg = "%s not found" % search | |||
raise ksc_exceptions.NotFound(msg) | |||
|
|||
return ret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,67 @@ | |||
# 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. | |||
# | |||
|
|||
"""Compute v2 API Library""" | |||
|
|||
from openstackclient.api import api | |||
|
|||
|
|||
class APIv2(api.BaseAPI): | |||
"""Compute v2 API""" | |||
|
|||
def __init__(self, **kwargs): | |||
super(APIv2, self).__init__(**kwargs) | |||
|
|||
def flavor_list( | |||
self, | |||
detailed=True, | |||
is_public=True, | |||
): | |||
"""Get available flavors | |||
:param detailed: retrieve detailed response from server if True | |||
:param is_public: return only public flavors if True | |||
""" | |||
|
|||
params = {} | |||
|
|||
if not is_public: | |||
params['is_public'] = is_public | |||
|
|||
url = "/flavors" | |||
if detailed: | |||
url += "/detail" | |||
|
|||
return self.list(url, **params)['flavors'] | |||
|
|||
def flavor_show( | |||
self, | |||
flavor=None, | |||
): | |||
|
|||
return self.find("flavors", 'name', flavor) | |||
|
|||
def key_list( | |||
self, | |||
): | |||
"""Get available keys | |||
This is an extension, look at loading it separately | |||
""" | |||
|
|||
# Each effin object has the 'keypair' wrapper... | |||
ret = [] | |||
for k in self.list('os-keypairs')['keypairs']: | |||
ret.append(k['keypair']) | |||
|
|||
return ret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,33 @@ | |||
# 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. | |||
# | |||
|
|||
"""Identity v2 API Library""" | |||
|
|||
from openstackclient.api import api | |||
|
|||
|
|||
class APIv2(api.BaseAPI): | |||
"""Identity v2 API""" | |||
|
|||
def __init__(self, **kwargs): | |||
super(APIv2, self).__init__(**kwargs) | |||
|
|||
def project_list( | |||
self, | |||
): | |||
"""Get available projects | |||
can add limit/marker | |||
""" | |||
|
|||
return self.list('tenants')['tenants'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,39 @@ | |||
# 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. | |||
# | |||
|
|||
"""Identity v3 API Library""" | |||
|
|||
from openstackclient.api import api | |||
|
|||
|
|||
class APIv3(api.BaseAPI): | |||
"""Identity v3 API""" | |||
|
|||
def __init__(self, **kwargs): | |||
super(APIv3, self).__init__(**kwargs) | |||
|
|||
def find_domain(self, domain): | |||
"""Find domain by name or id""" | |||
return self.find('domain', attr='name', search=domain)['domain'] | |||
|
|||
def project_list( | |||
self, | |||
**params | |||
): | |||
"""Get available projects | |||
can add limit/marker | |||
need to convert to v3... | |||
""" | |||
|
|||
return self.list('projects')['projects'] |
Oops, something went wrong.