Adding refactored API Client
This commit is contained in:
parent
9793971bc2
commit
17e3483a43
|
@ -0,0 +1,266 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 Citrix Systems
|
||||
# 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.
|
||||
# @author: Tyler Smith, Cisco Systems
|
||||
|
||||
import httplib
|
||||
import socket
|
||||
import urllib
|
||||
from quantum.common.wsgi import Serializer
|
||||
|
||||
class api_call(object):
|
||||
"""A Decorator to add support for format and tenant overriding"""
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
def with_params(*args, **kwargs):
|
||||
# Backup the format and tenant, then temporarily change them if needed
|
||||
(format, tenant) = (instance.format, instance.tenant)
|
||||
|
||||
if 'format' in kwargs:
|
||||
instance.format = kwargs['format']
|
||||
if 'tenant' in kwargs:
|
||||
instance.tenant = kwargs['tenant']
|
||||
|
||||
ret = self.f(instance, *args)
|
||||
(instance.format, instance.tenant) = (format, tenant)
|
||||
return ret
|
||||
return with_params
|
||||
|
||||
class Client(object):
|
||||
|
||||
"""A base client class - derived from Glance.BaseClient"""
|
||||
|
||||
action_prefix = '/v0.1/tenants/{tenant_id}'
|
||||
|
||||
"""Action query strings"""
|
||||
networks_path = "/networks"
|
||||
network_path = "/networks/%s"
|
||||
ports_path = "/networks/%s/ports"
|
||||
port_path = "/networks/%s/ports/%s"
|
||||
attachment_path = "/networks/%s/ports/%s/attachment"
|
||||
|
||||
def __init__(self, host = "127.0.0.1", port = 9696, use_ssl = False,
|
||||
tenant=None, format="xml", testingStub=None, key_file=None, cert_file=None):
|
||||
"""
|
||||
Creates a new client to some service.
|
||||
|
||||
:param host: The host where service resides
|
||||
:param port: The port where service resides
|
||||
:param use_ssl: True to use SSL, False to use HTTP
|
||||
:param tenant: The tenant ID to make requests with
|
||||
:param format: The format to query the server with
|
||||
:param testingStub: A class that stubs basic server attributes for tests
|
||||
:param key_file: The SSL key file to use if use_ssl is true
|
||||
:param cert_file: The SSL cert file to use if use_ssl is true
|
||||
"""
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.use_ssl = use_ssl
|
||||
self.tenant = tenant
|
||||
self.format = format
|
||||
self.connection = None
|
||||
self.testingStub = testingStub
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
|
||||
def get_connection_type(self):
|
||||
"""
|
||||
Returns the proper connection type
|
||||
"""
|
||||
if self.testingStub:
|
||||
return self.testingStub
|
||||
if self.use_ssl:
|
||||
return httplib.HTTPSConnection
|
||||
else:
|
||||
return httplib.HTTPConnection
|
||||
|
||||
def do_request(self, method, action, body=None,
|
||||
headers=None, params=None):
|
||||
"""
|
||||
Connects to the server and issues a request.
|
||||
Returns the result data, or raises an appropriate exception if
|
||||
HTTP status code is not 2xx
|
||||
|
||||
:param method: HTTP method ("GET", "POST", "PUT", etc...)
|
||||
:param body: string of data to send, or None (default)
|
||||
:param headers: mapping of key/value pairs to add as headers
|
||||
:param params: dictionary of key/value pairs to add to append
|
||||
to action
|
||||
|
||||
"""
|
||||
|
||||
# Ensure we have a tenant id
|
||||
if not self.tenant:
|
||||
raise Exception("Tenant ID not set")
|
||||
|
||||
# Add format and tenant_id
|
||||
action += ".%s" % self.format
|
||||
action = Client.action_prefix + action
|
||||
action = action.replace('{tenant_id}',self.tenant)
|
||||
|
||||
if type(params) is dict:
|
||||
action += '?' + urllib.urlencode(params)
|
||||
|
||||
try:
|
||||
connection_type = self.get_connection_type()
|
||||
headers = headers or {}
|
||||
|
||||
# Open connection and send request, handling SSL certs
|
||||
certs = {'key_file':self.key_file, 'cert_file':self.cert_file}
|
||||
|
||||
# Only use ssl certs if the cert dict has 1 or more file strings
|
||||
if len([x for x in certs if x != None]):
|
||||
c = connection_type(self.host, self.port, **certs)
|
||||
else:
|
||||
c = connection_type(self.host, self.port)
|
||||
|
||||
c.request(method, action, body, headers)
|
||||
res = c.getresponse()
|
||||
status_code = self.get_status_code(res)
|
||||
if status_code in (httplib.OK,
|
||||
httplib.CREATED,
|
||||
httplib.ACCEPTED,
|
||||
httplib.NO_CONTENT):
|
||||
return self.deserialize(res)
|
||||
else:
|
||||
raise Exception("Server returned error: %s" % res.read())
|
||||
|
||||
except (socket.error, IOError), e:
|
||||
raise Exception("Unable to connect to "
|
||||
"server. Got error: %s" % e)
|
||||
|
||||
def get_status_code(self, response):
|
||||
"""
|
||||
Returns the integer status code from the response, which
|
||||
can be either a Webob.Response (used in testing) or httplib.Response
|
||||
"""
|
||||
if hasattr(response, 'status_int'):
|
||||
return response.status_int
|
||||
else:
|
||||
return response.status
|
||||
|
||||
def serialize(self, data):
|
||||
if type(data) is dict:
|
||||
return Serializer().serialize(data, self.content_type())
|
||||
|
||||
def deserialize(self, data):
|
||||
if self.get_status_code(data) == 202:
|
||||
return data.read()
|
||||
return Serializer().deserialize(data.read(), self.content_type())
|
||||
|
||||
def content_type(self, format=None):
|
||||
if not format:
|
||||
format = self.format
|
||||
return "application/%s" % (format)
|
||||
|
||||
@api_call
|
||||
def list_networks(self):
|
||||
"""
|
||||
Queries the server for a list of networks
|
||||
"""
|
||||
return self.do_request("GET", self.networks_path)
|
||||
|
||||
@api_call
|
||||
def list_network_details(self, network):
|
||||
"""
|
||||
Queries the server for the details of a certain network
|
||||
"""
|
||||
return self.do_request("GET", (self.network_path%network))
|
||||
|
||||
@api_call
|
||||
def create_network(self, body=None):
|
||||
"""
|
||||
Creates a new network on the server
|
||||
"""
|
||||
body = self.serialize(body)
|
||||
return self.do_request("POST", self.networks_path, body=body)
|
||||
|
||||
@api_call
|
||||
def update_network(self, network, body=None):
|
||||
"""
|
||||
Updates a network on the server
|
||||
"""
|
||||
body = self.serialize(body)
|
||||
return self.do_request("PUT", self.network_path % (network),body=body)
|
||||
|
||||
@api_call
|
||||
def delete_network(self, network):
|
||||
"""
|
||||
Deletes a network on the server
|
||||
"""
|
||||
return self.do_request("DELETE", self.network_path % (network))
|
||||
|
||||
@api_call
|
||||
def list_ports(self, network):
|
||||
"""
|
||||
Queries the server for a list of ports on a given network
|
||||
"""
|
||||
return self.do_request("GET", self.ports_path % (network))
|
||||
|
||||
@api_call
|
||||
def list_port_details(self, network, port):
|
||||
"""
|
||||
Queries the server for a list of ports on a given network
|
||||
"""
|
||||
return self.do_request("GET", self.port_path % (network,port))
|
||||
|
||||
@api_call
|
||||
def create_port(self, network):
|
||||
"""
|
||||
Creates a new port on a network on the server
|
||||
"""
|
||||
return self.do_request("POST", self.ports_path % (network))
|
||||
|
||||
@api_call
|
||||
def delete_port(self, network, port):
|
||||
"""
|
||||
Deletes a port from a network on the server
|
||||
"""
|
||||
return self.do_request("DELETE", self.port_path % (network,port))
|
||||
|
||||
@api_call
|
||||
def set_port_state(self, network, port, body=None):
|
||||
"""
|
||||
Sets the state of a port on the server
|
||||
"""
|
||||
body = self.serialize(body)
|
||||
return self.do_request("PUT",
|
||||
self.port_path % (network,port), body=body)
|
||||
|
||||
@api_call
|
||||
def list_port_attachments(self, network, port):
|
||||
"""
|
||||
Deletes a port from a network on the server
|
||||
"""
|
||||
return self.do_request("GET", self.attachment_path % (network,port))
|
||||
|
||||
@api_call
|
||||
def attach_resource(self, network, port, body=None):
|
||||
"""
|
||||
Deletes a port from a network on the server
|
||||
"""
|
||||
body = self.serialize(body)
|
||||
return self.do_request("PUT",
|
||||
self.attachment_path % (network,port), body=body)
|
||||
|
||||
@api_call
|
||||
def detach_resource(self, network, port):
|
||||
"""
|
||||
Deletes a port from a network on the server
|
||||
"""
|
||||
return self.do_request("DELETE", self.attachment_path % (network,port))
|
Loading…
Reference in New Issue