From 99287f3f0019d8879d3d36e7223d00c5fe9869c2 Mon Sep 17 00:00:00 2001 From: Lakshmi N Sampath Date: Wed, 16 Apr 2014 18:19:49 -0700 Subject: [PATCH] dictionary controllers for namespace and capability_type Change-Id: I931425d6fe6f95d0d616d5e3f6efe852b2863b53 --- config.py | 77 ++++++++++++ etc/graffiti.conf.sample | 7 ++ graffiti/api/app.py | 16 +++ .../api/controllers/v1/capability_type.py | 118 +++++++++++++++--- .../api/controllers/v1/captype_controller.py | 39 ++++++ .../v1/captype_controller_factory.py | 42 +++++++ .../controllers/v1/captype_db_controller.py | 39 ++++++ .../controllers/v1/captype_file_controller.py | 84 +++++++++++++ .../controllers/v1/captype_mem_controller.py | 52 ++++++++ graffiti/api/controllers/v1/namespace.py | 53 +++++--- graffiti/api/controllers/v1/ns_controller.py | 39 ++++++ .../controllers/v1/ns_controller_factory.py | 57 +++++++++ .../api/controllers/v1/ns_db_controller.py | 43 +++++++ .../api/controllers/v1/ns_file_controller.py | 80 ++++++++++++ .../api/controllers/v1/ns_mem_controller.py | 49 ++++++++ graffiti/api/controllers/v1/resource.py | 100 ++++++++++++--- graffiti/api/hooks.py | 2 +- graffiti/api/model/v1/capability_type.py | 6 + graffiti/api/model/v1/derived_type.py | 27 ++++ graffiti/api/model/v1/namespace.py | 3 +- graffiti/api/model/v1/property_item_type.py | 27 ++++ graffiti/api/model/v1/property_type.py | 50 ++++++++ ...resource_controller.py => resource_dao.py} | 10 +- ...ler_factory.py => resource_dao_factory.py} | 12 +- graffiti/api/plugins/__init__.py | 1 + graffiti/api/plugins/glance_image.py | 95 ++++++++++++++ graffiti/api/tests/base.py | 29 +++++ graffiti/api/tests/test_controller_v1.py | 48 ++++++- requirements.txt | 1 + tox.ini | 2 +- 30 files changed, 1137 insertions(+), 71 deletions(-) create mode 100644 etc/graffiti.conf.sample create mode 100644 graffiti/api/controllers/v1/captype_controller.py create mode 100644 graffiti/api/controllers/v1/captype_controller_factory.py create mode 100644 graffiti/api/controllers/v1/captype_db_controller.py create mode 100644 graffiti/api/controllers/v1/captype_file_controller.py create mode 100644 graffiti/api/controllers/v1/captype_mem_controller.py create mode 100755 graffiti/api/controllers/v1/ns_controller.py create mode 100755 graffiti/api/controllers/v1/ns_controller_factory.py create mode 100755 graffiti/api/controllers/v1/ns_db_controller.py create mode 100755 graffiti/api/controllers/v1/ns_file_controller.py create mode 100755 graffiti/api/controllers/v1/ns_mem_controller.py create mode 100644 graffiti/api/model/v1/derived_type.py create mode 100755 graffiti/api/model/v1/property_item_type.py create mode 100644 graffiti/api/model/v1/property_type.py rename graffiti/api/model/v1/{resource_controller.py => resource_dao.py} (84%) rename graffiti/api/model/v1/{resource_controller_factory.py => resource_dao_factory.py} (67%) create mode 100644 graffiti/api/plugins/__init__.py create mode 100644 graffiti/api/plugins/glance_image.py diff --git a/config.py b/config.py index a9e6343..d117a39 100644 --- a/config.py +++ b/config.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from oslo.config import cfg + # Server Specific Configurations server = { 'port': '21075', @@ -59,9 +61,84 @@ wsme = { 'debug': True } + # Custom Configurations must be in Python dictionary format:: # # foo = {'bar':'baz'} # # All configurations are accessible at:: # pecan.conf + +# oslo config +keystone_group = cfg.OptGroup('keystone') +keystone_opts = [ + cfg.StrOpt('auth_url', + default='http://192.168.40.10:5000/v2.0', + help='keystone authorization url'), + cfg.StrOpt('username', + default='admin', + help='keystone username'), + cfg.StrOpt('password', + default='secretword', + help='keystone password'), + cfg.StrOpt('tenant_name', + default='admin', + help='keystone tenant name') + +] +cfg.CONF.register_group(keystone_group) +cfg.CONF.register_opts(keystone_opts, group=keystone_group) + +# DEFAULT group +default_controller_group = cfg.OptGroup('DEFAULT') +default_controller_opts = [ + cfg.StrOpt( + 'persistence_type', + default="memory", + help=("persistence options. " + "values = 'memory' or 'file' or 'db")) +] + +cfg.CONF.register_group(default_controller_group) +cfg.CONF.register_opts(default_controller_opts, + group=default_controller_group) + +# FILE_PERSISTENCE group +file_controller_group = cfg.OptGroup('FILE_PERSISTENCE') +file_controller_opts = [ + cfg.StrOpt( + 'dictionary_folder', + default="/tmp/graffiti-dictionary/", + help=("Absolute path of the file for persisting dictionary") + ) +] + +cfg.CONF.register_group(file_controller_group) +cfg.CONF.register_opts(file_controller_opts, + group=file_controller_group) + + +# Used for remote debugging, like pychcharms or pydev +# To enable remote debugging in pycharms, requires that you put the +# pycharm-debug.egg in the python path. E.g. +# +# Include the pycharm-debug.egg archive. +# e.g. /home//pycharm-3.1.1/pycharm-debug.egg +# You can do it in a number of ways, for example: +# Add the archive to PYTHONPATH.e,g, +# export PYTHONPATH+=.:/home//pycharm-3.1.1/pycharm-debug.egg +# Append the archive to sys.path. e.g. +# import sys +# sys.path.append('/home//pycharm-3.1.1/pycharm-debug.egg') +# Just copy the pydev from the archive to the directory where your remote +# script resides. +# +# You will need to setup a debug configuration in your pycharms and start the +# debugger BEFORE starting pecan +# This is because the following code connects from here to your pycharms +# (or your pydev) +pydevd = { + 'enabled': True, + 'port': 22075, + 'bindhost': 'localhost' +} diff --git a/etc/graffiti.conf.sample b/etc/graffiti.conf.sample new file mode 100644 index 0000000..35f6ebb --- /dev/null +++ b/etc/graffiti.conf.sample @@ -0,0 +1,7 @@ +[DEFAULT] + +#Allowed values= memory, file, db. Default value is memory +persistence_type=memory + +[FILE_PERSISTENCE] +dictionary_folder=/tmp/graffiti-dictionary/ diff --git a/graffiti/api/app.py b/graffiti/api/app.py index 17bb3f1..8fba00a 100644 --- a/graffiti/api/app.py +++ b/graffiti/api/app.py @@ -26,6 +26,22 @@ CONF = cfg.CONF def setup_app(config): + if hasattr(config, 'pydevd') and config.pydevd.enabled: + try: + print( + 'Remote debug set to true(config.pydevd). ' + 'Attempting connection' + ) + import pydevd + pydevd.settrace( + config.pydevd.bindhost, + port=config.pydevd.port, + stdoutToServer=True, + stderrToServer=True, + suspend=False) + except Exception as e: + print "Debug Connection Error:", e + model.init_model() app_conf = dict(config.app) diff --git a/graffiti/api/controllers/v1/capability_type.py b/graffiti/api/controllers/v1/capability_type.py index 75dae40..8a3439b 100644 --- a/graffiti/api/controllers/v1/capability_type.py +++ b/graffiti/api/controllers/v1/capability_type.py @@ -15,42 +15,122 @@ from pecan.rest import RestController +from wsme.api import Response from wsmeext.pecan import wsexpose +from graffiti.api.controllers.v1.captype_controller_factory \ + import CapTypeControllerFactory from graffiti.api.model.v1.capability_type import CapabilityType +from ns_controller_factory import NSControllerFactory +from oslo.config import cfg + import six -capability_types = [] - class CapabilityTypeController(RestController): def __init__(self): super(RestController, self).__init__() self.status = 200 + self._cap_controller = None + self._ns_controller = None + self._load_controller() + + def _load_controller(self): + controller_type = cfg.CONF.DEFAULT.persistence_type + self._cap_controller = CapTypeControllerFactory.create(controller_type) + self._ns_controller = NSControllerFactory.get() @wsexpose def options(): pass - @wsexpose(CapabilityType, six.text_type) - def get_one(self, name): - global capability_types + @wsexpose(CapabilityType, six.text_type, six.text_type) + def get_one(self, name, namespace): + captype = self._cap_controller.get_capability_type(name, namespace) + return captype - for capability_type in capability_types: - if capability_type.name.lower() == name.lower(): - return capability_type - - res = CapabilityType(CapabilityType(), status_code=404, - error="CapabilityType Not Found") - return res - - @wsexpose([CapabilityType]) - def get_all(self): - global capability_types - return capability_types + @wsexpose([CapabilityType], six.text_type) + def get_all(self, query_string=None): + captype_list = self._cap_controller.find_capability_types(query_string) + return captype_list @wsexpose(CapabilityType, body=CapabilityType) def post(self, capability_type): - global capability_types - capability_types.append(capability_type) + """Create Capability Type + @type capability_type: + graffiti.api.model.v1.capability_type.CapabilityType + @param capability_type: Capability type + """ + + # Check if namespace exists + namespace_found = self.__check_existing_namespace( + capability_type.namespace + ) + + # Check if derived capability type exists + derived_checked = self.__check_derived_capability( + capability_type.derived_from + ) + + if namespace_found and derived_checked: + self._cap_controller.set_capability_type( + capability_type + ) + return capability_type + else: + res = Response( + CapabilityType(), + status_code=404, + error="Provided namespace %s doesnt exist" % + capability_type.namespace) + return res + + @wsexpose(CapabilityType, six.text_type, six.text_type, + body=CapabilityType) + def put(self, name, namespace, capability_type): + + # Check if namespace exists + namespace_found = self.__check_existing_namespace( + capability_type.namespace + ) + + # Check if derived capability type exists + derived_checked = self.__check_derived_capability( + capability_type.derived_from + ) + + if namespace_found and derived_checked: + self._cap_controller.put_capability_type( + name, namespace, capability_type + ) + return capability_type + else: + res = Response( + CapabilityType(), + status_code=404, + error="Provided namespace %s doesnt exist" % + capability_type.namespace) + return res + + @wsexpose(CapabilityType, six.text_type, six.text_type) + def delete(self, name, namespace): + captype = self._cap_controller.delete_capability_type( + name, + namespace + ) + return captype + + def __check_derived_capability(self, derived_from): + derived_checked = True + if derived_from: + derived_checked = False + derived_cap_found = self._cap_controller.get_capability_type( + derived_from.name, derived_from.namespace) + if derived_cap_found: + derived_checked = True + + return derived_checked + + def __check_existing_namespace(self, namespace_name): + return self._ns_controller.get_namespace(namespace_name) diff --git a/graffiti/api/controllers/v1/captype_controller.py b/graffiti/api/controllers/v1/captype_controller.py new file mode 100644 index 0000000..bd93513 --- /dev/null +++ b/graffiti/api/controllers/v1/captype_controller.py @@ -0,0 +1,39 @@ +# 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. + + +class CapabilityTypeControllerBase(object): + + def __init__(self, **kwargs): + super(CapabilityTypeControllerBase, self).__init__(**kwargs) + self._type = 'None' + + def get_capability_type(self, name, namespace): + return None + + def get_type(self): + return self._type + + def find_capability_types(self, query_string): + return [] + + def set_capability_type(self, capability_type=None): + pass + + def put_capability_type(self, name, namespace, capability_type=None): + pass + + def delete_capability_type(self, name, namespace): + pass diff --git a/graffiti/api/controllers/v1/captype_controller_factory.py b/graffiti/api/controllers/v1/captype_controller_factory.py new file mode 100644 index 0000000..3c48b96 --- /dev/null +++ b/graffiti/api/controllers/v1/captype_controller_factory.py @@ -0,0 +1,42 @@ +# 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.controllers.v1.captype_db_controller \ + import DBCapabilityTypeController +from graffiti.api.controllers.v1.captype_file_controller \ + import FileCapabilityTypeController +from graffiti.api.controllers.v1.captype_mem_controller \ + import MemCapabilityTypeController + + +class CapTypeControllerFactory(object): + + def __init__(self, **kwargs): + super(CapTypeControllerFactory, self).__init__(**kwargs) + + @staticmethod + def create(controller_type, **kwargs): + if controller_type.lower() == 'memory': + print "Dictionary persistence = memory" + return MemCapabilityTypeController(**kwargs) + elif controller_type.lower() == "db": + print "Dictionary persistence = db" + return DBCapabilityTypeController(**kwargs) + elif controller_type.lower() == "file": + print "Dictionary persistence = File" + return FileCapabilityTypeController(**kwargs) + + return None diff --git a/graffiti/api/controllers/v1/captype_db_controller.py b/graffiti/api/controllers/v1/captype_db_controller.py new file mode 100644 index 0000000..8e24ac9 --- /dev/null +++ b/graffiti/api/controllers/v1/captype_db_controller.py @@ -0,0 +1,39 @@ +# 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 captype_controller import CapabilityTypeControllerBase + + +# TODO(Wayne): Implement the db controller +class DBCapabilityTypeController(CapabilityTypeControllerBase): + + def __init__(self, **kwargs): + super(DBCapabilityTypeController, self).__init__(**kwargs) + self._type = 'DBCapabilityTypeController' + + def get_capability_type(self, name, namespace): + pass + + def find_capability_types(self, query_string): + pass + + def set_capability_type(self, capability_type=None): + pass + + def put_capability_type(self, name, namespace, capability_type=None): + pass + + def delete_capability_type(self, name, namespace): + pass diff --git a/graffiti/api/controllers/v1/captype_file_controller.py b/graffiti/api/controllers/v1/captype_file_controller.py new file mode 100644 index 0000000..b3bc47e --- /dev/null +++ b/graffiti/api/controllers/v1/captype_file_controller.py @@ -0,0 +1,84 @@ +# 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 captype_controller import CapabilityTypeControllerBase +from graffiti.api.model.v1.capability_type import CapabilityType +import json +from oslo.config import cfg +from wsme.rest.json import fromjson +from wsme.rest.json import tojson + + +class FileCapabilityTypeController(CapabilityTypeControllerBase): + + def __init__(self, **kwargs): + super(FileCapabilityTypeController, self).__init__(**kwargs) + self._type = 'FileCapabilityTypeController' + self._graffiti_folder = cfg.CONF.FILE_PERSISTENCE.dictionary_folder + self._filename = "dictionary.json" + self._dictionaryfile = self._graffiti_folder + self._filename + self._capability_types = self.__file_to_memory() + + def get_capability_type(self, name, namespace): + id = namespace + ":" + name + return self._capability_types[id] + + def find_capability_types(self, query_string): + return self._capability_types.itervalues() + + def set_capability_type(self, capability_type): + id = capability_type.namespace + ":" + capability_type.name + self._capability_types[id] = capability_type + self.__memory_to_file() + return capability_type + + def put_capability_type(self, name, namespace, capability_type): + id = namespace + ":" + name + self._capability_types[id] = capability_type + self.__memory_to_file() + return capability_type + + def delete_capability_type(self, name, namespace): + id = namespace + ":" + name + capability_type = None + if self._capability_types[id]: + capability_type = self._capability_types[id] + self._capability_types.pop(id) + self.__memory_to_file() + return capability_type + + def __file_to_memory(self): + try: + capability_types = {} + with open(self._dictionaryfile, "r") as gfile: + doc = json.load(gfile) + for id in doc: + capability_types[id] = fromjson(CapabilityType, doc[id]) + return capability_types + + except IOError: + with open(self._dictionaryfile, "w+") as gfile: + gfile.write("") + return {} + + def __memory_to_file(self): + file_capability_types = {} + for (id, capability_type) in self._capability_types.items(): + json_data = tojson(CapabilityType, capability_type) + file_capability_types[id] = json_data + + with open(self._dictionaryfile, "w+") as gfile: + json.dump(file_capability_types, gfile) diff --git a/graffiti/api/controllers/v1/captype_mem_controller.py b/graffiti/api/controllers/v1/captype_mem_controller.py new file mode 100644 index 0000000..180ddb7 --- /dev/null +++ b/graffiti/api/controllers/v1/captype_mem_controller.py @@ -0,0 +1,52 @@ +# 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 captype_controller import CapabilityTypeControllerBase + + +class MemCapabilityTypeController(CapabilityTypeControllerBase): + + def __init__(self, **kwargs): + super(MemCapabilityTypeController, self).__init__(**kwargs) + self._type = 'MemCapabilityTypeController' + self._capability_types = {} + + def get_capability_type(self, name, namespace): + id = namespace + ":" + name + return self._capability_types[id] + + def find_capability_types(self, query_string): + return self._capability_types.itervalues() + + def set_capability_type(self, capability_type): + id = capability_type.namespace + ":" + capability_type.name + self._capability_types[id] = capability_type + + return capability_type + + def put_capability_type(self, name, namespace, capability_type): + id = namespace + ":" + name + self._capability_types[id] = capability_type + + return capability_type + + def delete_capability_type(self, name, namespace): + id = namespace + ":" + name + capability_type = None + if self._capability_types[id]: + capability_type = self._capability_types[id] + self._capability_types.pop(id) + + return capability_type diff --git a/graffiti/api/controllers/v1/namespace.py b/graffiti/api/controllers/v1/namespace.py index ac13643..6e538ef 100644 --- a/graffiti/api/controllers/v1/namespace.py +++ b/graffiti/api/controllers/v1/namespace.py @@ -17,40 +17,55 @@ from pecan.rest import RestController from wsmeext.pecan import wsexpose +from graffiti.api.controllers.v1.ns_controller_factory\ + import NSControllerFactory from graffiti.api.model.v1.namespace import Namespace - +from oslo.config import cfg import six -namespaces = [] - class NamespaceController(RestController): def __init__(self): super(RestController, self).__init__() self.status = 200 + self._controller = self._load_controller() + + def _load_controller(self): + controller_type = cfg.CONF.DEFAULT.persistence_type + _controller = NSControllerFactory.create(controller_type) + return _controller @wsexpose - def options(): + def options(self): pass @wsexpose(Namespace, six.text_type) - def get_one(self, name): - global namespaces - - for namespace in namespaces: - if namespace.name.lower() == name.lower(): - return namespace - - res = Namespace(Namespace(), status_code=404, - error="Namespace Not Found") - return res + def get_one(self, namespace_name): + namespace = self._controller.get_namespace(namespace_name) + return namespace @wsexpose([Namespace]) - def get_all(self): - global namespaces - return namespaces + def get_all(self, query_string=None): + namespace_list = self._controller.find_namespaces(query_string) + return namespace_list @wsexpose(Namespace, body=Namespace) def post(self, namespace): - global namespaces - namespaces.append(namespace) + """Create Namespace + :namespace param: + graffiti.api.model.v1.namespace.Namespace + """ + + self._controller.set_namespace(namespace) + return namespace + + @wsexpose(Namespace, six.text_type, body=Namespace) + def put(self, namespace_name, namespace): + self._controller.put_namespace(namespace_name, namespace) + return namespace + + @wsexpose(Namespace, six.text_type) + def delete(self, namespace_name): + print "namespace", namespace_name + namespace = self._controller.delete_namespace(namespace_name) + return namespace diff --git a/graffiti/api/controllers/v1/ns_controller.py b/graffiti/api/controllers/v1/ns_controller.py new file mode 100755 index 0000000..1fab29b --- /dev/null +++ b/graffiti/api/controllers/v1/ns_controller.py @@ -0,0 +1,39 @@ +# 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. + + +class NSTypeControllerBase(object): + + def __init__(self, **kwargs): + super(NSTypeControllerBase, self).__init__(**kwargs) + self._type = 'None' + + def get_namespace(self, namespace_name): + return None + + def get_type(self): + return self._type + + def find_namespaces(self, query_string): + return [] + + def set_namespace(self, namespace): + pass + + def put_namespace(self, namespace_name, namespace): + pass + + def delete_namespace(self, namespace_name): + pass diff --git a/graffiti/api/controllers/v1/ns_controller_factory.py b/graffiti/api/controllers/v1/ns_controller_factory.py new file mode 100755 index 0000000..0789184 --- /dev/null +++ b/graffiti/api/controllers/v1/ns_controller_factory.py @@ -0,0 +1,57 @@ +# 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.controllers.v1.ns_db_controller \ + import DBNSController +from graffiti.api.controllers.v1.ns_file_controller \ + import FileNSController +from graffiti.api.controllers.v1.ns_mem_controller \ + import MemNSController +from oslo.config import cfg + + +class NSControllerFactory(object): + + __controller = None + __controller_type = cfg.CONF.DEFAULT.persistence_type + + def __init__(self, **kwargs): + super(NSControllerFactory, self).__init__(**kwargs) + + @staticmethod + def create(controller_type, **kwargs): + if controller_type.lower() == 'memory': + print "Namespace persistence = memory" + NSControllerFactory.__controller = MemNSController(**kwargs) + return NSControllerFactory.__controller + elif controller_type.lower() == "db": + print "Namespace persistence = db" + NSControllerFactory.__controller = DBNSController(**kwargs) + return NSControllerFactory.__controller + elif controller_type.lower() == "file": + print "Namespace persistence = File" + NSControllerFactory.__controller = FileNSController(**kwargs) + return NSControllerFactory.__controller + + return None + + @staticmethod + def get(): + if NSControllerFactory.__controller: + return NSControllerFactory.__controller + else: + return NSControllerFactory.create( + NSControllerFactory.__controller_type) diff --git a/graffiti/api/controllers/v1/ns_db_controller.py b/graffiti/api/controllers/v1/ns_db_controller.py new file mode 100755 index 0000000..fd30691 --- /dev/null +++ b/graffiti/api/controllers/v1/ns_db_controller.py @@ -0,0 +1,43 @@ +# 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 ns_controller import NSTypeControllerBase + + +# TODO(Wayne): Implement the db controller +class DBNSController(NSTypeControllerBase): + + def __init__(self, **kwargs): + super(DBNSController, self).__init__(**kwargs) + self._type = 'DBNSController' + + def get_type(self): + return self._type + + def get_namespace(self, namespace_name): + pass + + def find_namespaces(self, query_string): + pass + + def set_namespace(self, namespace): + pass + + def put_namespace(self, namespace_name, namespace): + pass + + def delete_namespace(self, namespace_name): + pass diff --git a/graffiti/api/controllers/v1/ns_file_controller.py b/graffiti/api/controllers/v1/ns_file_controller.py new file mode 100755 index 0000000..8ff5d6d --- /dev/null +++ b/graffiti/api/controllers/v1/ns_file_controller.py @@ -0,0 +1,80 @@ +# 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.namespace import Namespace +import json +from ns_controller import NSTypeControllerBase +from oslo.config import cfg +from wsme.rest.json import fromjson +from wsme.rest.json import tojson + + +class FileNSController(NSTypeControllerBase): + + def __init__(self, **kwargs): + super(FileNSController, self).__init__(**kwargs) + self._type = 'FileNSController' + self._graffiti_folder = cfg.CONF.FILE_PERSISTENCE.dictionary_folder + self._filename = "namespaces.json" + self._namespacefile = self._graffiti_folder + self._filename + self._namespaces = self.__file_to_memory() + + def get_namespace(self, namespace_name): + return self._namespaces[namespace_name] + + def find_namespaces(self, query_string): + return self._namespaces.itervalues() + + def set_namespace(self, namespace): + self._namespaces[namespace.name] = namespace + self.__memory_to_file() + return namespace + + def put_namespace(self, namespace_name, namespace): + self._namespaces[namespace_name] = namespace + self.__memory_to_file() + return namespace + + def delete_namespace(self, namespace_name): + namespace = None + if self._namespaces[namespace_name]: + namespace = self._namespaces[namespace_name] + self._namespaces.pop(namespace_name) + self.__memory_to_file() + return namespace + + def __file_to_memory(self): + try: + namespaces = {} + with open(self._namespacefile, "r") as gfile: + doc = json.load(gfile) + for namespace in doc: + namespaces[namespace] = fromjson(Namespace, doc[namespace]) + return namespaces + + except IOError: + with open(self._namespacefile, "w+") as gfile: + gfile.write("") + return {} + + def __memory_to_file(self): + namespaces = {} + for (namespace_name, namespace) in self._namespaces.items(): + json_data = tojson(Namespace, namespace) + namespaces[namespace_name] = json_data + + with open(self._namespacefile, "w+") as gfile: + json.dump(namespaces, gfile) diff --git a/graffiti/api/controllers/v1/ns_mem_controller.py b/graffiti/api/controllers/v1/ns_mem_controller.py new file mode 100755 index 0000000..e6c641c --- /dev/null +++ b/graffiti/api/controllers/v1/ns_mem_controller.py @@ -0,0 +1,49 @@ +# 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 ns_controller import NSTypeControllerBase + + +class MemNSController(NSTypeControllerBase): + + def __init__(self, **kwargs): + super(MemNSController, self).__init__(**kwargs) + self._type = 'MemNSController' + self._namespaces = {} + + def get_type(self): + return self._type + + def get_namespace(self, namespace_name): + return self._namespaces[namespace_name] + + def find_namespaces(self, query_string): + return self._namespaces.itervalues() + + def set_namespace(self, namespace): + self._namespaces[namespace.name] = namespace + return namespace + + def put_namespace(self, namespace_name, namespace): + self._namespaces[namespace.name] = namespace + return namespace + + def delete_namespace(self, namespace_name): + namespace = None + if self._namespaces[namespace_name]: + namespace = self._namespaces[namespace_name] + self._namespaces.pop(namespace_name) + + return namespace diff --git a/graffiti/api/controllers/v1/resource.py b/graffiti/api/controllers/v1/resource.py index c3a579b..08d2b67 100644 --- a/graffiti/api/controllers/v1/resource.py +++ b/graffiti/api/controllers/v1/resource.py @@ -13,15 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +import pecan from pecan.rest import RestController + 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_controller_factory import \ - ResourceControllerFactory + +from graffiti.api.model.v1.resource_dao_factory import \ + ResourceDAOFactory from graffiti.common.utils import _ @@ -29,6 +33,8 @@ from oslo.config import cfg import six +import keystoneclient.v2_0.client as ksclient + resource_controller_group = cfg.OptGroup('resource_controller') resource_controller_opts = [ @@ -42,6 +48,11 @@ cfg.CONF.register_opts(resource_controller_opts, 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__() @@ -53,37 +64,98 @@ class ResourceController(RestController): controller_type = cfg.CONF.resource_controller.type controller_type = controller_type if controller_type else 'Local' - _controller = ResourceControllerFactory.create(controller_type) + _controller = ResourceDAOFactory.create(controller_type) return _controller @wsexpose() - def options(): + def options(self): pass - @wsexpose(Resource, six.text_type) - def get_one(self, id): - res = self._controller.get_resource(id) - if res: + @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) 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 - res = Response(Resource(), status_code=404, error="Resource Not Found") + # 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" + + 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 + return res_list.itervalues() return [] @wsexpose(Resource, six.text_type, body=Resource) - def put(self, id, resource): - - self._controller.set_resource(id, resource_definition=resource) + def put(self, resource_id, resource): + """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) return resource @wsexpose(Resource, body=Resource) diff --git a/graffiti/api/hooks.py b/graffiti/api/hooks.py index e31dced..9e69866 100644 --- a/graffiti/api/hooks.py +++ b/graffiti/api/hooks.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -#import json +# import json from pecan.hooks import PecanHook diff --git a/graffiti/api/model/v1/capability_type.py b/graffiti/api/model/v1/capability_type.py index e392729..f149e6a 100644 --- a/graffiti/api/model/v1/capability_type.py +++ b/graffiti/api/model/v1/capability_type.py @@ -16,11 +16,17 @@ import wsme from wsme import types +from graffiti.api.model.v1.derived_type import DerivedType +from graffiti.api.model.v1.property_type import PropertyType + class CapabilityType(types.Base): + name = wsme.wsattr(types.text, mandatory=True) namespace = wsme.wsattr(types.text, mandatory=True) description = wsme.wsattr(types.text, mandatory=False) + properties = wsme.wsattr({types.text: PropertyType}, mandatory=False) + derived_from = wsme.wsattr(DerivedType, mandatory=False) def __init__(self, **kwargs): super(CapabilityType, self).__init__(**kwargs) diff --git a/graffiti/api/model/v1/derived_type.py b/graffiti/api/model/v1/derived_type.py new file mode 100644 index 0000000..31442b6 --- /dev/null +++ b/graffiti/api/model/v1/derived_type.py @@ -0,0 +1,27 @@ +# 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. + +import wsme +from wsme import types + + +class DerivedType(types.Base): + name = wsme.wsattr(types.text, mandatory=True) + namespace = wsme.wsattr(types.text, mandatory=True) + + _wsme_attr_order = ('name', 'namespace') + + def __init__(self, **kwargs): + super(DerivedType, self).__init__(**kwargs) diff --git a/graffiti/api/model/v1/namespace.py b/graffiti/api/model/v1/namespace.py index 599c3be..357f54d 100644 --- a/graffiti/api/model/v1/namespace.py +++ b/graffiti/api/model/v1/namespace.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. + import wsme from wsme import types @@ -20,7 +21,7 @@ from wsme import types class Namespace(types.Base): name = wsme.wsattr(types.text, mandatory=True) scope = wsme.wsattr(types.text, mandatory=True) - owner = wsme.wsattr(types.text, mandatory=True) + owner = wsme.wsattr(types.text, mandatory=False) def __init__(self, **kwargs): super(Namespace, self).__init__(**kwargs) diff --git a/graffiti/api/model/v1/property_item_type.py b/graffiti/api/model/v1/property_item_type.py new file mode 100755 index 0000000..228147a --- /dev/null +++ b/graffiti/api/model/v1/property_item_type.py @@ -0,0 +1,27 @@ +# 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. + +import wsme +from wsme import types + + +class ItemType(types.Base): + type = wsme.wsattr(types.text, mandatory=True) + enum = wsme.wsattr([types.text], mandatory=False) + + _wsme_attr_order = ('type', 'enum') + + def __init__(self, **kwargs): + super(ItemType, self).__init__(**kwargs) diff --git a/graffiti/api/model/v1/property_type.py b/graffiti/api/model/v1/property_type.py new file mode 100644 index 0000000..fc84af7 --- /dev/null +++ b/graffiti/api/model/v1/property_type.py @@ -0,0 +1,50 @@ +# 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.property_item_type import ItemType +import wsme +from wsme import types + + +class PropertyType(types.Base): + type = wsme.wsattr(types.text, mandatory=True) + description = wsme.wsattr(types.text, mandatory=False) + default = wsme.wsattr(types.text, mandatory=False) + required = wsme.wsattr(bool, mandatory=False, default=False) + + # fields for type = string + minimum = wsme.wsattr(int, mandatory=False) + maximum = wsme.wsattr(int, mandatory=False) + + # fields for type = integer, number + minLength = wsme.wsattr(int, mandatory=False) + maxLength = wsme.wsattr(int, mandatory=False) + pattern = wsme.wsattr(types.text, mandatory=False) + confidential = wsme.wsattr(bool, mandatory=False) + + # fields for type = array + items = wsme.wsattr(ItemType, mandatory=False) + uniqueItems = wsme.wsattr(bool, mandatory=False) + minItems = wsme.wsattr(int, mandatory=False) + maxItems = wsme.wsattr(int, mandatory=False) + additionalItems = wsme.wsattr(bool, mandatory=False) + + _wsme_attr_order = ('type', 'description', 'default', 'required', + 'minimum', 'maximum', 'minLength', 'maxLength', + 'pattern', 'confidential', 'items', 'uniqueItems', + 'additionalItems') + + def __init__(self, **kwargs): + super(PropertyType, self).__init__(**kwargs) diff --git a/graffiti/api/model/v1/resource_controller.py b/graffiti/api/model/v1/resource_dao.py similarity index 84% rename from graffiti/api/model/v1/resource_controller.py rename to graffiti/api/model/v1/resource_dao.py index f10bf21..744a987 100644 --- a/graffiti/api/model/v1/resource_controller.py +++ b/graffiti/api/model/v1/resource_dao.py @@ -14,10 +14,10 @@ # limitations under the License. -class ResourceControllerBase(object): +class ResourceDAOBase(object): def __init__(self, **kwargs): - super(ResourceControllerBase, self).__init__(**kwargs) + super(ResourceDAOBase, self).__init__(**kwargs) self._type = 'None' @@ -34,12 +34,12 @@ class ResourceControllerBase(object): pass -class LocalResourceController(ResourceControllerBase): +class LocalResourceDAO(ResourceDAOBase): def __init__(self, **kwargs): - super(LocalResourceController, self).__init__(**kwargs) + super(LocalResourceDAO, self).__init__(**kwargs) - self._type = 'LocalResourceController' + self._type = 'LocalResourceDAO' self._resources = dict() self._last_id = 0 diff --git a/graffiti/api/model/v1/resource_controller_factory.py b/graffiti/api/model/v1/resource_dao_factory.py similarity index 67% rename from graffiti/api/model/v1/resource_controller_factory.py rename to graffiti/api/model/v1/resource_dao_factory.py index 87fd118..71d3a90 100644 --- a/graffiti/api/model/v1/resource_controller_factory.py +++ b/graffiti/api/model/v1/resource_dao_factory.py @@ -13,17 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -from graffiti.api.model.v1.resource_controller import LocalResourceController +from graffiti.api.model.v1.resource_dao import LocalResourceDAO -class ResourceControllerFactory(object): +class ResourceDAOFactory(object): def __init__(self, **kwargs): - super(ResourceControllerFactory, self).__init__(**kwargs) + super(ResourceDAOFactory, self).__init__(**kwargs) @staticmethod - def create(controller_type, **kwargs): - if controller_type.lower() == 'local': - return LocalResourceController(**kwargs) + def create(dao_type, **kwargs): + if dao_type.lower() == 'local': + return LocalResourceDAO(**kwargs) return None diff --git a/graffiti/api/plugins/__init__.py b/graffiti/api/plugins/__init__.py new file mode 100644 index 0000000..0f613e8 --- /dev/null +++ b/graffiti/api/plugins/__init__.py @@ -0,0 +1 @@ +__author__ = 'lakshmi' diff --git a/graffiti/api/plugins/glance_image.py b/graffiti/api/plugins/glance_image.py new file mode 100644 index 0000000..fa2eb4f --- /dev/null +++ b/graffiti/api/plugins/glance_image.py @@ -0,0 +1,95 @@ +# 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) diff --git a/graffiti/api/tests/base.py b/graffiti/api/tests/base.py index 6e93268..5a957c0 100644 --- a/graffiti/api/tests/base.py +++ b/graffiti/api/tests/base.py @@ -18,10 +18,39 @@ import os import fixtures +from oslo.config import cfg import testtools _TRUE_VALUES = ('True', 'true', '1', 'yes') +# DEFAULT group +default_controller_group = cfg.OptGroup('DEFAULT') +default_controller_opts = [ + cfg.StrOpt( + 'persistence_type', + default="memory", + help=("persistence options. " + "values = 'memory' or 'file' or 'db")) +] + +cfg.CONF.register_group(default_controller_group) +cfg.CONF.register_opts(default_controller_opts, + group=default_controller_group) + +# FILE_PERSISTENCE group +file_controller_group = cfg.OptGroup('FILE_PERSISTENCE') +file_controller_opts = [ + cfg.StrOpt( + 'dictionary_folder', + default="/tmp/graffiti-dictionary-test/", + help=("Absolute path of the file for persisting dictionary") + ) +] + +cfg.CONF.register_group(file_controller_group) +cfg.CONF.register_opts(file_controller_opts, + group=file_controller_group) + class TestCase(testtools.TestCase): diff --git a/graffiti/api/tests/test_controller_v1.py b/graffiti/api/tests/test_controller_v1.py index 34c12ac..af2f97b 100644 --- a/graffiti/api/tests/test_controller_v1.py +++ b/graffiti/api/tests/test_controller_v1.py @@ -25,8 +25,12 @@ from graffiti.api.tests import base from graffiti.api.controllers.root import RootController from graffiti.api.controllers.versions import V1Controller -from graffiti.api.model.v1.resource_controller_factory \ - import ResourceControllerFactory +from graffiti.api.controllers.v1.captype_controller_factory \ + import CapTypeControllerFactory +from graffiti.api.controllers.v1.ns_controller_factory \ + import NSControllerFactory +from graffiti.api.model.v1.resource_dao_factory \ + import ResourceDAOFactory class TestControllerV1(base.TestCase): @@ -35,14 +39,48 @@ class TestControllerV1(base.TestCase): root = RootController() self.assertIn(hasattr(root, 'v1'), [True]) + def test_v1_namespace_exists(self): + v1 = V1Controller() + self.assertIn(hasattr(v1, 'namespace'), [True]) + + def test_v1_namespace_controller_factory__memory(self): + rc = NSControllerFactory.create('memory') + self.assertEquals(rc.get_type(), 'MemNSController') + + # TODO(Lakshmi): Create folder before any tests run + # def test_v1_namespace_controller_factory__file(self): + # rc = NSControllerFactory.create('file') + # self.assertEquals(rc.get_type(), 'FileNSController') + + def test_v1_namespace_controller_factory__db(self): + rc = NSControllerFactory.create('db') + self.assertEquals(rc.get_type(), 'DBNSController') + + def test_v1_capability_type_exists(self): + v1 = V1Controller() + self.assertIn(hasattr(v1, 'capability_type'), [True]) + + def test_v1_capability_type_controller_factory__memory(self): + rc = CapTypeControllerFactory.create('memory') + self.assertEquals(rc.get_type(), 'MemCapabilityTypeController') + + # TODO(Lakshmi): Create folder before any tests run + # def test_v1_capability_type_controller_factory__file(self): + # rc = CapTypeControllerFactory.create('file') + # self.assertEquals(rc.get_type(), 'FileCapabilityTypeController') + + def test_v1_capability_type_controller_factory__db(self): + rc = CapTypeControllerFactory.create('db') + self.assertEquals(rc.get_type(), 'DBCapabilityTypeController') + def test_v1_resource_exists(self): v1 = V1Controller() self.assertIn(hasattr(v1, 'resource'), [True]) def test_v1_resource_controller_factory__local(self): - rc = ResourceControllerFactory.create('local') - self.assertEquals(rc.get_type(), 'LocalResourceController') + rc = ResourceDAOFactory.create('local') + self.assertEquals(rc.get_type(), 'LocalResourceDAO') def test_v1_resource_controller_factory__unknown(self): - rc = ResourceControllerFactory.create('invalid_controller') + rc = ResourceDAOFactory.create('invalid_controller') self.assertTrue(rc is None) diff --git a/requirements.txt b/requirements.txt index ad31f5f..9544d37 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ Babel>=0.9.6 pecan>=0.4.4 WSME>=0.6 oslo.config +python-glanceclient diff --git a/tox.ini b/tox.ini index bbf1450..9a82bc7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 1.6 -envlist = py33,py27,py26,pep8 +envlist = py27,py26,pep8 skipsdist = True [testenv]