Cinder driver initial add

Basic support for geting and updating cinder metadata
Outstanding issues to be resolved on metadata handling
See mailing list archive

http://lists.openstack.org/pipermail/openstack-dev/2014-May/034657.html

Change-Id: I866c1e2d90f9b7a6b81f9945b6663939fe1fd10d
This commit is contained in:
Travis Tripp 2014-05-07 22:36:17 -06:00
parent b6e7b5dd80
commit 38fd7915fd
5 changed files with 287 additions and 1 deletions

View File

@ -22,7 +22,7 @@ from stevedore import dispatch
driver_opts = [
cfg.ListOpt('enabled_drivers',
default=['local', 'glance'],
default=['local', 'glance', 'cinder'],
help='List of drivers to enable. Missing drivers, or '
'drivers which can not be loaded will be '
'treated as a fatal exception.'),

View 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 cinder
class CinderResourceDriver(base.BaseDriver):
"""This driver implements cinder resource driver interface
"""
def __init__(self):
self.resource = cinder.CinderResourceDriver()
self.resource_types = ["OS::Cinder::Volume"]
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

View File

@ -0,0 +1,252 @@
# 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 cinderclient 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
from graffiti.openstack.common import log as logging
import keystoneclient.v2_0.client as ksclient
from oslo.config import cfg
LOG = logging.getLogger(__name__)
class CinderResourceDriver(base.ResourceInterface):
def __init__(self):
super(CinderResourceDriver, self).__init__()
self.separator = "."
self.service_type = 'volume'
self.endpoint_type = 'publicURL'
self.default_namespace_postfix = "::Default"
self.unknown_properties_type = "AdditionalProperties"
def get_resource(self, resource_type, resource_id, auth_token,
endpoint_id=None, **kwargs):
"""Retrieve the resource detail
:param resource_type: resource_type set for this call
: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
"""
cinder_client = self.__get_cinder_client(endpoint_id, auth_token)
volume = cinder_client.volumes.get(resource_id)
cinder_resource = self.transform_to_resource(resource_type, volume)
return cinder_resource
def update_resource(self, resource_type, resource_id, resource, auth_token,
endpoint_id=None, **kwargs):
"""Update resource
:param resource_type: resource_type set for this call
: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,
"""
cinder_client = self.__get_cinder_client(endpoint_id, auth_token)
volume_properties = {}
for capability in resource.capabilities:
if capability.capability_type_namespace \
== resource_type + self.default_namespace_postfix \
and capability.capability_type \
== self.unknown_properties_type:
# For unknown properties, just directly set property name.
for property_name, property_value in \
capability.properties.iteritems():
volume_properties[property_name] = property_value
else:
properties = capability.properties
capability_type = self.replace_colon_from_name(
capability.capability_type
)
capability_type_namespace = self.replace_colon_from_name(
capability.capability_type_namespace
)
for property_name, property_value in properties.iteritems():
prop_name = capability_type_namespace + \
self.separator + \
capability_type + \
self.separator + \
self.replace_colon_from_name(property_name)
volume_properties[prop_name] = property_value
volume = cinder_client.volumes.get(resource_id)
try:
volume.set_metadata(volume, volume_properties)
except AttributeError:
#Temporary until bug fixed
LOG.debug('Hit error: https://bugs.launchpad.net/bugs/1315175')
pass
def find_resources(self, query_string, auth_token,
endpoint_id=None, **kwargs):
"""Find resources matching the query
:param query_string: query expression. Include resource type(s)
: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
"""
resource_list = dict()
if not query_string:
cinder_client = self.__get_cinder_client(endpoint_id, auth_token)
volumes = cinder_client.volumes.list()
for volume in list(volumes):
resource = self.transform_to_resource(
self.default_resource_type,
volume
)
resource_list[resource.id] = resource
return resource_list
def create_resource(self, resource_type, resource, auth_token,
endpoint_id=None, **kwargs):
"""Create resource
:param resource_type: resource_type set for this call
: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_type, resource_id, auth_token,
endpoint_id=None, **kwargs):
"""Delete resource
:param resource_type: resource_type set for this call
: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_cinder_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
)
cinder_public_url = None
if endpoint_id:
for entry in keystone.service_catalog.catalog.get(
'serviceCatalog'):
for endpoint in entry['endpoints']:
if endpoint['id'] == endpoint_id:
cinder_public_url = endpoint['publicURL']
break
if cinder_public_url:
break
else:
cinder_public_url = keystone.service_catalog.url_for(
service_type=self.service_type,
endpoint_type=self.endpoint_type
)
cinder_client = client.Client(
'1',
cfg.CONF.keystone.username,
cfg.CONF.keystone.password,
cfg.CONF.keystone.tenant_name,
cfg.CONF.keystone.auth_url
)
return cinder_client
def transform_to_resource(self, resource_type, volume):
cinder_volume_properties = {}
volume_metadata = {}
volume_image_metadata = {}
try:
volume_image_metadata = volume.volume_image_metadata
except AttributeError:
pass
try:
volume_metadata = volume.metadata
except AttributeError:
pass
cinder_volume_properties = dict(volume_metadata,
**volume_image_metadata)
result = Resource()
result_capabilities = []
result.capabilities = result_capabilities
result.id = volume.id
result.type = resource_type
result.name = volume.display_name
for key in cinder_volume_properties:
if key.count(self.separator) == 2:
(namespace, capability_type, prop_name) = key.split(".")
namespace = self.replace_hash_from_name(namespace)
capability_type = self.replace_hash_from_name(capability_type)
prop_name = self.replace_hash_from_name(prop_name)
else:
namespace = resource_type + self.default_namespace_postfix
capability_type = self.unknown_properties_type
prop_name = key
result_property = Property()
result_property.name = prop_name
result_property.value = cinder_volume_properties[key]
result_capability = None
for capability in result.capabilities:
if capability.capability_type_namespace == namespace and \
capability.capability_type == capability_type:
result_capability = capability
if not result_capability:
result_capability = Capability()
result_capability.properties = {}
result.capabilities.append(result_capability)
result_capability.capability_type_namespace = namespace
result_capability.capability_type = capability_type
result_capability.properties[result_property.name] = \
result_property.value
return result
def replace_colon_from_name(self, name):
if name:
return name.replace(':', '#')
return
def replace_hash_from_name(self, name):
if name:
return name.replace('#', ':')
return

View File

@ -4,6 +4,7 @@ pecan>=0.4.4
WSME>=0.6
oslo.config>=1.2.0
python-glanceclient
python-cinderclient
alembic>=0.4.1
iso8601>=0.1.8
requests>=1.1

View File

@ -27,6 +27,7 @@ packages =
graffiti.drivers =
local = graffiti.drivers.local:LocalResourceDriver
glance = graffiti.drivers.glance:GlanceResourceDriver
cinder = graffiti.drivers.cinder:CinderResourceDriver
[build_sphinx]