Introducing driver framework. db and glance implemented as drivers
Change-Id: If47bb1b7eb92e3eafe978ad63d60d801621b8952
This commit is contained in:
parent
c897e9d9f0
commit
313eb511e2
@ -15,6 +15,8 @@
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from graffiti.common import driver_factory
|
||||
|
||||
# Server Specific Configurations
|
||||
server = {
|
||||
'port': '21075',
|
||||
@ -142,3 +144,7 @@ pydevd = {
|
||||
'port': 22075,
|
||||
'bindhost': 'localhost'
|
||||
}
|
||||
|
||||
|
||||
# Discover and load drivers
|
||||
df = driver_factory.DriverFactory()
|
||||
|
@ -21,103 +21,97 @@ from wsme.api import Response
|
||||
from wsmeext.pecan import wsexpose
|
||||
|
||||
from graffiti.api.model.v1.resource import Resource
|
||||
from graffiti.api.plugins.glance_image import GlanceImage
|
||||
|
||||
|
||||
from graffiti.api.model.v1.resource_dao_factory import \
|
||||
ResourceDAOFactory
|
||||
|
||||
from graffiti.common.utils import _
|
||||
|
||||
from oslo.config import cfg
|
||||
from graffiti.common import driver_factory
|
||||
|
||||
import six
|
||||
|
||||
import keystoneclient.v2_0.client as ksclient
|
||||
|
||||
|
||||
resource_controller_group = cfg.OptGroup('resource_controller')
|
||||
resource_controller_opts = [
|
||||
cfg.StrOpt('type',
|
||||
help=_("The resource controller plugin"))
|
||||
]
|
||||
|
||||
cfg.CONF.register_group(resource_controller_group)
|
||||
cfg.CONF.register_opts(resource_controller_opts,
|
||||
group=resource_controller_group)
|
||||
|
||||
|
||||
class ResourceController(RestController):
|
||||
|
||||
# TODO(Lakshmi): Lookup supported types from plugin registry
|
||||
local_resource_type = 'GFT:Local'
|
||||
glance_resource_type = 'OS::Glance:Image'
|
||||
|
||||
def __init__(self):
|
||||
super(ResourceController, self).__init__()
|
||||
|
||||
self.status = 200
|
||||
self.default_resource_type = "GFT::Local"
|
||||
|
||||
self._controller = self._load_controller('Local')
|
||||
@wsexpose(None, six.text_type, six.text_type, six.text_type)
|
||||
def options(self, param1, param2=None, param3=None):
|
||||
return Response(None, status_code=204)
|
||||
|
||||
def _load_controller(self, which_one):
|
||||
controller_type = cfg.CONF.resource_controller.type
|
||||
controller_type = controller_type if controller_type else 'Local'
|
||||
@wsexpose(Resource, six.text_type, six.text_type, six.text_type)
|
||||
def get_one(self, param1, param2=None, param3=None):
|
||||
"""Retrieve the resource based on the passed parameters
|
||||
Depending on the number of parameters passed, the meaning
|
||||
of the parameter is determined.
|
||||
|
||||
_controller = ResourceDAOFactory.create(controller_type)
|
||||
Use case #1: only param1 is set
|
||||
eg. /v1/resource/12345
|
||||
param1 is treated as resource id and resource type is defaulted
|
||||
to graffiti local resource
|
||||
|
||||
return _controller
|
||||
Use case #2: param1 and param2 are set
|
||||
eg /v1/resource/OS::Glance::Image/d24a33343
|
||||
param1 = resource type
|
||||
param2 = resource id
|
||||
|
||||
@wsexpose()
|
||||
def options(self):
|
||||
pass
|
||||
Use case #3: param1, param2 and param3 are set
|
||||
eg /v1/resource/OS::Glance::Image/d24a33343/e8dd383a838c83
|
||||
param1 = resource type
|
||||
param2 = resource id
|
||||
param3 = endpoint id
|
||||
|
||||
@wsexpose(Resource, six.text_type, six.text_type, six.text_type,
|
||||
six.text_type)
|
||||
def get_one(self, resource_id, resource_type=None, param1=None,
|
||||
param2=None):
|
||||
print "args:", resource_id, resource_type, param1, param2
|
||||
error_str = None
|
||||
if not resource_type:
|
||||
res = self._controller.get_resource(resource_id)
|
||||
"""
|
||||
print "args:", param1, param2, param3
|
||||
auth_token = pecan.request.headers.get('X-Auth-Token')
|
||||
endpoint_id = None
|
||||
|
||||
if not param2:
|
||||
#Use case #1
|
||||
resource_id = param1
|
||||
resource_type = self.default_resource_type
|
||||
else:
|
||||
#Use case #2
|
||||
resource_type = param1
|
||||
resource_id = param2
|
||||
|
||||
if param3:
|
||||
endpoint_id = param3
|
||||
|
||||
driver = driver_factory.get_driver(resource_type)
|
||||
if driver.resource:
|
||||
res = driver.resource.get_resource(
|
||||
resource_id,
|
||||
auth_token,
|
||||
endpoint_id
|
||||
)
|
||||
return res
|
||||
elif resource_type.lower() == \
|
||||
ResourceController.glance_resource_type.lower():
|
||||
auth_token = pecan.request.headers.get('X-Auth-Token')
|
||||
endpoint_id = param1
|
||||
image_id = resource_id
|
||||
glance_public_url = None
|
||||
keystone = ksclient.Client(
|
||||
auth_url=cfg.CONF.keystone.auth_url,
|
||||
username=cfg.CONF.keystone.username,
|
||||
password=cfg.CONF.keystone.password,
|
||||
tenant_name=cfg.CONF.keystone.tenant_name)
|
||||
for endpoint in keystone.endpoints.list():
|
||||
if endpoint.id == endpoint_id:
|
||||
glance_public_url = endpoint.publicurl
|
||||
|
||||
# TODO(Lakshmi): Load plugins with plugin framework
|
||||
if auth_token and glance_public_url:
|
||||
glance_plugin = GlanceImage(
|
||||
glance_public_url,
|
||||
keystone.auth_token
|
||||
)
|
||||
res = glance_plugin.get_resource(image_id)
|
||||
if res:
|
||||
return res
|
||||
else:
|
||||
error_str = "Resource not found"
|
||||
else:
|
||||
error_str = "auth_token and/or endpointid not found"
|
||||
else:
|
||||
error_str = "Driver not found for the resource type %s", \
|
||||
resource_type
|
||||
|
||||
res = Response(Resource(), status_code=404, error=error_str)
|
||||
return res
|
||||
|
||||
@wsexpose([Resource], six.text_type)
|
||||
def get_all(self, query_string=None):
|
||||
res_list = self._controller.find_resources(query_string)
|
||||
if res_list:
|
||||
return res_list.itervalues()
|
||||
@wsexpose([Resource], six.text_type, six.text_type)
|
||||
def get_all(self, resource_type=None, query_string=None):
|
||||
auth_token = pecan.request.headers.get('X-Auth-Token')
|
||||
|
||||
if not resource_type:
|
||||
resource_type = self.default_resource_type
|
||||
|
||||
driver = driver_factory.get_driver(resource_type)
|
||||
if driver.resource:
|
||||
res_list = driver.resource.find_resources(
|
||||
query_string,
|
||||
auth_token
|
||||
)
|
||||
if res_list:
|
||||
return res_list.itervalues()
|
||||
else:
|
||||
resource = Response(
|
||||
Resource(),
|
||||
status_code=404,
|
||||
error="Driver not found for the resource type")
|
||||
return resource
|
||||
|
||||
return []
|
||||
|
||||
@ -126,42 +120,43 @@ class ResourceController(RestController):
|
||||
"""Modify resource
|
||||
:resource param: graffiti.api.model.v1.resource.Resource
|
||||
"""
|
||||
resource_type = resource.type
|
||||
if not resource_type:
|
||||
resource_type = ResourceController.local_resource_type
|
||||
if resource_type.lower() == \
|
||||
ResourceController.local_resource_type.lower():
|
||||
self._controller.set_resource(
|
||||
resource_id,
|
||||
resource_definition=resource
|
||||
)
|
||||
elif resource_type.lower() == \
|
||||
ResourceController.glance_resource_type.lower():
|
||||
auth_token = pecan.request.headers.get('X-Auth-Token')
|
||||
endpoint_id = resource.provider.id
|
||||
glance_public_url = None
|
||||
keystone = ksclient.Client(
|
||||
auth_url=cfg.CONF.keystone.auth_url,
|
||||
username=cfg.CONF.keystone.username,
|
||||
password=cfg.CONF.keystone.password,
|
||||
tenant_name=cfg.CONF.keystone.tenant_name)
|
||||
for endpoint in keystone.endpoints.list():
|
||||
if endpoint.id == endpoint_id:
|
||||
glance_public_url = endpoint.publicurl
|
||||
|
||||
# TODO(Lakshmi): Load plugins with plugin framework
|
||||
if auth_token and glance_public_url:
|
||||
glance_plugin = GlanceImage(
|
||||
glance_public_url,
|
||||
keystone.auth_token
|
||||
)
|
||||
glance_plugin.update_resource(resource)
|
||||
auth_token = pecan.request.headers.get('X-Auth-Token')
|
||||
endpoint_id = resource.provider.id
|
||||
|
||||
if not resource.type:
|
||||
resource_type = self.default_resource_type
|
||||
else:
|
||||
resource_type = resource.type
|
||||
|
||||
driver = driver_factory.get_driver(resource_type)
|
||||
if driver.resource:
|
||||
driver.resource.update_resource(
|
||||
resource_id,
|
||||
resource,
|
||||
auth_token,
|
||||
endpoint_id=endpoint_id
|
||||
)
|
||||
else:
|
||||
resource = Response(
|
||||
Resource(),
|
||||
status_code=404,
|
||||
error="Driver not found for the resource type"
|
||||
)
|
||||
|
||||
return resource
|
||||
|
||||
@wsexpose(Resource, body=Resource)
|
||||
def post(self, resource):
|
||||
auth_token = pecan.request.headers.get('X-Auth-Token')
|
||||
|
||||
id = resource.id if hasattr(resource, 'id') else None
|
||||
self._controller.set_resource(id, resource_definition=resource)
|
||||
if not resource.type:
|
||||
resource_type = self.default_resource_type
|
||||
else:
|
||||
resource_type = resource.type
|
||||
|
||||
driver = driver_factory.get_driver(resource_type)
|
||||
if driver.resource:
|
||||
resource = driver.resource.create_resource(resource, auth_token)
|
||||
|
||||
return resource
|
||||
|
@ -24,7 +24,7 @@ class CorsHook(PecanHook):
|
||||
state.response.headers['Access-Control-Allow-Methods'] = \
|
||||
'GET, PUT, POST, DELETE, OPTIONS'
|
||||
state.response.headers['Access-Control-Allow-Headers'] = \
|
||||
'origin, authorization, accept, content-type'
|
||||
'origin, authorization, accept, content-type, X-Auth-Token'
|
||||
|
||||
if not state.response.headers['Content-Length']:
|
||||
state.response.headers['Content-Length'] = \
|
||||
|
@ -55,6 +55,7 @@ class LocalResourceDAO(ResourceDAOBase):
|
||||
id = self._generate_id()
|
||||
|
||||
self._resources[id] = resource_definition
|
||||
return id
|
||||
|
||||
def _generate_id(self):
|
||||
return_value = self._last_id
|
||||
|
@ -23,7 +23,8 @@ class ResourceDAOFactory(object):
|
||||
|
||||
@staticmethod
|
||||
def create(dao_type, **kwargs):
|
||||
if dao_type.lower() == 'local':
|
||||
if dao_type.lower() == 'memory':
|
||||
print "Directory persistence = memory"
|
||||
return LocalResourceDAO(**kwargs)
|
||||
|
||||
return None
|
||||
|
@ -1,95 +0,0 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 glanceclient import Client
|
||||
from graffiti.api.model.v1.capability import Capability
|
||||
from graffiti.api.model.v1.property import Property
|
||||
from graffiti.api.model.v1.resource import Resource
|
||||
|
||||
|
||||
class GlanceImage(object):
|
||||
|
||||
def __init__(self, glance_endpoint, auth_token):
|
||||
self.glance_endpoint = glance_endpoint
|
||||
self.auth_token = auth_token
|
||||
self.glance = Client('1', endpoint=glance_endpoint, token=auth_token)
|
||||
self.separator = "."
|
||||
|
||||
def get_resource(self, image_id):
|
||||
# glance_image_properties = {
|
||||
# "GLANCE.MySQL.Port": "3605",
|
||||
# "GLANCE.MySQL.Home": "/opt/mysql",
|
||||
# "GLANCE.Apache.Port": "8080",
|
||||
# "GLANCE.Apache.docroot": "/var/apache/static"
|
||||
# }
|
||||
image = self.glance.images.get(image_id)
|
||||
glance_image_properties = image.properties
|
||||
image_resource = Resource()
|
||||
image_capability = Capability()
|
||||
image_capabilities = []
|
||||
image_resource.capabilities = image_capabilities
|
||||
|
||||
image_resource.id = image_id
|
||||
image_resource.type = 'image'
|
||||
# image_resource.name = "ubuntu 12.04"
|
||||
image_resource.name = image.name
|
||||
|
||||
for key in glance_image_properties:
|
||||
# replace if check with pattern matching
|
||||
if key.count(self.separator) == 2:
|
||||
(namespace, capability_type, prop_name) = key.split(".")
|
||||
image_properties = []
|
||||
image_property = Property()
|
||||
image_property.name = prop_name
|
||||
image_property.value = glance_image_properties[key]
|
||||
|
||||
image_capability = None
|
||||
for capability in image_resource.capabilities:
|
||||
if capability.capability_type_namespace == namespace and \
|
||||
capability.capability_type == capability_type:
|
||||
image_capability = capability
|
||||
|
||||
if not image_capability:
|
||||
image_capability = Capability()
|
||||
image_resource.capabilities.append(image_capability)
|
||||
|
||||
image_capability.capability_type_namespace = namespace
|
||||
image_capability.capability_type = capability_type
|
||||
image_properties.append(image_property)
|
||||
|
||||
image_capability.properties = image_properties
|
||||
|
||||
return image_resource
|
||||
|
||||
def update_resource(self, resource):
|
||||
"""Update Glance Image
|
||||
:type param: graffiti.api.model.v1.resource.Resource
|
||||
"""
|
||||
|
||||
image_properties = {}
|
||||
for capability in resource.capabilities:
|
||||
properties = capability.properties
|
||||
capability_type = capability.capability_type
|
||||
capability_type_namespace = capability.capability_type_namespace
|
||||
for property in properties:
|
||||
prop_name = capability_type_namespace + \
|
||||
self.separator + \
|
||||
capability_type + \
|
||||
self.separator + \
|
||||
property.name
|
||||
image_properties[prop_name] = property.value
|
||||
|
||||
image = self.glance.images.get(resource.id)
|
||||
image.update(properties=image_properties, purge_props=False)
|
@ -24,9 +24,10 @@ import os
|
||||
import pecan
|
||||
import pecan.testing
|
||||
|
||||
from oslo.config import cfg
|
||||
#from oslo.config import cfg
|
||||
|
||||
from graffiti.api.tests import base
|
||||
from graffiti.common import driver_factory
|
||||
|
||||
|
||||
class TestCase(base.TestCase):
|
||||
@ -35,8 +36,9 @@ class TestCase(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
self.app = self._make_app()
|
||||
cfg.CONF.set_override(name='type', override='Local',
|
||||
group='resource_controller')
|
||||
#cfg.CONF.set_override(name='type', override='Local',
|
||||
# group='resource_controller')
|
||||
driver_factory.DriverFactory()
|
||||
|
||||
def _make_app(self):
|
||||
root_dir = self.path_get()
|
||||
|
@ -53,7 +53,7 @@
|
||||
"capability_type_namespace": "TEST:RESOURCE:2014-1",
|
||||
"criterion": "StandardCriterion2"
|
||||
}],
|
||||
"type": "TEST:RESOURCE:2014-1:StandardResource"
|
||||
"type": "GFT::Local"
|
||||
}]
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ class TestControllerV1(base.TestCase):
|
||||
self.assertIn(hasattr(v1, 'resource'), [True])
|
||||
|
||||
def test_v1_resource_controller_factory__local(self):
|
||||
rc = ResourceDAOFactory.create('local')
|
||||
rc = ResourceDAOFactory.create('memory')
|
||||
self.assertEquals(rc.get_type(), 'LocalResourceDAO')
|
||||
|
||||
def test_v1_resource_controller_factory__unknown(self):
|
||||
|
119
graffiti/common/driver_factory.py
Normal file
119
graffiti/common/driver_factory.py
Normal file
@ -0,0 +1,119 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 graffiti.common import exception
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from stevedore import dispatch
|
||||
|
||||
driver_opts = [
|
||||
cfg.ListOpt('enabled_drivers',
|
||||
default=['local', 'glance'],
|
||||
help='List of drivers to enable. Missing drivers, or '
|
||||
'drivers which can not be loaded will be '
|
||||
'treated as a fatal exception.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(driver_opts)
|
||||
|
||||
|
||||
def get_driver(resource_type):
|
||||
"""Simple method to get a ref to an instance of a driver by the
|
||||
supported resource type.
|
||||
|
||||
Driver loading is handled by the DriverFactory class. This method
|
||||
conveniently wraps that class and returns the actual driver object.
|
||||
|
||||
:param resource_type: the resource type supported by a driver
|
||||
:returns: An instance of a class which implements
|
||||
graffiti.drivers.base.BaseResourceDriver
|
||||
:raises: DriverNotFound if the requested driver_name could not be
|
||||
found in the "graffiti.drivers" namespace.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
factory = DriverFactory()
|
||||
print "resource types", factory.resource_types
|
||||
print "resource type", resource_type
|
||||
if resource_type in factory.resource_types.keys():
|
||||
driver_name = factory.resource_types[resource_type]
|
||||
return factory[driver_name].obj
|
||||
else:
|
||||
raise exception.DriverNotFoundForResourceType(
|
||||
resource_type=resource_type
|
||||
)
|
||||
|
||||
except KeyError:
|
||||
raise exception.DriverNotFound(driver_name=driver_name)
|
||||
|
||||
|
||||
class DriverFactory(object):
|
||||
"""Discover, load and manage the drivers available."""
|
||||
|
||||
_driver_manager = None
|
||||
_resource_types = {}
|
||||
|
||||
def __init__(self):
|
||||
if not DriverFactory._driver_manager:
|
||||
DriverFactory._init_driver_manager()
|
||||
print "Loaded drivers:", self.names
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self._driver_manager[name]
|
||||
|
||||
@classmethod
|
||||
def _init_driver_manager(self):
|
||||
if self._driver_manager:
|
||||
return
|
||||
|
||||
def _catch_driver_not_found(mgr, ep, exc):
|
||||
if (isinstance(exc, exception.DriverLoadError) and
|
||||
ep.name not in CONF.enabled_drivers):
|
||||
return
|
||||
raise exc
|
||||
|
||||
def _check_func(ext):
|
||||
return ext.name in CONF.enabled_drivers
|
||||
|
||||
self._driver_manager = dispatch.NameDispatchExtensionManager(
|
||||
'graffiti.drivers',
|
||||
_check_func,
|
||||
invoke_on_load=True,
|
||||
on_load_failure_callback=_catch_driver_not_found
|
||||
)
|
||||
|
||||
#Get supported resource types
|
||||
for driver_name in self._driver_manager.names():
|
||||
driver = self._driver_manager[driver_name].obj
|
||||
driver_resource_types = driver.get_resource_types()
|
||||
for type in driver_resource_types:
|
||||
self._resource_types[type] = driver_name
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
"""The list of driver names available."""
|
||||
return self._driver_manager.names()
|
||||
|
||||
@property
|
||||
def resource_types(self):
|
||||
"""Returns all resource types supported by all the drivers
|
||||
:returns dictionary with resource type as key and driver name
|
||||
as its value
|
||||
"""
|
||||
return self._resource_types
|
@ -13,34 +13,55 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from graffiti.openstack.common.gettextutils import _
|
||||
|
||||
|
||||
class GraffitiException(Exception):
|
||||
"""Base Exception for the project
|
||||
|
||||
To correctly use this class, inherit from it and define
|
||||
the 'message' property.
|
||||
That message will get printf'd
|
||||
with the keyword arguments provided to the constructor.
|
||||
"""
|
||||
|
||||
message = "An unknown exception occurred"
|
||||
message = _("An unknown exception occurred")
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
def __init__(self):
|
||||
super(GraffitiException, self).__init__(self.message)
|
||||
def __init__(self, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
|
||||
try:
|
||||
message = self.message % kwargs
|
||||
except KeyError:
|
||||
#TODO(Any): print to log
|
||||
pass
|
||||
|
||||
super(GraffitiException, self).__init__(message)
|
||||
|
||||
|
||||
class NotFound(GraffitiException):
|
||||
message = "Object not found"
|
||||
|
||||
def __init__(self, message=None):
|
||||
if message:
|
||||
self.message = message
|
||||
message = _("Object not found")
|
||||
|
||||
|
||||
class DuplicateEntry(GraffitiException):
|
||||
message = "Database object already exists"
|
||||
message = _("Database object already exists")
|
||||
|
||||
def __init__(self, message=None):
|
||||
if message:
|
||||
self.message = message
|
||||
|
||||
class DriverNotFound(NotFound):
|
||||
message = _("Failed to load driver %(driver_name)s.")
|
||||
|
||||
|
||||
class DriverLoadError(GraffitiException):
|
||||
message = _("Driver %(driver)s could not be loaded. Reason: %(reason)s.")
|
||||
|
||||
|
||||
class MethodNotSupported(GraffitiException):
|
||||
message = _("Method %(method)s is not supported by this driver")
|
||||
|
||||
|
||||
class DriverNotFoundForResourceType(NotFound):
|
||||
message = _("Cannot find a registered driver for the resource "
|
||||
"type %(resource_type)s")
|
||||
|
103
graffiti/drivers/base.py
Normal file
103
graffiti/drivers/base.py
Normal file
@ -0,0 +1,103 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Abstract base class for graffiti resource drivers.
|
||||
"""
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseDriver(object):
|
||||
"""Base class for all drivers.
|
||||
|
||||
Defines resource and definitions interface.
|
||||
Any loadable driver must implement the interfaces it supports
|
||||
|
||||
"""
|
||||
|
||||
resource = None
|
||||
|
||||
#TBD in future
|
||||
#definitions = None
|
||||
|
||||
@abc.abstractmethod
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_resource_types(self):
|
||||
"""Returns the resource types supported by the implementing driver
|
||||
:returns [str] List of resource type strings
|
||||
"""
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ResourceInterface(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_resource(self, resource_id, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Retrieve the resource detail
|
||||
:param resource_id: unique resource identifier
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
:returns resource detail
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def find_resources(self, query_string, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Find resources matching the query
|
||||
:param query_string: query expression
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
:returns list of resources
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_resource(self, resource, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Create resource
|
||||
:param resource: resource detail
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_resource(self, resource_id, resource, auth_token,
|
||||
endpoint_id=None, **kwargs):
|
||||
"""Update resource
|
||||
:param resource_id: unique resource identifier
|
||||
:param resource: resource detail
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_resource(self, resource_id, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Delete resource
|
||||
:param resource_id: unique resource identifier
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
"""
|
32
graffiti/drivers/glance.py
Normal file
32
graffiti/drivers/glance.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 graffiti.drivers import base
|
||||
from graffiti.drivers.modules import glance
|
||||
|
||||
|
||||
class GlanceResourceDriver(base.BaseDriver):
|
||||
"""This driver implements glance resource driver interface
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.resource = glance.GlanceResourceDriver()
|
||||
self.resource_types = ["OS::Glance::Image"]
|
||||
|
||||
def get_resource_types(self):
|
||||
"""Returns the resource types supported by the implementing driver
|
||||
:returns [str] List of resource type strings
|
||||
"""
|
||||
return self.resource_types
|
33
graffiti/drivers/local.py
Normal file
33
graffiti/drivers/local.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 graffiti.drivers import base
|
||||
from graffiti.drivers.modules import local
|
||||
|
||||
|
||||
class LocalResourceDriver(base.BaseDriver):
|
||||
"""This driver implements resource interface locally by graffiti
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.resource = local.LocalResourceDriver()
|
||||
self.resource_types = ["GFT::Local"]
|
||||
|
||||
def get_resource_types(self):
|
||||
"""Returns the resource types supported by the implementing
|
||||
driver
|
||||
:returns [str] List of resource type strings
|
||||
"""
|
||||
return self.resource_types
|
0
graffiti/drivers/modules/__init__.py
Normal file
0
graffiti/drivers/modules/__init__.py
Normal file
167
graffiti/drivers/modules/glance.py
Normal file
167
graffiti/drivers/modules/glance.py
Normal file
@ -0,0 +1,167 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 glanceclient import Client
|
||||
from graffiti.api.model.v1.capability import Capability
|
||||
from graffiti.api.model.v1.property import Property
|
||||
from graffiti.api.model.v1.resource import Resource
|
||||
from graffiti.common import exception
|
||||
from graffiti.drivers import base
|
||||
|
||||
import keystoneclient.v2_0.client as ksclient
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
|
||||
class GlanceResourceDriver(base.ResourceInterface):
|
||||
|
||||
def __init__(self):
|
||||
super(GlanceResourceDriver, self).__init__()
|
||||
self.separator = "."
|
||||
|
||||
def get_resource(self, resource_id, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Retrieve the resource detail
|
||||
:param resource_id: unique resource identifier
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
:returns resource detail
|
||||
"""
|
||||
|
||||
# glance_image_properties = {
|
||||
# "GLANCE.MySQL.Port": "3605",
|
||||
# "GLANCE.MySQL.Home": "/opt/mysql",
|
||||
# "GLANCE.Apache.Port": "8080",
|
||||
# "GLANCE.Apache.docroot": "/var/apache/static"
|
||||
# }
|
||||
|
||||
glance_client = self.__get_glance_client(endpoint_id, auth_token)
|
||||
|
||||
image = glance_client.images.get(resource_id)
|
||||
glance_image_properties = image.properties
|
||||
image_resource = Resource()
|
||||
image_capabilities = []
|
||||
image_resource.capabilities = image_capabilities
|
||||
|
||||
image_resource.id = resource_id
|
||||
image_resource.type = 'image'
|
||||
image_resource.name = image.name
|
||||
|
||||
for key in glance_image_properties:
|
||||
# replace if check with pattern matching
|
||||
if key.count(self.separator) == 2:
|
||||
(namespace, capability_type, prop_name) = key.split(".")
|
||||
image_properties = []
|
||||
image_property = Property()
|
||||
image_property.name = prop_name
|
||||
image_property.value = glance_image_properties[key]
|
||||
|
||||
image_capability = None
|
||||
for capability in image_resource.capabilities:
|
||||
if capability.capability_type_namespace == namespace and \
|
||||
capability.capability_type == capability_type:
|
||||
image_capability = capability
|
||||
|
||||
if not image_capability:
|
||||
image_capability = Capability()
|
||||
image_resource.capabilities.append(image_capability)
|
||||
|
||||
image_capability.capability_type_namespace = namespace
|
||||
image_capability.capability_type = capability_type
|
||||
image_properties.append(image_property)
|
||||
|
||||
image_capability.properties = image_properties
|
||||
|
||||
return image_resource
|
||||
|
||||
def update_resource(self, resource_id, resource, auth_token,
|
||||
endpoint_id=None, **kwargs):
|
||||
"""Update resource
|
||||
:param resource_id: unique resource identifier
|
||||
:param resource: resource detail
|
||||
:type param: graffiti.api.model.v1.resource.Resource
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
"""
|
||||
|
||||
glance_client = self.__get_glance_client(endpoint_id, auth_token)
|
||||
|
||||
image_properties = {}
|
||||
for capability in resource.capabilities:
|
||||
properties = capability.properties
|
||||
capability_type = capability.capability_type
|
||||
capability_type_namespace = capability.capability_type_namespace
|
||||
for property in properties:
|
||||
prop_name = capability_type_namespace + \
|
||||
self.separator + \
|
||||
capability_type + \
|
||||
self.separator + \
|
||||
property.name
|
||||
image_properties[prop_name] = property.value
|
||||
|
||||
image = glance_client.images.get(resource.id)
|
||||
image.update(properties=image_properties, purge_props=False)
|
||||
|
||||
def find_resources(self, query_string, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Find resources matching the query
|
||||
:param query_string: query expression
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
:returns list of resources
|
||||
"""
|
||||
#TODO(Lakshmi): Implement this method
|
||||
pass
|
||||
|
||||
def create_resource(self, resource, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Create resource
|
||||
:param resource: resource detail
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
"""
|
||||
raise exception.MethodNotSupported(method="create_resource")
|
||||
|
||||
def delete_resource(self, resource_id, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Delete resource
|
||||
:param resource_id: unique resource identifier
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
"""
|
||||
raise exception.MethodNotSupported(method="delete_resource")
|
||||
|
||||
def __get_glance_client(self, endpoint_id, auth_token):
|
||||
keystone = ksclient.Client(
|
||||
auth_url=cfg.CONF.keystone.auth_url,
|
||||
username=cfg.CONF.keystone.username,
|
||||
password=cfg.CONF.keystone.password,
|
||||
tenant_name=cfg.CONF.keystone.tenant_name
|
||||
)
|
||||
self.__endpoint_list = keystone.endpoints.list()
|
||||
for endpoint in self.__endpoint_list:
|
||||
if endpoint.id == endpoint_id:
|
||||
glance_public_url = endpoint.publicurl
|
||||
glance_client = Client(
|
||||
'1',
|
||||
endpoint=glance_public_url,
|
||||
token=auth_token
|
||||
)
|
||||
return glance_client
|
97
graffiti/drivers/modules/local.py
Normal file
97
graffiti/drivers/modules/local.py
Normal file
@ -0,0 +1,97 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 graffiti.api.model.v1.resource_dao_factory import \
|
||||
ResourceDAOFactory
|
||||
|
||||
from graffiti.drivers import base
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
|
||||
class LocalResourceDriver(base.ResourceInterface):
|
||||
|
||||
def __init__(self):
|
||||
super(LocalResourceDriver, self).__init__()
|
||||
persistence_type = cfg.CONF.DEFAULT.persistence_type
|
||||
self._resource_dao = ResourceDAOFactory.create(persistence_type)
|
||||
|
||||
def get_resource(self, resource_id, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Retrieve the resource detail
|
||||
:param resource_id: unique resource identifier
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
:returns resource detail
|
||||
"""
|
||||
|
||||
res = self._resource_dao.get_resource(resource_id)
|
||||
return res
|
||||
|
||||
def find_resources(self, query_string, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Find resources matching the query
|
||||
:param query_string: query expression
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
:returns list of resources
|
||||
"""
|
||||
res_list = self._resource_dao.find_resources(query_string)
|
||||
if res_list:
|
||||
return res_list
|
||||
|
||||
return []
|
||||
|
||||
def create_resource(self, resource, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Create resource
|
||||
:param resource: resource detail
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
"""
|
||||
|
||||
id = resource.id if hasattr(resource, 'id') else None
|
||||
self._resource_dao.set_resource(id, resource_definition=resource)
|
||||
|
||||
return resource
|
||||
|
||||
def update_resource(self, resource_id, resource, auth_token,
|
||||
endpoint_id=None, **kwargs):
|
||||
"""Update resource
|
||||
:param resource_id: unique resource identifier
|
||||
:param resource: resource detail
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
"""
|
||||
self._resource_dao.set_resource(
|
||||
resource_id,
|
||||
resource_definition=resource
|
||||
)
|
||||
return resource
|
||||
|
||||
def delete_resource(self, resource_id, auth_token, endpoint_id=None,
|
||||
**kwargs):
|
||||
"""Delete resource
|
||||
:param resource_id: unique resource identifier
|
||||
:param auth_token: keystone auth_token of request user
|
||||
:param endpoint_id: id for locating the cloud resource provider
|
||||
:param **kwargs: Include additional info required by the driver,
|
||||
"""
|
||||
#TODO(Lakshmi): Implement delete
|
||||
pass
|
0
graffiti/tests/drivers/__init__.py
Normal file
0
graffiti/tests/drivers/__init__.py
Normal file
32
graffiti/tests/drivers/test_db_resource_driver.py
Normal file
32
graffiti/tests/drivers/test_db_resource_driver.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 graffiti.api.tests import pecan_base
|
||||
from graffiti.common import driver_factory
|
||||
from graffiti.drivers import base as driver_base
|
||||
|
||||
|
||||
class TestLocalResourceDriver(pecan_base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestLocalResourceDriver, self).setUp()
|
||||
self.driver = driver_factory.get_driver("GFT::Local")
|
||||
|
||||
def test_driver_interfaces(self):
|
||||
self.assertIsInstance(
|
||||
self.driver.resource,
|
||||
driver_base.ResourceInterface
|
||||
)
|
32
graffiti/tests/drivers/test_glance_resource_driver.py
Normal file
32
graffiti/tests/drivers/test_glance_resource_driver.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 graffiti.api.tests import pecan_base
|
||||
from graffiti.common import driver_factory
|
||||
from graffiti.drivers import base as driver_base
|
||||
|
||||
|
||||
class TestGlanceResourceDriver(pecan_base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestGlanceResourceDriver, self).setUp()
|
||||
self.driver = driver_factory.get_driver("OS::Glance::Image")
|
||||
|
||||
def test_driver_interfaces(self):
|
||||
self.assertIsInstance(
|
||||
self.driver.resource,
|
||||
driver_base.ResourceInterface
|
||||
)
|
@ -9,3 +9,4 @@ iso8601>=0.1.8
|
||||
requests>=1.1
|
||||
six>=1.5.2
|
||||
SQLAlchemy>=0.8,<=0.8.99
|
||||
stevedore>=0.14
|
||||
|
Loading…
x
Reference in New Issue
Block a user