move everything into pecan
This commit is contained in:
		@@ -6,3 +6,4 @@ wsgiref>=0.1.2
 | 
			
		||||
pecan>=0.4.5
 | 
			
		||||
oslo.config>=1.2.0
 | 
			
		||||
WSME>=0.6
 | 
			
		||||
six>=1.5.2
 | 
			
		||||
 
 | 
			
		||||
@@ -112,7 +112,7 @@ class TeethAgent(object):
 | 
			
		||||
        self.listen_address = listen_address
 | 
			
		||||
        self.mode_implementation = None
 | 
			
		||||
        self.version = pkg_resources.get_distribution('teeth-agent').version
 | 
			
		||||
        self.api = app.VersionSelectorApplication()
 | 
			
		||||
        self.api = app.VersionSelectorApplication(self)
 | 
			
		||||
        self.command_results = collections.OrderedDict()
 | 
			
		||||
        self.heartbeater = TeethAgentHeartbeater(self)
 | 
			
		||||
        self.hardware = hardware.get_manager()
 | 
			
		||||
@@ -187,7 +187,10 @@ class TeethAgent(object):
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                # Other errors are considered command execution errors, and are
 | 
			
		||||
                # recorded as an
 | 
			
		||||
                result = base.SyncCommandResult(command_name, kwargs, False, e)
 | 
			
		||||
                result = base.SyncCommandResult(command_name,
 | 
			
		||||
                                                kwargs,
 | 
			
		||||
                                                False,
 | 
			
		||||
                                                unicode(e))
 | 
			
		||||
 | 
			
		||||
            self.command_results[result.id] = result
 | 
			
		||||
            return result
 | 
			
		||||
 
 | 
			
		||||
@@ -16,19 +16,29 @@ limitations under the License.
 | 
			
		||||
 | 
			
		||||
from oslo.config import cfg
 | 
			
		||||
import pecan
 | 
			
		||||
from pecan import hooks
 | 
			
		||||
 | 
			
		||||
from teeth_agent.api import config
 | 
			
		||||
 | 
			
		||||
CONF = cfg.CONF
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AgentHook(hooks.PecanHook):
 | 
			
		||||
    def __init__(self, agent, *args, **kwargs):
 | 
			
		||||
        super(AgentHook, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.agent = agent
 | 
			
		||||
 | 
			
		||||
    def before(self, state):
 | 
			
		||||
        state.request.agent = self.agent
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_pecan_config():
 | 
			
		||||
    # Set up the pecan configuration
 | 
			
		||||
    filename = config.__file__.replace('.pyc', '.py')
 | 
			
		||||
    return pecan.configuration.conf_from_file(filename)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_app(pecan_config=None, extra_hooks=None):
 | 
			
		||||
def setup_app(agent, pecan_config=None, extra_hooks=None):
 | 
			
		||||
    #policy.init()
 | 
			
		||||
 | 
			
		||||
    #app_hooks = [hooks.ConfigHook(),
 | 
			
		||||
@@ -39,6 +49,8 @@ def setup_app(pecan_config=None, extra_hooks=None):
 | 
			
		||||
    #if extra_hooks:
 | 
			
		||||
        #app_hooks.extend(extra_hooks)
 | 
			
		||||
 | 
			
		||||
    app_hooks = [AgentHook(agent)]
 | 
			
		||||
 | 
			
		||||
    if not pecan_config:
 | 
			
		||||
        pecan_config = get_pecan_config()
 | 
			
		||||
 | 
			
		||||
@@ -50,7 +62,7 @@ def setup_app(pecan_config=None, extra_hooks=None):
 | 
			
		||||
        debug=True,
 | 
			
		||||
        #debug=CONF.debug,
 | 
			
		||||
        force_canonical=getattr(pecan_config.app, 'force_canonical', True),
 | 
			
		||||
        #hooks=app_hooks,
 | 
			
		||||
        hooks=app_hooks,
 | 
			
		||||
        #wrap_app=middleware.ParsableErrorMiddleware,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
@@ -58,9 +70,9 @@ def setup_app(pecan_config=None, extra_hooks=None):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VersionSelectorApplication(object):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
    def __init__(self, agent):
 | 
			
		||||
        pc = get_pecan_config()
 | 
			
		||||
        self.v1 = setup_app(pecan_config=pc)
 | 
			
		||||
        self.v1 = setup_app(agent, pecan_config=pc)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, environ, start_response):
 | 
			
		||||
        return self.v1(environ, start_response)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,13 +13,7 @@
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Version 1 of the Ironic API
 | 
			
		||||
 | 
			
		||||
NOTE: IN PROGRESS AND NOT FULLY IMPLEMENTED.
 | 
			
		||||
 | 
			
		||||
Should maintain feature parity with Nova Baremetal Extension.
 | 
			
		||||
 | 
			
		||||
Specification can be found at ironic/doc/api/v1.rst
 | 
			
		||||
Version 1 of the Ironic Python Agent API
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import pecan
 | 
			
		||||
@@ -29,11 +23,8 @@ from wsme import types as wtypes
 | 
			
		||||
import wsmeext.pecan as wsme_pecan
 | 
			
		||||
 | 
			
		||||
from teeth_agent.api.controllers.v1 import base
 | 
			
		||||
#from ironic.api.controllers.v1 import chassis
 | 
			
		||||
#from ironic.api.controllers.v1 import driver
 | 
			
		||||
from teeth_agent.api.controllers.v1 import command
 | 
			
		||||
from teeth_agent.api.controllers.v1 import link
 | 
			
		||||
#from ironic.api.controllers.v1 import node
 | 
			
		||||
#from ironic.api.controllers.v1 import port
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MediaType(base.APIBase):
 | 
			
		||||
@@ -59,17 +50,8 @@ class V1(base.APIBase):
 | 
			
		||||
    links = [link.Link]
 | 
			
		||||
    "Links that point to a specific URL for this version and documentation"
 | 
			
		||||
 | 
			
		||||
    #chassis = [link.Link]
 | 
			
		||||
    #"Links to the chassis resource"
 | 
			
		||||
 | 
			
		||||
    #nodes = [link.Link]
 | 
			
		||||
    #"Links to the nodes resource"
 | 
			
		||||
 | 
			
		||||
    #ports = [link.Link]
 | 
			
		||||
    #"Links to the ports resource"
 | 
			
		||||
 | 
			
		||||
    #drivers = [link.Link]
 | 
			
		||||
    #"Links to the drivers resource"
 | 
			
		||||
    commands = [link.Link]
 | 
			
		||||
    "Links to the command resource"
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def convert(self):
 | 
			
		||||
@@ -84,46 +66,22 @@ class V1(base.APIBase):
 | 
			
		||||
                                'api-spec-v1.html',
 | 
			
		||||
                                bookmark=True, type='text/html')
 | 
			
		||||
        ]
 | 
			
		||||
        v1.command = [link.Link.make_link('self', pecan.request.host_url,
 | 
			
		||||
                                          'commands', ''),
 | 
			
		||||
                      link.Link.make_link('bookmark',
 | 
			
		||||
                                          pecan.request.host_url,
 | 
			
		||||
                                          'commands', '',
 | 
			
		||||
                                          bookmark=True)
 | 
			
		||||
                     ]
 | 
			
		||||
        v1.media_types = [MediaType('application/json',
 | 
			
		||||
                          'application/vnd.openstack.ironic.v1+json')]
 | 
			
		||||
        #v1.chassis = [link.Link.make_link('self', pecan.request.host_url,
 | 
			
		||||
                                          #'chassis', ''),
 | 
			
		||||
                      #link.Link.make_link('bookmark',
 | 
			
		||||
                                           #pecan.request.host_url,
 | 
			
		||||
                                           #'chassis', '',
 | 
			
		||||
                                           #bookmark=True)
 | 
			
		||||
                     #]
 | 
			
		||||
        #v1.nodes = [link.Link.make_link('self', pecan.request.host_url,
 | 
			
		||||
                                        #'nodes', ''),
 | 
			
		||||
                    #link.Link.make_link('bookmark',
 | 
			
		||||
                                        #pecan.request.host_url,
 | 
			
		||||
                                        #'nodes', '',
 | 
			
		||||
                                        #bookmark=True)
 | 
			
		||||
                   #]
 | 
			
		||||
        #v1.ports = [link.Link.make_link('self', pecan.request.host_url,
 | 
			
		||||
                                        #'ports', ''),
 | 
			
		||||
                    #link.Link.make_link('bookmark',
 | 
			
		||||
                                        #pecan.request.host_url,
 | 
			
		||||
                                        #'ports', '',
 | 
			
		||||
                                        #bookmark=True)
 | 
			
		||||
                   #]
 | 
			
		||||
        #v1.drivers = [link.Link.make_link('self', pecan.request.host_url,
 | 
			
		||||
                                          #'drivers', ''),
 | 
			
		||||
                      #link.Link.make_link('bookmark',
 | 
			
		||||
                                          #pecan.request.host_url,
 | 
			
		||||
                                          #'drivers', '',
 | 
			
		||||
                                          #bookmark=True)
 | 
			
		||||
                     #]
 | 
			
		||||
        return v1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Controller(rest.RestController):
 | 
			
		||||
    """Version 1 API controller root."""
 | 
			
		||||
 | 
			
		||||
    #nodes = node.NodesController()
 | 
			
		||||
    #ports = port.PortsController()
 | 
			
		||||
    #chassis = chassis.ChassisController()
 | 
			
		||||
    #drivers = driver.DriversController()
 | 
			
		||||
    commands = command.CommandController()
 | 
			
		||||
 | 
			
		||||
    @wsme_pecan.wsexpose(V1)
 | 
			
		||||
    def get(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -12,36 +12,44 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import wsme
 | 
			
		||||
from wsme import types as wtypes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MultiType(wtypes.UserType):
 | 
			
		||||
    """A complex type that represents one or more types.
 | 
			
		||||
 | 
			
		||||
    Used for validating that a value is an instance of one of the types.
 | 
			
		||||
 | 
			
		||||
    :param *types: Variable-length list of types.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, *types):
 | 
			
		||||
        self.types = types
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return ' | '.join(map(str, self.types))
 | 
			
		||||
 | 
			
		||||
    def validate(self, value):
 | 
			
		||||
        for t in self.types:
 | 
			
		||||
            if t is wtypes.text and isinstance(value, wtypes.bytes):
 | 
			
		||||
                value = value.decode()
 | 
			
		||||
            if isinstance(value, t):
 | 
			
		||||
                return value
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Wrong type. Expected '{type}', got '{value}'".format(
 | 
			
		||||
                    type=self.types, value=type(value)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
json_type = MultiType(list, dict, six.integer_types, wtypes.text)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class APIBase(wtypes.Base):
 | 
			
		||||
 | 
			
		||||
    created_at = wsme.wsattr(datetime.datetime, readonly=True)
 | 
			
		||||
    "The time in UTC at which the object is created"
 | 
			
		||||
 | 
			
		||||
    updated_at = wsme.wsattr(datetime.datetime, readonly=True)
 | 
			
		||||
    "The time in UTC at which the object is updated"
 | 
			
		||||
 | 
			
		||||
    def as_dict(self):
 | 
			
		||||
        """Render this object as a dict of its fields."""
 | 
			
		||||
        return dict((k, getattr(self, k))
 | 
			
		||||
                    for k in self.fields
 | 
			
		||||
                    if hasattr(self, k) and
 | 
			
		||||
                    getattr(self, k) != wsme.Unset)
 | 
			
		||||
 | 
			
		||||
    def unset_fields_except(self, except_list=None):
 | 
			
		||||
        """Unset fields so they don't appear in the message body.
 | 
			
		||||
 | 
			
		||||
        :param except_list: A list of fields that won't be touched.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if except_list is None:
 | 
			
		||||
            except_list = []
 | 
			
		||||
 | 
			
		||||
        for k in self.as_dict():
 | 
			
		||||
            if k not in except_list:
 | 
			
		||||
                setattr(self, k, wsme.Unset)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										88
									
								
								teeth_agent/api/controllers/v1/command.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								teeth_agent/api/controllers/v1/command.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
# Copyright 2013 Red Hat, Inc.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import pecan
 | 
			
		||||
from pecan import rest
 | 
			
		||||
from wsme import types
 | 
			
		||||
from wsmeext import pecan as wsme_pecan
 | 
			
		||||
 | 
			
		||||
from teeth_agent.api.controllers.v1 import base
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandResult(base.APIBase):
 | 
			
		||||
    id = types.text
 | 
			
		||||
    command_name = types.text
 | 
			
		||||
    command_params = base.json_type
 | 
			
		||||
    command_status = types.text
 | 
			
		||||
    command_error = types.text
 | 
			
		||||
    command_result = types.text
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_result(cls, result):
 | 
			
		||||
        instance = cls()
 | 
			
		||||
        for field in ('id', 'command_name', 'command_params', 'command_status',
 | 
			
		||||
                      'command_error', 'command_result'):
 | 
			
		||||
            setattr(instance, field, getattr(result, field))
 | 
			
		||||
        return instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandResultList(base.APIBase):
 | 
			
		||||
    commands = [CommandResult]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_results(cls, results):
 | 
			
		||||
        instance = cls()
 | 
			
		||||
        instance.commands = [CommandResult.from_result(result)
 | 
			
		||||
                             for result in results]
 | 
			
		||||
        return instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Command(base.APIBase):
 | 
			
		||||
    """A command representation."""
 | 
			
		||||
    name = types.text
 | 
			
		||||
    params = base.json_type
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def deserialize(cls, obj):
 | 
			
		||||
        instance = cls()
 | 
			
		||||
        instance.name = obj['name']
 | 
			
		||||
        instance.params = obj['params']
 | 
			
		||||
        return instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandController(rest.RestController):
 | 
			
		||||
    """Controller for issuing commands and polling for command status."""
 | 
			
		||||
 | 
			
		||||
    @wsme_pecan.wsexpose(CommandResultList)
 | 
			
		||||
    def get_all(self):
 | 
			
		||||
        agent = pecan.request.agent
 | 
			
		||||
        results = agent.list_command_results()
 | 
			
		||||
        return CommandResultList.from_results(results)
 | 
			
		||||
 | 
			
		||||
    @wsme_pecan.wsexpose(CommandResult, types.text, types.text)
 | 
			
		||||
    def get_one(self, result_id, wait=False):
 | 
			
		||||
        agent = pecan.request.agent
 | 
			
		||||
        result = agent.get_command_result(result_id)
 | 
			
		||||
 | 
			
		||||
        #if wait and wait.lower() == 'true':
 | 
			
		||||
            #result.join()
 | 
			
		||||
 | 
			
		||||
        return CommandResult.from_result(result)
 | 
			
		||||
 | 
			
		||||
    @wsme_pecan.wsexpose(CommandResult, body=Command)
 | 
			
		||||
    def post(self, command):
 | 
			
		||||
        agent = pecan.request.agent
 | 
			
		||||
        result = agent.execute_command(command.name, **command.params)
 | 
			
		||||
        return result
 | 
			
		||||
@@ -112,7 +112,7 @@ class AsyncCommandResult(BaseCommandResult):
 | 
			
		||||
                e = errors.CommandExecutionError(str(e))
 | 
			
		||||
 | 
			
		||||
            with self.command_state_lock:
 | 
			
		||||
                self.command_error = e
 | 
			
		||||
                self.command_error = '{}: {}'.format(e.message, e.details)
 | 
			
		||||
                self.command_status = AgentCommandStatus.FAILED
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user