Commit b32f00e0 authored by Giorgos Verigakis's avatar Giorgos Verigakis
Browse files

Update image client to the new infrastructure

Delete obsolete HTTPClient.
parent df79206f
# Copyright 2011 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import json
import logging
from httplib import HTTPConnection, HTTPSConnection
from urlparse import urlparse
from . import ClientError
log = logging.getLogger('kamaki.clients')
class HTTPClient(object):
def __init__(self, config):
self.config = config
@property
def url(self):
url = self.config.get('url')
if not url:
raise ClientError('No URL was given')
return url
@property
def token(self):
token = self.config.get('token')
if not token:
raise ClientError('No token was given')
return token
def raw_http_cmd(self, method, path, body=None, headers=None, success=200,
json_reply=False, skip_read=False):
p = urlparse(self.url)
path = p.path + path
if p.scheme == 'http':
conn = HTTPConnection(p.netloc)
elif p.scheme == 'https':
conn = HTTPSConnection(p.netloc)
else:
raise ClientError('Unknown URL scheme')
headers = headers or {}
headers['X-Auth-Token'] = self.token
if body:
headers.setdefault('Content-Type', 'application/json')
headers['Content-Length'] = len(body)
log.debug('>' * 50)
log.debug('%s %s', method, path)
for key, val in headers.items():
log.debug('%s: %s', key, val)
if body:
log.debug('')
log.debug(body)
conn.request(method, path, body, headers)
resp = conn.getresponse()
reply = '' if skip_read else resp.read()
log.debug('<' * 50)
log.info('%d %s', resp.status, resp.reason)
for key, val in resp.getheaders():
log.info('%s: %s', key.capitalize(), val)
log.info('')
log.debug(reply)
log.debug('-' * 50)
if json_reply:
try:
reply = json.loads(reply) if reply else {}
except ValueError:
raise ClientError('Did not receive valid JSON reply',
resp.status, reply)
if success and resp.status != success:
if len(reply) == 1:
if json_reply:
key = reply.keys()[0]
val = reply[key]
message = '%s: %s' % (key, val.get('message', ''))
details = val.get('details', '')
else:
message = reply
details = ''
raise ClientError(message, resp.status, details)
else:
raise ClientError('Invalid response from the server')
return resp, reply
def http_cmd(self, method, path, body=None, headers=None, success=200):
resp, reply = self.raw_http_cmd(method, path, body, headers, success,
json_reply=True)
return reply
def http_get(self, path, success=200):
return self.http_cmd('GET', path, success=success)
def http_post(self, path, body=None, headers=None, success=202):
return self.http_cmd('POST', path, body, headers, success)
def http_put(self, path, body=None, headers=None, success=204):
return self.http_cmd('PUT', path, body, headers, success)
def http_delete(self, path, success=204):
return self.http_cmd('DELETE', path, success=success)
......@@ -31,30 +31,19 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
"""
OpenStack Image Service API 1.0 client
"""
from urllib import quote
from . import Client, ClientError
from . import ClientError
from .http import HTTPClient
class ImageClient(HTTPClient):
@property
def url(self):
url = self.config.get('image_url') or self.config.get('url')
if not url:
raise ClientError('No URL was given')
return url
class ImageClient(Client):
"""OpenStack Image Service API 1.0 and GRNET Plankton client"""
@property
def token(self):
token = self.config.get('image_token') or self.config.get('token')
if not token:
raise ClientError('No token was given')
return token
def raise_for_status(self, r):
if r.status_code == 404:
raise ClientError("Image not found", r.status_code)
# Fallback to the default
super(ImageClient, self).raise_for_status(r)
def list_public(self, detail=False, filters={}, order=''):
path = '/images/detail' if detail else '/images/'
......@@ -70,57 +59,67 @@ class ImageClient(HTTPClient):
if order:
params['sort_key'] = order
if params:
path += '?' + '&'.join('%s=%s' % item for item in params.items())
return self.http_get(path)
r = self.get(path, params=params, success=200)
return r.json
def get_meta(self, image_id):
path = '/images/%s' % image_id
resp, buf = self.raw_http_cmd('HEAD', path)
path = '/images/%s' % (image_id,)
r = self.head(path, success=200)
reply = {}
prefix = 'x-image-meta-'
for key, val in resp.getheaders():
properties = {}
meta_prefix = 'x-image-meta-'
property_prefix = 'x-image-meta-property-'
for key, val in r.headers.items():
key = key.lower()
if not key.startswith(prefix):
continue
key = key[len(prefix):]
reply[key] = val
if key.startswith(property_prefix):
key = key[len(property_prefix):]
properties[key] = val
elif key.startswith(meta_prefix):
key = key[len(meta_prefix):]
reply[key] = val
if properties:
reply['properties'] = properties
return reply
def register(self, name, location, params={}, properties={}):
path = '/images/'
headers = {}
headers['x-image-meta-name'] = quote(name)
headers['x-image-meta-name'] = name
headers['x-image-meta-location'] = location
for key, val in params.items():
if key in ('id', 'store', 'disk_format', 'container_format',
'size', 'checksum', 'is_public', 'owner'):
key = 'x-image-meta-' + key.replace('_', '-')
headers[key] = val
for key, val in properties.items():
headers['x-image-meta-property-' + quote(key)] = quote(val)
return self.http_post(path, headers=headers, success=200)
headers['x-image-meta-property-' + key] = val
self.post(path, headers=headers, success=200)
def list_members(self, image_id):
path = '/images/%s/members' % image_id
reply = self.http_get(path)
return reply['members']
path = '/images/%s/members' % (image_id,)
r = self.get(path, success=200)
return r.json['members']
def list_shared(self, member):
path = '/shared-images/%s' % member
reply = self.http_get(path)
return reply['shared_images']
path = '/shared-images/%s' % (member,)
r = self.get(path, success=200)
return r.json['shared_images']
def add_member(self, image_id, member):
path = '/images/%s/members/%s' % (image_id, member)
self.http_put(path)
r = self.put(path, success=204)
def remove_member(self, image_id, member):
path = '/images/%s/members/%s' % (image_id, member)
self.http_delete(path)
self.delete(path, success=204)
def set_members(self, image_id, members):
path = '/images/%s/members' % image_id
req = {'memberships': [{'member_id': member} for member in members]}
body = json.dumps(req)
self.http_put(path, body)
self.put(path, json=req, success=204)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment