diff --git a/ironic_python_agent/api/app.py b/ironic_python_agent/api/app.py index 40b0f5f50..5b5601028 100644 --- a/ironic_python_agent/api/app.py +++ b/ironic_python_agent/api/app.py @@ -19,6 +19,7 @@ from ironic_python_agent.api import config class AgentHook(hooks.PecanHook): + """Hook to attach agent instance to API requests.""" def __init__(self, agent, *args, **kwargs): super(AgentHook, self).__init__(*args, **kwargs) self.agent = agent @@ -28,12 +29,22 @@ class AgentHook(hooks.PecanHook): def get_pecan_config(): - # Set up the pecan configuration + """Set up the pecan configuration. + + :returns: pecan configuration object. + """ filename = config.__file__.replace('.pyc', '.py') return pecan.configuration.conf_from_file(filename) -def setup_app(pecan_config=None, extra_hooks=None, agent=None): +def setup_app(pecan_config=None, agent=None): + """Set up the API app. + + :param pecan_config: a pecan configuration object. + :param agent: an :class:`ironic_python_agent.agent.IronicPythonAgent` + instance. + :returns: wsgi app object. + """ app_hooks = [AgentHook(agent)] if not pecan_config: @@ -53,6 +64,8 @@ def setup_app(pecan_config=None, extra_hooks=None, agent=None): class VersionSelectorApplication(object): + """WSGI application that handles multiple API versions.""" + def __init__(self, agent): pc = get_pecan_config() self.v1 = setup_app(pecan_config=pc, agent=agent) diff --git a/ironic_python_agent/api/controllers/v1/command.py b/ironic_python_agent/api/controllers/v1/command.py index 26cc82ac7..e4483e292 100644 --- a/ironic_python_agent/api/controllers/v1/command.py +++ b/ironic_python_agent/api/controllers/v1/command.py @@ -22,6 +22,8 @@ from ironic_python_agent.api.controllers.v1 import base class CommandResult(base.APIBase): + """Object representing the result of a given command.""" + id = types.text command_name = types.text command_params = types.DictType(types.text, base.json_type) @@ -31,6 +33,13 @@ class CommandResult(base.APIBase): @classmethod def from_result(cls, result): + """Convert a BaseCommandResult object to a CommandResult object. + + :param result: a :class:`ironic_python_agent.extensions.base. + BaseCommandResult` object. + :returns: a :class:`ironic_python_agent.api.controllers.v1.command. + CommandResult` object. + """ instance = cls() for field in ('id', 'command_name', 'command_params', 'command_status', 'command_error', 'command_result'): @@ -39,10 +48,18 @@ class CommandResult(base.APIBase): class CommandResultList(base.APIBase): + """An object representing a list of CommandResult objects.""" commands = [CommandResult] @classmethod def from_results(cls, results): + """Convert a list of BaseCommandResult objects to a CommandResultList. + + :param results: a list of :class:`ironic_python_agent.extensions.base. + BaseCommandResult` objects. + :returns: a :class:`ironic_python_agent.api.controllers.v1.command. + CommandResultList` object. + """ instance = cls() instance.commands = [CommandResult.from_result(result) for result in results] @@ -50,7 +67,7 @@ class CommandResultList(base.APIBase): class Command(base.APIBase): - """A command representation.""" + """A representation of a command.""" name = types.wsattr(types.text, mandatory=True) params = types.wsattr(base.MultiType(dict), mandatory=True) @@ -60,12 +77,20 @@ class CommandController(rest.RestController): @wsme_pecan.wsexpose(CommandResultList) def get_all(self): + """Get all command results.""" 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=None): + """Get a command result by ID. + + :param result_id: the ID of the result to get. + :param wait: if 'true', block until the command completes. + :returns: a :class:`ironic_python_agent.api.controller.v1.command. + CommandResult` object. + """ agent = pecan.request.agent result = agent.get_command_result(result_id) @@ -76,6 +101,14 @@ class CommandController(rest.RestController): @wsme_pecan.wsexpose(CommandResult, types.text, body=Command) def post(self, wait=None, command=None): + """Post a command for the agent to run. + + :param wait: if 'true', block until the command completes. + :param command: the command to execute. If None, an InvalidCommandError + will be returned. + :returns: a :class:`ironic_python_agent.api.controller.v1.command. + CommandResult` object. + """ # the POST body is always the last arg, # so command must be a kwarg here if command is None: diff --git a/ironic_python_agent/api/controllers/v1/status.py b/ironic_python_agent/api/controllers/v1/status.py index 0c7c152b7..84efc6a49 100644 --- a/ironic_python_agent/api/controllers/v1/status.py +++ b/ironic_python_agent/api/controllers/v1/status.py @@ -22,11 +22,20 @@ from ironic_python_agent.api.controllers.v1 import base class AgentStatus(base.APIBase): + """An object representing an agent instance's status.""" + started_at = base.MultiType(float) version = types.text @classmethod def from_agent_status(cls, status): + """Convert an object representing agent status to an AgentStatus. + + :param status: An :class:`ironic_python_agent.agent. + IronicPythonAgentStatus` object. + :returns: An :class:`ironic_python_agent.api.controllers.v1.status. + AgentStatus` object. + """ instance = cls() for field in ('started_at', 'version'): setattr(instance, field, getattr(status, field)) @@ -38,6 +47,7 @@ class StatusController(rest.RestController): @wsme_pecan.wsexpose(AgentStatus) def get_all(self): + """Get current status of the running agent.""" agent = pecan.request.agent status = agent.get_status() return AgentStatus.from_agent_status(status)