Browse Source

Applied cookiecutter template

Change-Id: Id860d5c4c97ae02c361c06e407a2e11cb3379783
changes/01/292801/8
Fabio Verboso 7 years ago
parent
commit
7fd6112605
  1. 4
      .gitreview
  2. 17
      CONTRIBUTING.rst
  3. 4
      HACKING.rst
  4. 30
      LICENSE
  5. 6
      MANIFEST.in
  6. 19
      README.rst
  7. 2
      babel.cfg
  8. 75
      doc/source/conf.py
  9. 4
      doc/source/contributing.rst
  10. 25
      doc/source/index.rst
  11. 12
      doc/source/installation.rst
  12. 1
      doc/source/readme.rst
  13. 7
      doc/source/usage.rst
  14. 7
      iotronic/__init__.py
  15. 4
      iotronic/api/__init__.py
  16. 20
      iotronic/api/app.py
  17. 4
      iotronic/api/config.py
  18. 4
      iotronic/api/controllers/base.py
  19. 53
      iotronic/api/controllers/v1/__init__.py
  20. 207
      iotronic/api/controllers/v1/__old/__init__.py
  21. 270
      iotronic/api/controllers/v1/__old/chassis.py
  22. 48
      iotronic/api/controllers/v1/__old/collection.py
  23. 210
      iotronic/api/controllers/v1/__old/driver.py
  24. 1104
      iotronic/api/controllers/v1/__old/node.py
  25. 396
      iotronic/api/controllers/v1/__old/port.py
  26. 34
      iotronic/api/controllers/v1/__old/state.py
  27. 239
      iotronic/api/controllers/v1/__old/types.py
  28. 107
      iotronic/api/controllers/v1/__old/utils.py
  29. 4
      iotronic/api/controllers/v1/collection.py
  30. 33
      iotronic/api/controllers/v1/location.py
  31. 130
      iotronic/api/controllers/v1/node.py
  32. 2
      iotronic/api/controllers/v1/utils.py
  33. 8
      iotronic/api/hooks.py
  34. 1
      iotronic/api/middleware/auth_token.py
  35. 3
      iotronic/api/middleware/parsable_error.py
  36. 2
      iotronic/common/config.py
  37. 3
      iotronic/common/config_generator/generator.py
  38. 26
      iotronic/common/disk_partitioner.py
  39. 4
      iotronic/common/exception.py
  40. 2
      iotronic/common/fsm.py
  41. 1
      iotronic/common/glance_service/base_image_service.py
  42. 5
      iotronic/common/image_service.py
  43. 2
      iotronic/common/images.py
  44. 8
      iotronic/common/paths.py
  45. 2
      iotronic/common/pxe_utils.py
  46. 20
      iotronic/common/rpc.py
  47. 2
      iotronic/common/service.py
  48. 2
      iotronic/common/states.py
  49. 2
      iotronic/common/utils.py
  50. 2195
      iotronic/conductor/__old/manager.py
  51. 362
      iotronic/conductor/__old/task_manager.py
  52. 160
      iotronic/conductor/__old/utils.py
  53. 115
      iotronic/conductor/manager.py
  54. 6
      iotronic/conductor/rpcapi.py
  55. 15
      iotronic/conductor/task_manager.py
  56. 23
      iotronic/db/api.py
  57. 2
      iotronic/db/sqlalchemy/alembic/versions/21b331f883ef_add_provision_updated_at.py
  58. 2
      iotronic/db/sqlalchemy/alembic/versions/2581ebaf0cb2_initial_migration.py
  59. 2
      iotronic/db/sqlalchemy/alembic/versions/3ae36a5f5131_add_logical_name.py
  60. 2
      iotronic/db/sqlalchemy/alembic/versions/4f399b21ae71_add_node_clean_step.py
  61. 2
      iotronic/db/sqlalchemy/alembic/versions/5674c57409b9_replace_nostate_with_available.py
  62. 48
      iotronic/db/sqlalchemy/api.py
  63. 29
      iotronic/db/sqlalchemy/models.py
  64. 26
      iotronic/objects/__init__.py
  65. 186
      iotronic/objects/__old/chassis.py
  66. 272
      iotronic/objects/__old/node.py
  67. 217
      iotronic/objects/__old/port.py
  68. 14
      iotronic/objects/base.py
  69. 20
      iotronic/objects/location.py
  70. 4
      iotronic/objects/node.py
  71. 26
      iotronic/objects/sessionwp.py
  72. 1
      iotronic/objects/utils.py
  73. 3
      iotronic/openstack/common/eventlet_backdoor.py
  74. 2
      iotronic/openstack/common/loopingcall.py
  75. 2
      iotronic/openstack/common/periodic_task.py
  76. 3
      iotronic/openstack/common/service.py
  77. 2
      iotronic/openstack/common/threadgroup.py
  78. 0
      iotronic/tests/__init__.py
  79. 23
      iotronic/tests/base.py
  80. 28
      iotronic/tests/test_iotronic.py
  81. 108
      iotronic/wamp/clientwamp.py
  82. 59
      iotronic/wamp/functions.py
  83. 91
      iotronic/wamp/rpcwampserver.py
  84. 5
      requirements.txt
  85. 46
      setup.cfg
  86. 110
      setup.py
  87. 163
      temp/clients_wamp_rw.py
  88. 147
      temp/test_autobahn_wamp_client.py
  89. 107
      temp/test_autobahn_ws_client.py
  90. 40
      temp/test_autobahn_ws_server.py
  91. 46
      temp/test_c.py
  92. 97
      temp/test_pub.py
  93. 14
      test-requirements.txt
  94. 60
      tox.ini
  95. 10
      utils/chat_wamp.py
  96. 20
      utils/rpc_test.py
  97. 6
      utils/wamp_rpc_server.py

4
.gitreview

@ -0,0 +1,4 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/iotronic.git

17
CONTRIBUTING.rst

@ -0,0 +1,17 @@
If you would like to contribute to the development of OpenStack, you must
follow the steps in this page:
http://docs.openstack.org/infra/manual/developers.html
If you already have a good understanding of how the system works and your
OpenStack accounts are set up, you can skip to the development workflow
section of this documentation to learn how changes to OpenStack should be
submitted for review via the Gerrit tool:
http://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad, not GitHub:
https://bugs.launchpad.net/iotronic

4
HACKING.rst

@ -0,0 +1,4 @@
iotronic Style Commandments
===============================================
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/

30
LICENSE

@ -1,4 +1,5 @@
Apache License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@ -173,30 +174,3 @@ Apache License
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.

6
MANIFEST.in

@ -0,0 +1,6 @@
include AUTHORS
include ChangeLog
exclude .gitignore
exclude .gitreview
global-exclude *.pyc

19
README.rst

@ -0,0 +1,19 @@
===============================
iotronic
===============================
IoTronic is an Internet of Things resource management service for OpenStack clouds.
Please feel here a long description which must be at least 3 lines wrapped on
80 cols, so that distribution package maintainers can use it in their packages.
Note that this is a hard requirement.
* Free software: Apache license
* Documentation: http://docs.openstack.org/developer/iotronic
* Source: http://git.openstack.org/cgit/openstack/iotronic
* Bugs: http://bugs.launchpad.net/iotronic
Features
--------
* TODO

2
babel.cfg

@ -0,0 +1,2 @@
[python: **.py]

75
doc/source/conf.py

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# 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 os
import sys
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'sphinx.ext.autodoc',
#'sphinx.ext.intersphinx',
'oslosphinx'
]
# autodoc generation is a bit aggressive and a nuisance when doing heavy
# text edit cycles.
# execute "export SPHINX_DEBUG=1" in your terminal to disable
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'iotronic'
copyright = u'2013, OpenStack Foundation'
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
# html_theme_path = ["."]
# html_theme = '_theme'
# html_static_path = ['static']
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).
latex_documents = [
('index',
'%s.tex' % project,
u'%s Documentation' % project,
u'OpenStack Foundation', 'manual'),
]
# Example configuration for intersphinx: refer to the Python standard library.
#intersphinx_mapping = {'http://docs.python.org/': None}

4
doc/source/contributing.rst

@ -0,0 +1,4 @@
============
Contributing
============
.. include:: ../../CONTRIBUTING.rst

25
doc/source/index.rst

@ -0,0 +1,25 @@
.. iotronic documentation master file, created by
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to iotronic's documentation!
========================================================
Contents:
.. toctree::
:maxdepth: 2
readme
installation
usage
contributing
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

12
doc/source/installation.rst

@ -0,0 +1,12 @@
============
Installation
============
At the command line::
$ pip install iotronic
Or, if you have virtualenvwrapper installed::
$ mkvirtualenv iotronic
$ pip install iotronic

1
doc/source/readme.rst

@ -0,0 +1 @@
.. include:: ../../README.rst

7
doc/source/usage.rst

@ -0,0 +1,7 @@
========
Usage
========
To use iotronic in a project::
import iotronic

7
iotronic/__init__.py

@ -13,10 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
import eventlet
import os
os.environ['EVENTLET_NO_GREENDNS'] = 'yes'
import eventlet
eventlet.monkey_patch(os=False)
os.environ['EVENTLET_NO_GREENDNS'] = 'yes'
eventlet.monkey_patch(os=False)

4
iotronic/api/__init__.py

@ -27,7 +27,7 @@ API_SERVICE_OPTS = [
default=1000,
help='The maximum number of items returned in a single '
'response from a collection resource.'),
]
]
CONF = cfg.CONF
@ -35,4 +35,4 @@ CONF = cfg.CONF
opt_group = cfg.OptGroup(name='api',
title='Options for the iotronic-api service')
CONF.register_group(opt_group)
CONF.register_opts(API_SERVICE_OPTS, opt_group)
CONF.register_opts(API_SERVICE_OPTS, opt_group)

20
iotronic/api/app.py

@ -25,16 +25,19 @@ from iotronic.api import middleware
api_opts = [
cfg.StrOpt('auth_strategy',
cfg.StrOpt(
'auth_strategy',
default='keystone',
help='Authentication strategy used by iotronic-api: one of "keystone" '
'or "noauth". "noauth" should not be used in a production '
'environment because all authentication will be disabled.'),
cfg.BoolOpt('pecan_debug',
default=False,
help=('Enable pecan debug mode. WARNING: this is insecure '
'and should not be used in a production environment.')),
]
'or "noauth". "noauth" should not be used in a production '
'environment because all authentication will be disabled.'),
cfg.BoolOpt(
'pecan_debug',
default=False,
help=(
'Enable pecan debug mode. WARNING: this is insecure '
'and should not be used in a production environment.')),
]
CONF = cfg.CONF
CONF.register_opts(api_opts)
@ -79,6 +82,7 @@ def setup_app(pecan_config=None, extra_hooks=None):
class VersionSelectorApplication(object):
def __init__(self):
pc = get_pecan_config()
pc.app.enable_acl = (CONF.auth_strategy == 'keystone')

4
iotronic/api/config.py

@ -30,8 +30,8 @@ app = {
'acl_public_routes': [
'/',
'/v1',
#'/v1/drivers/[a-z_]*/vendor_passthru/lookup',
#'/v1/nodes/[a-z0-9\-]+/vendor_passthru/heartbeat',
# '/v1/drivers/[a-z_]*/vendor_passthru/lookup',
# '/v1/nodes/[a-z0-9\-]+/vendor_passthru/heartbeat',
'/v1/nodes/[a-z0-9\-]',
],
}

4
iotronic/api/controllers/base.py

@ -70,8 +70,8 @@ class Version(object):
:param latest_version: version to use if latest is requested
:raises: webob.HTTPNotAcceptable
"""
(self.major, self.minor) = Version.parse_headers(headers,
default_version, latest_version)
(self.major, self.minor) = Version.parse_headers(
headers, default_version, latest_version)
def __repr__(self):
return '%s.%s' % (self.major, self.minor)

53
iotronic/api/controllers/v1/__init__.py

@ -16,24 +16,23 @@
Version 1 of the Iotronic API
"""
from iotronic.api.controllers import base
from iotronic.api.controllers import link
from iotronic.api.controllers.v1 import node
from iotronic.api import expose
from iotronic.common.i18n import _
import pecan
from pecan import rest
from webob import exc
from wsme import types as wtypes
from iotronic.api.controllers import link
from iotronic.api.controllers.v1 import node
'''
#from iotronic.api.controllers.v1 import chassis
#from iotronic.api.controllers.v1 import driver
# from iotronic.api.controllers.v1 import chassis
# from iotronic.api.controllers.v1 import driver
#from iotronic.api.controllers.v1 import port
# from iotronic.api.controllers.v1 import port
'''
from iotronic.api.controllers import base
from iotronic.api import expose
from iotronic.common.i18n import _
BASE_VERSION = 1
MIN_VER_STR = '1.0'
@ -56,7 +55,7 @@ class MediaType(base.APIBase):
def __init__(self, base, type):
self.base = base
self.type = type
'''
'''
class V1(base.APIBase):
@ -65,29 +64,29 @@ class V1(base.APIBase):
id = wtypes.text
"""The ID of the version, also acts as the release number"""
#media_types = [MediaType]
# media_types = [MediaType]
"""An array of supported media types for this version"""
#links = [link.Link]
# links = [link.Link]
"""Links that point to a specific URL for this version and documentation"""
#chassis = [link.Link]
# chassis = [link.Link]
"""Links to the chassis resource"""
nodes = [link.Link]
"""Links to the nodes resource"""
#ports = [link.Link]
# ports = [link.Link]
"""Links to the ports resource"""
#drivers = [link.Link]
# drivers = [link.Link]
"""Links to the drivers resource"""
@staticmethod
def convert():
v1 = V1()
v1.id = "v1"
v1.nodes = [link.Link.make_link('self', pecan.request.host_url,
'nodes', ''),
link.Link.make_link('bookmark',
@ -95,7 +94,7 @@ class V1(base.APIBase):
'nodes', '',
bookmark=True)
]
'''
v1.links = [link.Link.make_link('self', pecan.request.host_url,
'v1', '', bookmark=True),
@ -105,10 +104,10 @@ class V1(base.APIBase):
'api-spec-v1.html',
bookmark=True, type='text/html')
]
v1.media_types = [MediaType('application/json',
'application/vnd.openstack.iotronic.v1+json')]
v1.chassis = [link.Link.make_link('self', pecan.request.host_url,
'chassis', ''),
link.Link.make_link('bookmark',
@ -138,11 +137,11 @@ class V1(base.APIBase):
class Controller(rest.RestController):
"""Version 1 API controller root."""
nodes = node.NodesController()
#ports = port.PortsController()
#chassis = chassis.ChassisController()
#drivers = driver.DriversController()
# ports = port.PortsController()
# chassis = chassis.ChassisController()
# drivers = driver.DriversController()
@expose.expose(V1)
def get(self):
@ -159,8 +158,10 @@ class Controller(rest.RestController):
raise exc.HTTPNotAcceptable(_(
"Mutually exclusive versions requested. Version %(ver)s "
"requested but not supported by this service. The supported "
"version range is: [%(min)s, %(max)s].") % {'ver': version,
'min': MIN_VER_STR, 'max': MAX_VER_STR}, headers=headers)
"version range is: [%(min)s,%(max)s]."
) % {'ver': version, 'min': MIN_VER_STR,
'max': MAX_VER_STR},
headers=headers)
# ensure the minor version is within the supported range
if version < MIN_VER or version > MAX_VER:
raise exc.HTTPNotAcceptable(_(

207
iotronic/api/controllers/v1/__old/__init__.py

@ -1,207 +0,0 @@
# 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.
"""
Version 1 of the Iotronic API
"""
import pecan
from pecan import rest
from webob import exc
from wsme import types as wtypes
from iotronic.api.controllers import base
from iotronic.api.controllers import link
#from iotronic.api.controllers.v1 import chassis
#from iotronic.api.controllers.v1 import driver
from iotronic.api.controllers.v1 import node
from iotronic.api.controllers.v1 import board
#from iotronic.api.controllers.v1 import port
from iotronic.api import expose
from iotronic.common.i18n import _
BASE_VERSION = 1
# NOTE(deva): v1.0 is reserved to indicate Juno's API, but is not presently
# supported by the API service. All changes between Juno and the
# point where we added microversioning are considered backwards-
# compatible, but are not specifically discoverable at this time.
#
# The v1.1 version indicates this "initial" version as being
# different from Juno (v1.0), and includes the following changes:
#
# 827db7fe: Add Node.maintenance_reason
# 68eed82b: Add API endpoint to set/unset the node maintenance mode
# bc973889: Add sync and async support for passthru methods
# e03f443b: Vendor endpoints to support different HTTP methods
# e69e5309: Make vendor methods discoverable via the Iotronic API
# edf532db: Add logic to store the config drive passed by Nova
# v1.1: API at the point in time when microversioning support was added
MIN_VER_STR = '1.0'
# v1.2: Renamed NOSTATE ("None") to AVAILABLE ("available")
# v1.3: Add node.driver_internal_info
# v1.4: Add MANAGEABLE state
# v1.5: Add logical node names
# v1.6: Add INSPECT* states
MAX_VER_STR = '1.0'
MIN_VER = base.Version({base.Version.string: MIN_VER_STR},
MIN_VER_STR, MAX_VER_STR)
MAX_VER = base.Version({base.Version.string: MAX_VER_STR},
MIN_VER_STR, MAX_VER_STR)
class MediaType(base.APIBase):
"""A media type representation."""
base = wtypes.text
type = wtypes.text
def __init__(self, base, type):
self.base = base
self.type = type
class V1(base.APIBase):
"""The representation of the version 1 of the API."""
id = wtypes.text
"""The ID of the version, also acts as the release number"""
media_types = [MediaType]
"""An array of supported media types for this version"""
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"""
@staticmethod
def convert():
v1 = V1()
v1.id = "v1"
v1.links = [link.Link.make_link('self', pecan.request.host_url,
'v1', '', bookmark=True),
link.Link.make_link('describedby',
'http://docs.openstack.org',
'developer/iotronic/dev',
'api-spec-v1.html',
bookmark=True, type='text/html')
]
v1.media_types = [MediaType('application/json',
'application/vnd.openstack.iotronic.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()
boards= board.BoardsController()
@expose.expose(V1)
def get(self):
# NOTE: The reason why convert() it's being called for every
# request is because we need to get the host url from
# the request object to make the links.
return V1.convert()
def _check_version(self, version, headers=None):
if headers is None:
headers = {}
# ensure that major version in the URL matches the header
if version.major != BASE_VERSION:
raise exc.HTTPNotAcceptable(_(
"Mutually exclusive versions requested. Version %(ver)s "
"requested but not supported by this service. The supported "
"version range is: [%(min)s, %(max)s].") % {'ver': version,
'min': MIN_VER_STR, 'max': MAX_VER_STR}, headers=headers)
# ensure the minor version is within the supported range
if version < MIN_VER or version > MAX_VER:
raise exc.HTTPNotAcceptable(_(
"Version %(ver)s was requested but the minor version is not "
"supported by this service. The supported version range is: "
"[%(min)s, %(max)s].") % {'ver': version, 'min': MIN_VER_STR,
'max': MAX_VER_STR}, headers=headers)
@pecan.expose()
def _route(self, args):
v = base.Version(pecan.request.headers, MIN_VER_STR, MAX_VER_STR)
# Always set the min and max headers
pecan.response.headers[base.Version.min_string] = MIN_VER_STR
pecan.response.headers[base.Version.max_string] = MAX_VER_STR
# assert that requested version is supported
self._check_version(v, pecan.response.headers)
pecan.response.headers[base.Version.string] = str(v)
pecan.request.version = v
return super(Controller, self)._route(args)
__all__ = (Controller)

270
iotronic/api/controllers/v1/__old/chassis.py

@ -1,270 +0,0 @@
# 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 datetime
import pecan
from pecan import rest
import wsme
from wsme import types as wtypes
from iotronic.api.controllers import base
from iotronic.api.controllers import link
from iotronic.api.controllers.v1 import collection
from iotronic.api.controllers.v1 import node
from iotronic.api.controllers.v1 import types
from iotronic.api.controllers.v1 import utils as api_utils
from iotronic.api import expose
from iotronic.common import exception
from iotronic.common.i18n import _
from iotronic import objects
class ChassisPatchType(types.JsonPatchType):
pass
class Chassis(base.APIBase):
"""API representation of a chassis.
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of
a chassis.
"""
uuid = types.uuid
"""The UUID of the chassis"""
description = wtypes.text
"""The description of the chassis"""
extra = {wtypes.text: types.jsontype}
"""The metadata of the chassis"""
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing a self link and associated chassis links"""
nodes = wsme.wsattr([link.Link], readonly=True)
"""Links to the collection of nodes contained in this chassis"""
def __init__(self, **kwargs):
self.fields = []
for field in objects.Chassis.fields:
# Skip fields we do not expose.
if not hasattr(self, field):
continue
self.fields.append(field)
setattr(self, field, kwargs.get(field, wtypes.Unset))
@staticmethod
def _convert_with_links(chassis, url, expand=True):
if not expand:
chassis.unset_fields_except(['uuid', 'description'])
else:
chassis.nodes = [link.Link.make_link('self',
url,
'chassis',
chassis.uuid + "/nodes"),
link.Link.make_link('bookmark',
url,
'chassis',
chassis.uuid + "/nodes",
bookmark=True)
]
chassis.links = [link.Link.make_link('self',
url,
'chassis', chassis.uuid),
link.Link.make_link('bookmark',
url,
'chassis', chassis.uuid,
bookmark=True)
]
return chassis
@classmethod
def convert_with_links(cls, rpc_chassis, expand=True):
chassis = Chassis(**rpc_chassis.as_dict())
return cls._convert_with_links(chassis, pecan.request.host_url,
expand)
@classmethod
def sample(cls, expand=True):
time = datetime.datetime(2000, 1, 1, 12, 0, 0)
sample = cls(uuid='eaaca217-e7d8-47b4-bb41-3f99f20eed89', extra={},
description='Sample chassis', created_at=time,
updated_at=time)
return cls._convert_with_links(sample, 'http://localhost:6385',
expand)
class ChassisCollection(collection.Collection):
"""API representation of a collection of chassis."""
chassis = [Chassis]
"""A list containing chassis objects"""
def __init__(self, **kwargs):
self._type = 'chassis'
@staticmethod
def convert_with_links(chassis, limit, url=None, expand=False, **kwargs):
collection = ChassisCollection()
collection.chassis = [Chassis.convert_with_links(ch, expand)
for ch in chassis]
url = url or None
collection.next = collection.get_next(limit, url=url, **kwargs)
return collection
@classmethod
def sample(cls, expand=True):
sample = cls()
sample.chassis = [Chassis.sample(expand=False)]
return sample
class ChassisController(rest.RestController):
"""REST controller for Chassis."""
nodes = node.NodesController()
"""Expose nodes as a sub-element of chassis"""
# Set the flag to indicate that the requests to this resource are
# coming from a top-level resource
nodes.from_chassis = True
_custom_actions = {
'detail': ['GET'],
}
invalid_sort_key_list = ['extra']
def _get_chassis_collection(self, marker, limit, sort_key, sort_dir,
expand=False, resource_url=None):
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
marker_obj = objects.Chassis.get_by_uuid(pecan.request.context,
marker)
if sort_key in self.invalid_sort_key_list:
raise exception.InvalidParameterValue(_(
"The sort_key value %(key)s is an invalid field for sorting")
% {'key': sort_key})
chassis = objects.Chassis.list(pecan.request.context, limit,
marker_obj, sort_key=sort_key,
sort_dir=sort_dir)
return ChassisCollection.convert_with_links(chassis, limit,
url=resource_url,
expand=expand,
sort_key=sort_key,
sort_dir=sort_dir)
@expose.expose(ChassisCollection, types.uuid,
int, wtypes.text, wtypes.text)
def get_all(self, marker=None, limit=None, sort_key='id', sort_dir='asc'):
"""Retrieve a list of chassis.
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
"""
return self._get_chassis_collection(marker, limit, sort_key, sort_dir)
@expose.expose(ChassisCollection, types.uuid, int,
wtypes.text, wtypes.text)
def detail(self, marker=None, limit=None, sort_key='id', sort_dir='asc'):
"""Retrieve a list of chassis with detail.
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
"""
# /detail should only work against collections
parent = pecan.request.path.split('/')[:-1][-1]
if parent != "chassis":
raise exception.HTTPNotFound
expand = True
resource_url = '/'.join(['chassis', 'detail'])
return self._get_chassis_collection(marker, limit, sort_key, sort_dir,
expand, resource_url)
@expose.expose(Chassis, types.uuid)
def get_one(self, chassis_uuid):
"""Retrieve information about the given chassis.
:param chassis_uuid: UUID of a chassis.
"""
rpc_chassis = objects.Chassis.get_by_uuid(pecan.request.context,
chassis_uuid)
return Chassis.convert_with_links(rpc_chassis)
@expose.expose(Chassis, body=Chassis, status_code=201)
def post(self, chassis):
"""Create a new chassis.
:param chassis: a chassis within the request body.
"""
new_chassis = objects.Chassis(pecan.request.context,
**chassis.as_dict())
new_chassis.create()
# Set the HTTP Location Header
pecan.response.location = link.build_url('chassis', new_chassis.uuid)
return Chassis.convert_with_links(new_chassis)
@wsme.validate(types.uuid, [ChassisPatchType])
@expose.expose(Chassis, types.uuid, body=[ChassisPatchType])
def patch(self, chassis_uuid, patch):
"""Update an existing chassis.
:param chassis_uuid: UUID of a chassis.
:param patch: a json PATCH document to apply to this chassis.
"""
rpc_chassis = objects.Chassis.get_by_uuid(pecan.request.context,
chassis_uuid)
try:
chassis = Chassis(**api_utils.apply_jsonpatch(
rpc_chassis.as_dict(), patch))
except api_utils.JSONPATCH_EXCEPTIONS as e:
raise exception.PatchError(patch=patch, reason=e)
# Update only the fields that have changed
for field in objects.Chassis.fields:
try:
patch_val = getattr(chassis, field)
except AttributeError:
# Ignore fields that aren't exposed in the API
continue
if patch_val == wtypes.Unset:
patch_val = None
if rpc_chassis[field] != patch_val:
rpc_chassis[field] = patch_val
rpc_chassis.save()
return Chassis.convert_with_links(rpc_chassis)
@expose.expose(None, types.uuid, status_code=204)
def delete(self, chassis_uuid):
"""Delete a chassis.
:param chassis_uuid: UUID of a chassis.
"""
rpc_chassis = objects.Chassis.get_by_uuid(pecan.request.context,
chassis_uuid)
rpc_chassis.destroy()

48
iotronic/api/controllers/v1/__old/collection.py

@ -1,48 +0,0 @@
# 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 wsme import types as wtypes
from iotronic.api.controllers import base
from iotronic.api.controllers import link
class Collection(base.APIBase):
next = wtypes.text
"""A link to retrieve the next subset of the collection"""
@property
def collection(self):
return getattr(self, self._type)
def has_next(self, limit):
"""Return whether collection has more items."""
return len(self.collection) and len(self.collection) == limit
def get_next(self, limit, url=None, **kwargs):
"""Return a link to the next subset of the collection."""
if not self.has_next(limit):
return wtypes.Unset
resource_url = url or self._type
q_args = ''.join(['%s=%s&' % (key, kwargs[key]) for key in kwargs])
next_args = '?%(args)slimit=%(limit)d&marker=%(marker)s' % {
'args': q_args, 'limit': limit,
'marker': self.collection[-1].uuid}
return link.Link.make_link('next', pecan.request.host_url,
resource_url, next_args).href

210
iotronic/api/controllers/v1/__old/driver.py

@ -1,210 +0,0 @@
# 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
import wsme
from wsme import types as wtypes
from iotronic.api.controllers import base
from iotronic.api.controllers import link
from iotronic.api import expose
from iotronic.common import exception
from iotronic.common.i18n import _
# Property information for drivers:
# key = driver name;
# value = dictionary of properties of that driver:
# key = property name.
# value = description of the property.
# NOTE(rloo). This is cached for the lifetime of the API service. If one or
# more conductor services are restarted with new driver versions, the API
# service should be restarted.
_DRIVER_PROPERTIES = {}
# Vendor information for drivers:
# key = driver name;
# value = dictionary of vendor methods of that driver:
# key = method name.
# value = dictionary with the metadata of that method.
# NOTE(lucasagomes). This is cached for the lifetime of the API
# service. If one or more conductor services are restarted with new driver
# versions, the API service should be restarted.
_VENDOR_METHODS = {}
class Driver(base.APIBase):
"""API representation of a driver."""
name = wtypes.text
"""The name of the driver"""
hosts = [wtypes.text]
"""A list of active conductors that support this driver"""
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing self and bookmark links"""
@staticmethod
def convert_with_links(name, hosts):
driver = Driver()
driver.name = name
driver.hosts = hosts
driver.links = [
link.Link.make_link('self',
pecan.request.host_url,
'drivers', name),
link.Link.make_link('bookmark',
pecan.request.host_url,
'drivers', name,
bookmark=True)
]
return driver
@classmethod
def sample(cls):
sample = cls(name="sample-driver",
hosts=["fake-host"])
return sample
class DriverList(base.APIBase):
"""API representation of a list of drivers."""
drivers = [Driver]
"""A list containing drivers objects"""
@staticmethod
def convert_with_links(drivers):
collection = DriverList()
collection.drivers = [
Driver.convert_with_links(dname, list(drivers[dname]))
for dname in drivers]
return collection
@classmethod
def sample(cls):
sample = cls()
sample.drivers = [Driver.sample()]
return sample
class DriverPassthruController(rest.RestController):
"""REST controller for driver passthru.
This controller allow vendors to expose cross-node functionality in the
Iotronic API. Iotronic will merely relay the message from here to the specified
driver, no introspection will be made in the message body.
"""
_custom_actions = {
'methods': ['GET']
}
@expose.expose(wtypes.text, wtypes.text)
def methods(self, driver_name):
"""Retrieve information about vendor methods of the given driver.
:param driver_name: name of the driver.
:returns: dictionary with <vendor method name>:<method metadata>
entries.
:raises: DriverNotFound if the driver name is invalid or the
driver cannot be loaded.
"""
if driver_name not in _VENDOR_METHODS:
topic = pecan.request.rpcapi.get_topic_for_driver(driver_name)
ret = pecan.request.rpcapi.get_driver_vendor_passthru_methods(
pecan.request.context, driver_name, topic=topic)
_VENDOR_METHODS[driver_name] = ret
return _VENDOR_METHODS[driver_name]
@expose.expose(wtypes.text, wtypes.text, wtypes.text,
body=wtypes.text)
def _default(self, driver_name, method, data=None):
"""Call a driver API extension.
:param driver_name: name of the driver to call.
:param method: name of the method, to be passed to the vendor
implementation.
:param data: body of data to supply to the specified method.
"""
if not method:
raise wsme.exc.ClientSideError(_("Method not specified"))
if data is None:
data = {}
http_method = pecan.request.method.upper()
topic = pecan.request.rpcapi.get_topic_for_driver(driver_name)
ret, is_async = pecan.request.rpcapi.driver_vendor_passthru(
pecan.request.context, driver_name, method,
http_method, data, topic=topic)
status_code = 202 if is_async else 200
return wsme.api.Response(ret, status_code=status_code)
class DriversController(rest.RestController):
"""REST controller for Drivers."""
vendor_passthru = DriverPassthruController()
_custom_actions = {
'properties': ['GET'],
}
@expose.expose(DriverList)
def get_all(self):
"""Retrieve a list of drivers."""
# FIXME(deva): formatting of the auto-generated REST API docs
# will break from a single-line doc string.
# This is a result of a bug in sphinxcontrib-pecanwsme
# https://github.com/dreamhost/sphinxcontrib-pecanwsme/issues/8
driver_list = pecan.request.dbapi.get_active_driver_dict()
return DriverList.convert_with_links(driver_list)
@expose.expose(Driver, wtypes.text)
def get_one(self, driver_name):
"""Retrieve a single driver."""
# NOTE(russell_h): There is no way to make this more efficient than
# retrieving a list of drivers using the current sqlalchemy schema, but
# this path must be exposed for Pecan to route any paths we might
# choose to expose below it.
driver_dict = pecan.request.dbapi.get_active_driver_dict()
for name, hosts in driver_dict.items():
if name == driver_name:
return Driver.convert_with_links(name, list(hosts))
raise exception.DriverNotFound(driver_name=driver_name)
@expose.expose(wtypes.text, wtypes.text)
def properties(self, driver_name):
"""Retrieve property information of the given driver.
:param driver_name: name of the driver.
:returns: dictionary with <property name>:<property description>
entries.
:raises: DriverNotFound (HTTP 404) if the driver name is invalid or
the driver cannot be loaded.
"""
if driver_name not in _DRIVER_PROPERTIES:
topic = pecan.request.rpcapi.get_topic_for_driver(driver_name)
properties = pecan.request.rpcapi.get_driver_properties(
pecan.request.context, driver_name, topic=topic)
_DRIVER_PROPERTIES[driver_name] = properties
return _DRIVER_PROPERTIES[driver_name]

1104
iotronic/api/controllers/v1/__old/node.py

File diff suppressed because it is too large Load Diff

396
iotronic/api/controllers/v1/__old/port.py

@ -1,396 +0,0 @@
# Copyright 2013 UnitedStack 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 datetime
from oslo_utils import uuidutils
import pecan
from pecan import rest
import wsme
from wsme import types as wtypes
from iotronic.api.controllers import base
from iotronic.api.controllers import link
from iotronic.api.controllers.v1 import collection
from iotronic.api.controllers.v1 import types
from iotronic.api.controllers.v1 import utils as api_utils
from iotronic.api import expose
from iotronic.common import exception
from iotronic.common.i18n import _
from iotronic import objects
class PortPatchType(types.JsonPatchType):
@staticmethod
def mandatory_attrs():
return ['/address', '/node_uuid']
class Port(base.APIBase):
"""API representation of a port.
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of a port.
"""
_node_uuid = None
def _get_node_uuid(self):
return self._node_uuid
def _set_node_uuid(self, value):
if value and self._node_uuid != value:
try:
# FIXME(comstud): One should only allow UUID here, but
# there seems to be a bug in that tests are passing an
# ID. See bug #1301046 for more details.
node = objects.Node.get(pecan.request.context, value)
self._node_uuid = node.uuid
# NOTE(lucasagomes): Create the node_id attribute on-the-fly
# to satisfy the api -> rpc object
# conversion.
self.node_id = node.id
except exception.NodeNotFound as e:
# Change error code because 404 (NotFound) is inappropriate
# response for a POST request to create a Port
e.code = 400 # BadRequest
raise e
elif value == wtypes.Unset:
self._node_uuid = wtypes.Unset
uuid = types.uuid
"""Unique UUID for this port"""
address = wsme.wsattr(types.macaddress, mandatory=True)
"""MAC Address for this port"""
extra = {wtypes.text: types.jsontype}
"""This port's meta data"""
node_uuid = wsme.wsproperty(types.uuid, _get_node_uuid, _set_node_uuid,
mandatory=True)
"""The UUID of the node this port belongs to"""
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing a self link and associated port links"""
def __init__(self, **kwargs):
self.fields = []
fields = list(objects.Port.fields)
# NOTE(lucasagomes): node_uuid is not part of objects.Port.fields
# because it's an API-only attribute
fields.append('node_uuid')
for field in fields:
# Skip fields we do not expose.
if not hasattr(self, field):
continue
self.fields.append(field)
setattr(self, field, kwargs.get(field, wtypes.Unset))
# NOTE(lucasagomes): node_id is an attribute created on-the-fly
# by _set_node_uuid(), it needs to be present in the fields so
# that as_dict() will contain node_id field when converting it
# before saving it in the database.
self.fields.append('node_id')
setattr(self, 'node_uuid', kwargs.get('node_id', wtypes.Unset))
@staticmethod
def _convert_with_links(port, url, expand=True):
if not expand:
port.unset_fields_except(['uuid', 'address'])
# never expose the node_id attribute
port.node_id = wtypes.Unset
port.links = [link.Link.make_link('self', url,
'ports', port.uuid),
link.Link.make_link('bookmark', url,
'ports', port.uuid,
bookmark=True)
]
return port