diff --git a/.coveragerc b/.coveragerc index 5db8dbbc188..a116ffae945 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,7 +1,7 @@ [run] branch = True source = nova -omit = nova/tests/*,nova/openstack/* +omit = nova/tests/* [report] ignore_errors = True diff --git a/.gitignore b/.gitignore index 4ae26157bc6..941cdc6ad9f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,12 +26,16 @@ CA/ ChangeLog coverage.xml cover/* -covhtml +covhtml/* dist/* doc/source/api/* doc/build/* api-guide/build/* +api-ref/build/* +placement-api-ref/build/* etc/nova/nova.conf.sample +etc/nova/policy.yaml.sample +etc/nova/policy.yaml.merged instances keeper keys @@ -42,6 +46,7 @@ nova/tests/cover/* nova/vcsversion.py tools/conf/nova.conf* doc/source/_static/nova.conf.sample +doc/source/_static/nova.policy.yaml.sample # Files created by releasenotes build releasenotes/build diff --git a/.gitreview b/.gitreview index 7311bb645ce..71fc1d345c7 100644 --- a/.gitreview +++ b/.gitreview @@ -2,4 +2,4 @@ host=review.openstack.org port=29418 project=openstack/nova.git -defaultbranch=stable/mitaka +defaultbranch=stable/pike diff --git a/.testr.conf b/.testr.conf index 6060451ed5c..2e95b830543 100644 --- a/.testr.conf +++ b/.testr.conf @@ -6,3 +6,13 @@ test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ test_id_option=--load-list $IDFILE test_list_option=--list +# NOTE(cdent): The group_regex describes how testrepository will +# group tests into the same process when running concurently. The +# following insures that gabbi tests coming from the same YAML file +# are all in the same process. This is important because each YAML +# file represents an ordered sequence of HTTP requests. Note that +# tests which do not match this regex will not be grouped in any +# special way. See the following for more details. +# http://testrepository.readthedocs.io/en/latest/MANUAL.html#grouping-tests +# https://gabbi.readthedocs.io/en/latest/#purpose +group_regex=nova\.tests\.functional\.api\.openstack\.placement\.test_placement_api(?:\.|_)([^_]+) diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 00000000000..773e220bffd --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,46 @@ +- job: + name: nova-tox-functional + parent: openstack-tox + description: | + Run tox-based functional tests for the OpenStack Nova project with Nova + specific irrelevant-files list. Uses tox with the ``functional`` + environment. + irrelevant-files: + - ^.*\.rst$ + - ^api-.*$ + - ^doc/source/.*$ + - ^nova/locale/.*$ + - ^placement-api-ref/.*$ + - ^releasenotes/.*$ + vars: + tox_envlist: functional + timeout: 3600 + +- job: + name: nova-tox-functional-py35 + parent: openstack-tox + description: | + Run tox-based functional tests for the OpenStack Nova project + under cPython version 3.5. with Nova specific irrelevant-files list. + Uses tox with the ``functional-py35`` environment. + irrelevant-files: + - ^.*\.rst$ + - ^api-.*$ + - ^doc/source/.*$ + - ^nova/locale/.*$ + - ^placement-api-ref/.*$ + - ^releasenotes/.*$ + vars: + tox_envlist: functional-py35 + timeout: 3600 + +- project: + name: openstack/nova + check: + jobs: + - nova-tox-functional + - nova-tox-functional-py35 + gate: + jobs: + - nova-tox-functional + - nova-tox-functional-py35 diff --git a/HACKING.rst b/HACKING.rst index 4fab4678274..f0c4ccfbd85 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -21,13 +21,10 @@ Nova Specific Commandments should be moved into a common module - [N313] capitalize help string Config parameter help strings should have a capitalized first letter -- [N314] vim configuration should not be kept in source files. - [N316] Change assertTrue(isinstance(A, B)) by optimal assert like assertIsInstance(A, B). - [N317] Change assertEqual(type(A), B) by optimal assert like assertIsInstance(A, B) -- [N318] Change assertEqual(A, None) or assertEqual(None, A) by optimal assert like - assertIsNone(A) - [N319] Validate that debug level logs are not translated. - [N320] Setting CONF.* attributes directly in tests is forbidden. Use self.flags(option=value) instead. @@ -37,6 +34,7 @@ Nova Specific Commandments - [N324] Ensure that jsonutils.%(fun)s must be used instead of json.%(fun)s - [N325] str() and unicode() cannot be used on an exception. Remove use or use six.text_type() - [N326] Translated messages cannot be concatenated. String should be included in translated message. +- [N327] Do not use xrange(). xrange() is not compatible with Python 3. Use range() or six.moves.range() instead. - [N328] Validate that LOG.info messages use _LI. - [N329] Validate that LOG.exception messages use _LE. - [N330] Validate that LOG.warning and LOG.warn messages use _LW. @@ -57,8 +55,17 @@ Nova Specific Commandments - [N344] Python 3: do not use dict.iteritems. - [N345] Python 3: do not use dict.iterkeys. - [N346] Python 3: do not use dict.itervalues. -- [N347] Provide enough help text for config options - [N348] Deprecated library function os.popen() +- [N349] Check for closures in tests which are not used +- [N350] Policy registration should be in the central location ``nova/policies/`` +- [N351] Do not use the oslo_policy.policy.Enforcer.enforce() method. +- [N352] LOG.warn is deprecated. Enforce use of LOG.warning. +- [N353] Validate that context objects is not passed in logging calls. +- [N355] Enforce use of assertTrue/assertFalse +- [N356] Enforce use of assertIs/assertIsNot +- [N357] Use oslo_utils.uuidutils or uuidsentinel(in case of test cases) to + generate UUID instead of uuid4(). +- [N358] Return must always be followed by a space when returning a value. Creating Unit Tests ------------------- @@ -119,8 +126,8 @@ need with:: apt-get install texlive-full imagemagick -Then you can then use the ``build_latex_pdf.sh`` script in tools/ to take care -of both the the sphinx latex generation and the latex compilation. For example:: +Then you can use the ``build_latex_pdf.sh`` script in tools/ to take care +of both the sphinx latex generation and the latex compilation. For example:: tools/build_latex_pdf.sh diff --git a/README.rst b/README.rst index 317ab1e00ae..96dcb1cce0b 100644 --- a/README.rst +++ b/README.rst @@ -1,61 +1,74 @@ -OpenStack Nova README -===================== +======================== +Team and repository tags +======================== -OpenStack Nova provides a cloud computing fabric controller, -supporting a wide variety of virtualization technologies, -including KVM, Xen, LXC, VMware, and more. In addition to -its native API, it includes compatibility with the commonly -encountered Amazon EC2 and S3 APIs. +.. image:: https://governance.openstack.org/badges/nova.svg + :target: https://governance.openstack.org/reference/tags/index.html -OpenStack Nova is distributed under the terms of the Apache -License, Version 2.0. The full terms and conditions of this -license are detailed in the LICENSE file. +.. Change things from this point on -Nova primarily consists of a set of Python daemons, though -it requires and integrates with a number of native system -components for databases, messaging and virtualization -capabilities. +OpenStack Nova +============== -To keep updated with new developments in the OpenStack project -follow `@openstack `_ on Twitter. +OpenStack Nova provides a cloud computing fabric controller, supporting a wide +variety of compute technologies, including: libvirt (KVM, Xen, LXC and more), +Hyper-V, VMware, XenServer, OpenStack Ironic and PowerVM. -To learn how to deploy OpenStack Nova, consult the documentation -available online at: +Use the following resources to learn more. + +API +--- + +To learn how to use Nova's API, consult the documentation available online at: + +- `Compute API Guide `__ +- `Compute API Reference `__ - http://docs.openstack.org +For more information on OpenStack APIs, SDKs and CLIs in general, refer to: -For information about the different compute (hypervisor) drivers -supported by Nova, read this page on the wiki: +- `OpenStack for App Developers `__ +- `Development resources for OpenStack clouds + `__ - https://wiki.openstack.org/wiki/HypervisorSupportMatrix +Operators +--------- -In the unfortunate event that bugs are discovered, they should -be reported to the appropriate bug tracker. If you obtained -the software from a 3rd party operating system vendor, it is -often wise to use their own bug tracker for reporting problems. -In all other cases use the master OpenStack bug tracker, +To learn how to deploy and configure OpenStack Nova, consult the documentation +available online at: + +- `OpenStack Nova `__ + +In the unfortunate event that bugs are discovered, they should be reported to +the appropriate bug tracker. If you obtained the software from a 3rd party +operating system vendor, it is often wise to use their own bug tracker for +reporting problems. In all other cases use the master OpenStack bug tracker, available at: - http://bugs.launchpad.net/nova +- `Bug Tracker `__ + +Developers +---------- -Developers wishing to work on the OpenStack Nova project should -always base their work on the latest Nova code, available from -the master GIT repository at: +For information on how to contribute to Nova, please see the contents of the +CONTRIBUTING.rst. - https://git.openstack.org/cgit/openstack/nova +Any new code must follow the development guidelines detailed in the HACKING.rst +file, and pass all unit tests. -Developers should also join the discussion on the mailing list, -at: +Further developer focused documentation is available at: - http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev +- `Official Nova Documentation `__ +- `Official Client Documentation + `__ -Any new code must follow the development guidelines detailed -in the HACKING.rst file, and pass all unit tests. Further -developer focused documentation is available at: +Other Information +----------------- - http://docs.openstack.org/developer/nova/ +During each `Summit`_ and `Project Team Gathering`_, we agree on what the whole +community wants to focus on for the upcoming release. The plans for nova can +be found at: -For information on how to contribute to Nova, please see the -contents of the CONTRIBUTING.rst file. +- `Nova Specs `__ --- End of broadcast +.. _Summit: https://www.openstack.org/summit/ +.. _Project Team Gathering: https://www.openstack.org/ptg/ diff --git a/api-guide/source/conf.py b/api-guide/source/conf.py index 5ced3d29736..40151861792 100644 --- a/api-guide/source/conf.py +++ b/api-guide/source/conf.py @@ -1,5 +1,3 @@ -# -*- 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 @@ -88,7 +86,7 @@ # source tree pwd = subprocess.Popen( - "pwd", stdout=subprocess.PIPE).communicate()[0].strip('\n') + "pwd", stdout=subprocess.PIPE).communicate()[0].decode().strip('\n') # html_context allows us to pass arbitrary values into the html template html_context = {"pwd": pwd, diff --git a/api-guide/source/faults.rst b/api-guide/source/faults.rst index 29bb677013e..c75327b1638 100644 --- a/api-guide/source/faults.rst +++ b/api-guide/source/faults.rst @@ -4,20 +4,65 @@ Faults This doc explains how to understand what has happened to your API request. -Every HTTP request has a status code. 2xx codes signify the API was a success. -However, that is often not the end of the story. That generally only means the -request to start the operation has been accepted. It does not mean the action -you requested has successfully completed. +Every HTTP request has a status code. 2xx codes signify the API call was a +success. However, that is often not the end of the story. That generally only +means the request to start the operation has been accepted. It does not mean +the action you requested has successfully completed. Tracking Errors by Request ID ============================= -Every request made has a unique Request ID. -This is returned in a response header. -Here is an example response header: +There are two types of request ID. -X-Compute-Request-ID: req-4b9e5c04-c40f-4b4f-960e-6ac0858dca6c +.. list-table:: + :header-rows: 1 + :widths: 2,8 + + * - Type + - Description + * - Local request ID + - Locally generated unique request ID by each service and different between + all services (Nova, Cinder, Glance, Neutron, etc.) involved + in that operation. The format is ``req-`` + UUID (UUID4). + * - Global request ID + - User specified request ID which is utilized as common identifier + by all services (Nova, Cinder, Glance, Neutron, etc.) involved + in that operation. This request ID is same among all services involved + in that operation. + The format is ``req-`` + UUID (UUID4). + +It is extremely common for clouds to have an ELK (Elastic Search, Logstash, +Kibana) infrastructure consuming their logs. +The only way to query these flows is if there is a common identifier across +all relevant messages. The global request ID immediately makes existing +deployed tooling better for managing OpenStack. + +**Request Header** + +In each REST API request, you can specify the global request ID +in ``X-Openstack-Request-Id`` header, starting from microversion 2.46. +The format must be ``req-`` + UUID (UUID4). +If not in accordance with the format, the global request ID is ignored by Nova. + +Request header example:: + + X-Openstack-Request-Id: req-3dccb8c4-08fe-4706-a91d-e843b8fe9ed2 + +**Response Header** + +In each REST API request, ``X-Compute-Request-Id`` is returned +in the response header. +Starting from microversion 2.46, ``X-Openstack-Request-Id`` is also returned +in the response header. + +``X-Compute-Request-Id`` and ``X-Openstack-Request-Id`` are local request IDs. +The global request IDs are not returned. + +Response header example:: + + X-Compute-Request-Id: req-d7bc29d0-7b99-4aeb-a356-89975043ab5e + X-Openstack-Request-Id: req-d7bc29d0-7b99-4aeb-a356-89975043ab5e Server Actions -------------- @@ -26,14 +71,38 @@ There is an API for end users to list the outcome of Server Actions, referencing the requested action by request id. For more details, please see: -http://developer.openstack.org/api-ref-compute-v2.1.html#os-instance-actions-v2.1 +http://developer.openstack.org/api-ref/compute/#servers-run-an-action-servers-action Logs ---- -All logs on the system, by default, include the request-id when available. -This allows an administrator to track the API request processing as it -transitions between all the different nova services. +All logs on the system, by default, include the global request ID and +the local request ID when available. This allows an administrator to +track the API request processing as it transitions between all the +different nova services or between nova and other component services +called by nova during that request. + +When nova services receive the local request IDs of other components in the +``X-Openstack-Request-Id`` header, the local request IDs are output to logs +along with the local request IDs of nova services. + +.. tip:: + + If a session client is used in client library, set ``DEBUG`` level to + the ``keystoneauth`` log level. If not, set ``DEBUG`` level to the client + library package. e.g. ``glanceclient``, ``cinderclient``. + +Sample log output is provided below. +In this example, nova is using local request ID +``req-034279a7-f2dd-40ff-9c93-75768fda494d``, +while neutron is using local request ID +``req-39b315da-e1eb-4ab5-a45b-3f2dbdaba787``:: + + Jun 19 09:16:34 devstack-master nova-compute[27857]: DEBUG keystoneauth.session [None req-034279a7-f2dd-40ff-9c93-75768fda494d admin admin] POST call to network for http://10.0.2.15:9696/v2.0/ports used request id req-39b315da-e1eb-4ab5-a45b-3f2dbdaba787 {{(pid=27857) request /usr/local/lib/python2.7/dist-packages/keystoneauth1/session.py:640}} + +.. note:: + + The local request IDs are useful to make 'call graphs'. Instance Faults --------------- @@ -91,7 +160,7 @@ An error may occur in the background while a server is being built or while a server is executing an action. In these cases, the server is usually placed in an ``ERROR`` state. For some -operations, like resize, its possible that the operations fails but +operations, like resize, it is possible that the operation fails but the instance gracefully returned to its original state before attempting the operation. In both of these cases, you should be able to find out more from the Server Actions API described above. diff --git a/api-guide/source/general_info.rst b/api-guide/source/general_info.rst index 2855f261f07..da818ca4d16 100644 --- a/api-guide/source/general_info.rst +++ b/api-guide/source/general_info.rst @@ -114,10 +114,6 @@ In this section we focus on this related to networking. TODO -- **Cloudpipe** - - TODO - - **Extended Networks** TODO @@ -133,7 +129,7 @@ on compute hosts rather than servers. Services are provided by Nova components. Normally, the Nova component runs as a process on the controller/compute node to provide the service. These - services may be end-user facing, such as the the OpenStack Compute REST API + services may be end-user facing, such as the OpenStack Compute REST API service, but most just work with other Nova services. The status of each service is monitored by Nova, and if it is not responding normally, Nova will update its status so that requests are not sent to that service @@ -180,10 +176,6 @@ on compute hosts rather than servers. This service provides authorization for compute instances consoles. - - **nova-cert** - - This service handles the management of X509 certificates. - - **Services Actions** - **enable, disable, disable-log-reason** @@ -249,7 +241,8 @@ on compute hosts rather than servers. - **Aggregates** - Please see :doc:`aggregates.rst` + See `Aggregates developer information + `_. - **Migrations** @@ -261,11 +254,6 @@ on compute hosts rather than servers. about migrations. For example, they can determine the source and destination hosts, type of migration, or changes in the server's flavor. -- **Certificates** - - Nova service "nova-cert" handles the management of X509 certificates which - are used to generate certificates for euca-bundle-image. - Relationship with Volume API ============================ diff --git a/api-guide/source/index.rst b/api-guide/source/index.rst index 194bdc361c8..653960804ba 100644 --- a/api-guide/source/index.rst +++ b/api-guide/source/index.rst @@ -24,7 +24,7 @@ compute resources might be Virtual Machines, Physical Machines or Containers. This guide covers the concepts in the OpenStack Compute API. For a full reference listing, please see: -`Compute API Reference `__. +`Compute API Reference `__. We welcome feedback, comments, and bug reports at `bugs.launchpad.net/nova `__. @@ -55,18 +55,17 @@ be made available to most end users but this is fully configurable. API Versions ============ -Following the Liberty release, every Nova deployment should have +Following the Mitaka release, every Nova deployment should have the following endpoints: * / - list of available versions * /v2 - the first version of the Compute API, uses extensions (we call this Compute API v2.0) -* /v1.1 - an alias for v2.0 for backwards compatibility * /v2.1 - same API, except uses microversions While this guide concentrates on documenting the v2.1 API, -please note that the v2.0 and v1.1 API are (almost) identical to first -microversion of the v2.1 API and are also covered by this guide. +please note that the v2.0 is (almost) identical to first microversion of +the v2.1 API and are also covered by this guide. Contents ======== @@ -87,3 +86,8 @@ Contents paginated_collections polling_changes-since_parameter request_and_response_formats + +.. toctree:: + :hidden: + + extra_specs_and_properties diff --git a/api-guide/source/limits.rst b/api-guide/source/limits.rst index 5208892a983..82b79c1774c 100644 --- a/api-guide/source/limits.rst +++ b/api-guide/source/limits.rst @@ -4,54 +4,12 @@ Limits Accounts may be pre-configured with a set of thresholds (or limits) to manage capacity and prevent abuse of the system. The system recognizes -two kinds of limits: *rate limits* and *absolute limits*. Rate limits -are thresholds that are reset after a certain amount of time passes. -Absolute limits are fixed. Limits are configured by operators and may -differ from one deployment of the OpenStack Compute service to -another. Please contact your provider to determine the limits that +*absolute limits*. Absolute limits are fixed. Limits are configured by +operators and may differ from one deployment of the OpenStack Compute service +to another. Please contact your provider to determine the limits that apply to your account. Your provider may be able to adjust your account's limits if they are too low. Also see the API Reference for -`*Limits* `__. - -Rate limits -~~~~~~~~~~~ - -Rate limits are specified in terms of both a human-readable wild-card -URI and a machine-processable regular expression. The human-readable -limit is intended for displaying in graphical user interfaces. The -machine-processable form is intended to be used directly by client -applications. - -The regular expression boundary matcher "^" for the rate limit takes -effect after the root URI path. For example, the regular expression -^/servers would match the bolded portion of the following URI: -https://servers.api.openstack.org/v2/3542812\ **/servers**. - -**Table: Sample rate limits** - -+------------+-------------------+----------------------+----------+ -| Verb | URI | RegEx | Default | -+------------+-------------------+----------------------+----------+ -| **POST** | \* | .\* | 120/min | -+------------+-------------------+----------------------+----------+ -| **POST** | \*/servers | ^/servers | 120/min | -+------------+-------------------+----------------------+----------+ -| **PUT** | \* | .\* | 120/min | -+------------+-------------------+----------------------+----------+ -| **GET** | \*changes-since\* | .\*changes-since.\* | 120/min | -+------------+-------------------+----------------------+----------+ -| **DELETE** | \* | .\* | 120/min | -+------------+-------------------+----------------------+----------+ -| **GET** | \*/os-fping\* | ^/os-fping | 12/min | -+------------+-------------------+----------------------+----------+ - - -Rate limits are applied in order relative to the verb, going from least -to most specific. - -In the event a request exceeds the thresholds established for your -account, a 413 HTTP response is returned with a ``Retry-After`` header -to notify the client when they can attempt to try again. +`Limits `__. Absolute limits ~~~~~~~~~~~~~~~ @@ -91,4 +49,4 @@ Determine limits programmatically Applications can programmatically determine current account limits. For information, see -`*Limits* `__. +`Limits `__. diff --git a/api-guide/source/links_and_references.rst b/api-guide/source/links_and_references.rst index 1e57b7dda5c..37adeac3171 100644 --- a/api-guide/source/links_and_references.rst +++ b/api-guide/source/links_and_references.rst @@ -86,11 +86,11 @@ following types of link relations are associated with resources: "links":[ { "rel":"self", - "href":"http://servers.api.openstack.org/v2/1234/servers/52415800-8b69-11e0-9b19-734fcece0043" + "href":"http://servers.api.openstack.org/v2.1/servers/52415800-8b69-11e0-9b19-734fcece0043" }, { "rel":"bookmark", - "href":"http://servers.api.openstack.org/1234/servers/52415800-8b69-11e0-9b19-734fcece0043" + "href":"http://servers.api.openstack.org/servers/52415800-8b69-11e0-9b19-734fcece0043" } ] } @@ -108,11 +108,11 @@ following types of link relations are associated with resources: "links": [ { "rel" : "self", - "href" : "http://servers.api.openstack.org/v2/1234/images/52415800-8b69-11e0-9b19-734f5736d2a2" + "href" : "http://servers.api.openstack.org/v2.1/images/52415800-8b69-11e0-9b19-734f5736d2a2" }, { "rel" : "bookmark", - "href" : "http://servers.api.openstack.org/1234/images/52415800-8b69-11e0-9b19-734f5736d2a2" + "href" : "http://servers.api.openstack.org/images/52415800-8b69-11e0-9b19-734f5736d2a2" }, { "rel" : "alternate", diff --git a/api-guide/source/microversions.rst b/api-guide/source/microversions.rst index fc4fcc0921f..1b202665edf 100644 --- a/api-guide/source/microversions.rst +++ b/api-guide/source/microversions.rst @@ -107,17 +107,28 @@ HTTP header:: X-OpenStack-Nova-API-Version: 2.4 +Starting with microversion `2.27` it is also correct to use the +following header to specify the microversion:: + + OpenStack-API-Version: compute 2.27 + +.. note:: For more detail on this newer form see the `Microversion Specification + `_. + This acts conceptually like the "Accept" header. Semantically this means: -* If `X-OpenStack-Nova-API-Version` is not provided, act as if the minimum - supported microversion was specified. +* If neither `X-OpenStack-Nova-API-Version` nor `OpenStack-API-Version` + (specifying `compute`) is provided, act as if the minimum supported + microversion was specified. -* If `X-OpenStack-Nova-API-Version` is provided, respond with the API at - that microversion. If that's outside of the range of microversions supported, - return 406 Not Acceptable. +* If both headers are provided, `OpenStack-API-Version` will be preferred. -* If `X-OpenStack-Nova-API-Version` is ``latest`` (special keyword), act as - if maximum was specified. +* If `X-OpenStack-Nova-API-Version` or `OpenStack-API-Version` is provided, + respond with the API at that microversion. If that's outside of the range + of microversions supported, return 406 Not Acceptable. + +* If `X-OpenStack-Nova-API-Version` or `OpenStack-API-Version` has a value + of ``latest`` (special keyword), act as if maximum was specified. .. warning:: The ``latest`` value is mostly meant for integration testing and would be dangerous to rely on in client code since microversions are not @@ -129,14 +140,21 @@ This means that out of the box, an old client without any knowledge of microversions can work with an OpenStack installation with microversions support. -Two extra headers are always returned in the response: +In microversions prior to `2.27` two extra headers are always returned in +the response:: -* X-OpenStack-Nova-API-Version: microversion_number -* Vary: X-OpenStack-Nova-API-Version + X-OpenStack-Nova-API-Version: microversion_number + Vary: X-OpenStack-Nova-API-Version The first header specifies the microversion number of the API which was executed. -The second header is used as a hint to caching proxies that the response -is also dependent on the X-OpenStack-Nova-API-Version and not just -the body and query parameters. See :rfc:`2616` section 14.44 for details. +The `Vary` header is used as a hint to caching proxies that the response +is also dependent on the microversion and not just the body and query +parameters. See :rfc:`2616` section 14.44 for details. + +From microversion `2.27` two additional headers are added to the +response:: + + OpenStack-API-Version: compute microversion_number + Vary: OpenStack-API-Version diff --git a/api-guide/source/paginated_collections.rst b/api-guide/source/paginated_collections.rst index f3c2c78a2d0..f1945ee9e59 100644 --- a/api-guide/source/paginated_collections.rst +++ b/api-guide/source/paginated_collections.rst @@ -25,7 +25,7 @@ implementation does not contain ``previous`` links. The last page in the list does not contain a link to "next" page. The following examples illustrate three pages in a collection of images. The first page was retrieved through a **GET** to -``http://servers.api.openstack.org/v2/1234/servers?limit=1``. In these +``http://servers.api.openstack.org/v2.1/servers?limit=1``. In these examples, the *``limit``* parameter sets the page size to a single item. Subsequent links honor the initial page size. Thus, a client can follow links to traverse a paginated collection without having to input the @@ -39,7 +39,7 @@ links to traverse a paginated collection without having to input the { "servers_links":[ { - "href":"https://servers.api.openstack.org/v2/1234/servers?limit=1&marker=fc45ace4-3398-447b-8ef9-72a22086d775", + "href":"https://servers.api.openstack.org/v2.1/servers?limit=1&marker=fc45ace4-3398-447b-8ef9-72a22086d775", "rel":"next" } ], @@ -48,11 +48,11 @@ links to traverse a paginated collection without having to input the "id":"fc55acf4-3398-447b-8ef9-72a42086d775", "links":[ { - "href":"https://servers.api.openstack.org/v2/1234/servers/fc45ace4-3398-447b-8ef9-72a22086d775", + "href":"https://servers.api.openstack.org/v2.1/servers/fc45ace4-3398-447b-8ef9-72a22086d775", "rel":"self" }, { - "href":"https://servers.api.openstack.org/v2/1234/servers/fc45ace4-3398-447b-8ef9-72a22086d775", + "href":"https://servers.api.openstack.org/v2.1/servers/fc45ace4-3398-447b-8ef9-72a22086d775", "rel":"bookmark" } ], @@ -91,13 +91,13 @@ keep following the ``next`` link to retrieve the full set of metadata. "metadata_links": [ { "rel": "next", - "href": "https://servers.api.openstack.org/v2/1234/servers/fc55acf4-3398-447b-8ef9-72a42086d775/meta?marker=ServiceType" + "href": "https://servers.api.openstack.org/v2.1/servers/fc55acf4-3398-447b-8ef9-72a42086d775/meta?marker=ServiceType" } ], "links": [ { "rel": "self", - "href": "https://servers.api.openstack.org/v2/1234/servers/fc55acf4-3398-447b-8ef9-72a42086d775" + "href": "https://servers.api.openstack.org/v2.1/servers/fc55acf4-3398-447b-8ef9-72a42086d775" } ] } diff --git a/api-guide/source/polling_changes-since_parameter.rst b/api-guide/source/polling_changes-since_parameter.rst index 7354ccf22f9..cd4d5fdb7b8 100644 --- a/api-guide/source/polling_changes-since_parameter.rst +++ b/api-guide/source/polling_changes-since_parameter.rst @@ -16,7 +16,7 @@ nothing has changed since the *``changes-since``* time, an empty list is returned. If data has changed, only the items changed since the specified time are returned in the response. For example, performing a **GET** against -https://api.servers.openstack.org/v2/224532/servers?\ *``changes-since``*\ =2015-01-24T17:08Z +https://api.servers.openstack.org/v2.1/servers?\ *``changes-since``*\ =2015-01-24T17:08Z would list all servers that have changed since Mon, 24 Jan 2015 17:08:00 UTC. diff --git a/api-guide/source/request_and_response_formats.rst b/api-guide/source/request_and_response_formats.rst index 65377f53a8d..73db8b32422 100644 --- a/api-guide/source/request_and_response_formats.rst +++ b/api-guide/source/request_and_response_formats.rst @@ -7,19 +7,21 @@ formats, with a mime-type of ``application/json``. As there is only one supported content type, all content is assumed to be ``application/json`` in both request and response formats. -Request and response examples -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Request and response example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The examples below show a request body in JSON format. +The example below shows a request body in JSON format: -**Example: JSON request with headers** - -| POST /v2/010101/servers HTTP/1.1 -| Host: servers.api.openstack.org -| X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb +**Example: JSON request with headers** .. code:: + POST /v2.1/servers HTTP/1.1 + Host: servers.api.openstack.org + X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb + +.. code:: JSON + { "server": { "name": "server-test-1", diff --git a/api-guide/source/server_concepts.rst b/api-guide/source/server_concepts.rst index 40ebecc038e..e4c0ee9648a 100644 --- a/api-guide/source/server_concepts.rst +++ b/api-guide/source/server_concepts.rst @@ -70,10 +70,10 @@ server status is one of the following values: be deleted first, depending on the value in the *``shutdown_terminate``* database field on the Instance model. -- ``SOFT_DELETED``: The server is marked as deleted while will keep in the - cloud for some time(configurable), during the period authorized user can - restore the server back to normal state. When the time expires, the - server will be deleted permanently. +- ``SOFT_DELETED``: The server is marked as deleted but will remain in the + cloud for some configurable amount of time. While soft-deleted, an + authorized user can restore the server back to normal state. When the time + expires, the server will be deleted permanently. - ``SUSPENDED``: The server is suspended, either by request or necessity. This status appears for only the following hypervisors: @@ -150,55 +150,59 @@ be used and interpreted by nova. and pagination. .. code:: + Precondition: there are 2 servers existing in cloud with following info: - "servers":[ - { - "name": "t1", - "locked": "true", - ... - } - { - "name":"t2", - "locked": "false", - ... - } + "servers": [ + { + "name": "t1", + "locked": "true", + ... + }, + { + "name": "t2", + "locked": "false", + ... + } + ] **Example: General user query server with administrator only options** .. code:: + Request with non-administrator context: GET /servers/detail?locked=1 Note that 'locked' is not returned through API layer Response: { - "servers":[ - { - "name": "t1", - ... - } - { - "name":"t2", - ... - } + "servers": [ + { + "name": "t1", + ... + }, + { + "name": "t2", + ... + } ] } **Example: Administrator query server with administrator only options** .. code:: + Request with administrator context: GET /servers/detail?locked=1 Response: { - "servers":[ - { - "name": "t1", - ... - } + "servers": [ + { + "name": "t1", + ... + } ] } @@ -213,6 +217,7 @@ be used and interpreted by nova. **Example: User query server using exact matching on host** .. code:: + Precondition: Request with administrator context: GET /servers/detail @@ -220,17 +225,17 @@ be used and interpreted by nova. Response: { - "servers":[ - { - "name": "t1", - "OS-EXT-SRV-ATTR:host": "devstack" - ... - } - { - "name": "t2", - "OS-EXT-SRV-ATTR:host": "devstack1" - ... - } + "servers": [ + { + "name": "t1", + "OS-EXT-SRV-ATTR:host": "devstack" + ... + }, + { + "name": "t2", + "OS-EXT-SRV-ATTR:host": "devstack1" + ... + } ] } @@ -240,18 +245,19 @@ be used and interpreted by nova. Response: { - "servers":[ - { - "name": "t1", - "OS-EXT-SRV-ATTR:host": "devstack" - ... - } + "servers": [ + { + "name": "t1", + "OS-EXT-SRV-ATTR:host": "devstack" + ... + } ] } - + **Example: Query server using regex matching on name** .. code:: + Precondition: Request with administrator context: GET /servers/detail @@ -259,23 +265,23 @@ be used and interpreted by nova. Response: { - "servers":[ - { - "name": "test11", - ... - } - { - "name": "test21", - ... - } - { - "name": "t1", - ... - } - { - "name": "t14", - ... - } + "servers": [ + { + "name": "test11", + ... + }, + { + "name": "test21", + ... + }, + { + "name": "t1", + ... + }, + { + "name": "t14", + ... + } ] } @@ -285,19 +291,19 @@ be used and interpreted by nova. Response: { - "servers":[ - { - "name": "test11", - ... - } - { - "name": "t1", - ... - } - { - "name": "t14", - ... - } + "servers": [ + { + "name": "test11", + ... + }, + { + "name": "t1", + ... + }, + { + "name": "t14", + ... + } ] } @@ -305,6 +311,7 @@ be used and interpreted by nova. regex matching on name** .. code:: + Precondition: Request with administrator context: GET /servers/detail @@ -312,22 +319,22 @@ be used and interpreted by nova. Response: { - "servers":[ - { - "name": "test1", - "OS-EXT-SRV-ATTR:host": "devstack" - ... - } - { - "name": "t2", - "OS-EXT-SRV-ATTR:host": "devstack1" - ... - } - { - "name": "test3", - "OS-EXT-SRV-ATTR:host": "devstack1" - ... - } + "servers": [ + { + "name": "test1", + "OS-EXT-SRV-ATTR:host": "devstack" + ... + }, + { + "name": "t2", + "OS-EXT-SRV-ATTR:host": "devstack1" + ... + }, + { + "name": "test3", + "OS-EXT-SRV-ATTR:host": "devstack1" + ... + } ] } @@ -337,16 +344,16 @@ be used and interpreted by nova. Response: { - "servers":[ - { - "name": "test3", - "OS-EXT-SRV-ATTR:host": "devstack1" - ... - } + "servers": [ + { + "name": "test3", + "OS-EXT-SRV-ATTR:host": "devstack1" + ... + } ] } -- **Speical keys are used to tweek the query** +- **Special keys are used to tweak the query** ``changes-since`` returns instances updated after the given time, ``deleted`` return (or exclude) deleted instances and ``soft_deleted`` modify behavior of 'deleted' to either include or exclude instances whose @@ -355,23 +362,25 @@ be used and interpreted by nova. **Example: User query server with special keys changes-since** .. code:: + Precondition: GET /servers/detail Response: { - "servers":[ - { - "name": "t1" - "updated": "2015-12-15T15:55:52Z" - ... - } - { - "name": "t2", - "updated": "2015-12-17T15:55:52Z" - ... - } - } + "servers": [ + { + "name": "t1" + "updated": "2015-12-15T15:55:52Z" + ... + }, + { + "name": "t2", + "updated": "2015-12-17T15:55:52Z" + ... + } + ] + } GET /servers/detail?changes-since='2015-12-16T15:55:52Z' @@ -650,12 +659,12 @@ assigned at creation time. .. code:: { - "server":{ - "name":"new-server-test", - "imageRef":"52415800-8b69-11e0-9b19-734f6f006e54", - "flavorRef":"52415800-8b69-11e0-9b19-734f1195ff37", - "accessIPv4":"67.23.10.132" - } + "server": { + "name": "new-server-test", + "imageRef": "52415800-8b69-11e0-9b19-734f6f006e54", + "flavorRef": "52415800-8b69-11e0-9b19-734f1195ff37", + "accessIPv4": "67.23.10.132" + } } .. note:: Both IPv4 and IPv6 addresses may be used as access addresses and both @@ -668,13 +677,13 @@ assigned at creation time. .. code:: { - "server":{ - "name":"new-server-test", - "imageRef":"52415800-8b69-11e0-9b19-734f6f006e54", - "flavorRef":"52415800-8b69-11e0-9b19-734f1195ff37", - "accessIPv4":"67.23.10.132", - "accessIPv6":"::babe:67.23.10.132" - } + "server": { + "name": "new-server-test", + "imageRef": "52415800-8b69-11e0-9b19-734f6f006e54", + "flavorRef": "52415800-8b69-11e0-9b19-734f1195ff37", + "accessIPv4": "67.23.10.132", + "accessIPv6": "::babe:67.23.10.132" + } } Moving servers diff --git a/api-guide/source/versions.rst b/api-guide/source/versions.rst index 2237eb5d7c3..656d1890e28 100644 --- a/api-guide/source/versions.rst +++ b/api-guide/source/versions.rst @@ -105,10 +105,7 @@ server might respond with a 302 redirection request. For examples of the list versions and get version details requests and responses, see `*API versions* -`__. +`__. The detailed version response contains pointers to both a human-readable -and a machine-processable description of the API service. The -machine-processable description is written in the Web Application -Description Language (WADL). - +and a machine-processable description of the API service. diff --git a/api-ref/source/conf.py b/api-ref/source/conf.py new file mode 100644 index 00000000000..c92859261a8 --- /dev/null +++ b/api-ref/source/conf.py @@ -0,0 +1,228 @@ +# 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. +# +# nova documentation build configuration file, created by +# sphinx-quickstart on Sat May 1 15:17:47 2010. +# +# This file is execfile()d with the current directory set to +# its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import os +import subprocess +import sys +import warnings + +import openstackdocstheme + +extensions = [ + 'os_api_ref', +] + + +html_theme = 'openstackdocs' +html_theme_path = [openstackdocstheme.get_html_theme_path()] +html_theme_options = { + "sidebar_mode": "toc", +} +html_context = {'bug_project': 'nova', 'bug_tag': 'api-ref'} + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('../../')) +sys.path.insert(0, os.path.abspath('../')) +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. + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# +# source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Compute API Reference' +copyright = u'2010-present, OpenStack Foundation' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +from nova.version import version_info +# The full version, including alpha/beta/rc tags. +release = version_info.release_string() +# The short X.Y version. +version = version_info.version_string() + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# The reST default role (used for this markup: `text`) to use +# for all documents. +# default_role = None + +# 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 = False + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# -- Options for man page output ---------------------------------------------- + +# Grouping the document tree for man pages. +# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' + + +# -- 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' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' +git_cmd = ["git", "log", "--pretty=format:'%ad, commit %h'", "--date=local", + "-n1"] +try: + html_last_updated_fmt = subprocess.check_output(git_cmd).decode('utf-8') +except Exception: + warnings.warn('Cannot get last updated time from git repository. ' + 'Not setting "html_last_updated_fmt".') + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_use_modindex = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'novadoc' + + +# -- Options for LaTeX output ------------------------------------------------- + +# The paper size ('letter' or 'a4'). +# latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +# latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto/manual]). +latex_documents = [ + ('index', 'Nova.tex', u'OpenStack Compute API Documentation', + u'OpenStack Foundation', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +# latex_preamble = '' + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_use_modindex = True diff --git a/api-ref/source/diagnostics.inc b/api-ref/source/diagnostics.inc new file mode 100644 index 00000000000..b5e63b05861 --- /dev/null +++ b/api-ref/source/diagnostics.inc @@ -0,0 +1,72 @@ +.. -*- rst -*- + +============================================ + Servers diagnostics (servers, diagnostics) +============================================ + +Shows the usage data for a server. + +Show Server Diagnostics +======================= + +.. rest_method:: GET /servers/{server_id}/diagnostics + +Shows basic usage data for a server. + +Policy defaults enable only users with the administrative role. Cloud +providers can change these permissions through the ``policy.json`` +file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), notfound(404), conflict(409), notimplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +Starting from **microversion 2.48** diagnostics response is standardized +across all virt drivers. The response should be considered a debug interface +only and not relied upon by programmatic tools. All response fields are listed +below. If the virt driver is unable to provide a specific field then this field +will be reported as ``None`` in the response. + +.. rest_parameters:: parameters.yaml + + - config_drive: config_drive_diagnostics + - state: vm_state_diagnostics + - driver: driver_diagnostics + - hypervisor: hypervisor_diagnostics + - hypervisor_os: hypervisor_os_diagnostics + - uptime: uptime_diagnostics + - num_cpus: num_cpus_diagnostics + - num_disks: num_disks_diagnostics + - num_nics: num_nics_diagnostics + - memory_details: memory_details_diagnostics + - cpu_details: cpu_details_diagnostics + - disk_details: disk_details_diagnostics + - nic_details: nic_details_diagnostics + +**Example Server diagnostics (2.48)** + +.. literalinclude:: ../../doc/api_samples/os-server-diagnostics/v2.48/server-diagnostics-get-resp.json + :language: javascript + +.. warning:: + Before **microversion 2.48** the response format for diagnostics was not + well defined. Each hypervisor had its own format. + +**Example Server diagnostics (2.1)** + +Below is an example of diagnostics for a libvirt based instance. The unit of the return +value is hypervisor specific, but in this case the unit of vnet1_rx* and +vnet1_tx* is octets. + +.. literalinclude:: ../../doc/api_samples/os-server-diagnostics/server-diagnostics-get-resp.json + :language: javascript diff --git a/api-ref/source/extensions.inc b/api-ref/source/extensions.inc new file mode 100644 index 00000000000..28123a89d05 --- /dev/null +++ b/api-ref/source/extensions.inc @@ -0,0 +1,94 @@ +.. -*- rst -*- + +===================================== + Extensions (extensions) (DEPRECATED) +===================================== + +Lists available extensions and shows information for an extension, by +alias. + +Nova originally supported the concept of API extensions, that allowed +implementations of Nova to change the API (add new resources, or +attributes to existing resource objects) via extensions. In an attempt +to expose to the user what was supported in a particular site, the +extensions resource provided a list of extensions and detailed +information on each. The net result was gratuitous differentiation in +the API that required all users of OpenStack clouds to write specific +code to interact with every cloud. + +As such, the entire extensions concept is deprecated, and will be +removed in the near future. + +For information about extensions, see `Extensions +`__. + +List Extensions +=============== + +.. rest_method:: GET /extensions + +Lists all extensions to the API. + +Normal response codes: 200 + +Error response codes: unauthorized(401) + + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - extensions: extensions + - name: extension_name + - alias: alias + - links: extension_links + - namespace: namespace + - description: extension_description + - updated: updated + +**Example List Extensions** + +Lists all extensions to the API. + +.. literalinclude:: ../../doc/api_samples/extension-info/extensions-list-resp.json + :language: javascript + + +Show Extension Details +====================== + +.. rest_method:: GET /extensions/{alias} + +Shows details for an extension, by alias. + +Normal response codes: 200 + +Error response codes: unauthorized(401), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - alias: alias + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - extension: extension + - name: extension_name + - alias: alias + - links: extension_links + - namespace: namespace + - description: extension_description + - updated: updated + +**Example Show Extension Details** + +Shows details about the ``os-agents`` extension. + +.. literalinclude:: ../../doc/api_samples/extension-info/extensions-get-resp.json + :language: javascript diff --git a/api-ref/source/flavors.inc b/api-ref/source/flavors.inc new file mode 100644 index 00000000000..6ef3feef1c7 --- /dev/null +++ b/api-ref/source/flavors.inc @@ -0,0 +1,229 @@ +.. -*- rst -*- + +========= + Flavors +========= + +Show and manage server flavors. + +Flavors are a way to describe the basic dimensions of a server to be +created including how much ``cpu``, ``ram``, and ``disk space`` are +allocated to a server built with this flavor. + +List Flavors +============ + +.. rest_method:: GET /flavors + +Lists all flavors accessible to your project. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - sort_key: sort_key_flavor + - sort_dir: sort_dir_flavor + - limit: limit + - marker: marker + - minDisk: minDisk + - minRam: minRam + - is_public: flavor_is_public_query + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - flavors: flavors + - id: flavor_id_body + - name: flavor_name + - links: links + +**Example List Flavors** + +Showing all the default flavors of a Liberty era Nova installation +that was not customized by the site operators. + +.. literalinclude:: ../../doc/api_samples/flavors/flavors-list-resp.json + :language: javascript + +Create Flavor +============= + +.. rest_method:: POST /flavors + +Creates a flavor. + +Creating a flavor is typically only available to administrators of a +cloud because this has implications for scheduling efficiently in the cloud. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - flavor: flavor + - name: flavor_name + - id: flavor_id_body_create + - ram: flavor_ram + - disk: flavor_disk + - vcpus: flavor_cpus + - OS-FLV-EXT-DATA:ephemeral: flavor_ephem_disk_in + - swap: flavor_swap_in + - rxtx_factor: flavor_rxtx_factor_in + - os-flavor-access:is_public: flavor_is_public_in + +**Example Create Flavor** + +.. literalinclude:: ../../doc/api_samples/flavor-manage/flavor-create-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - flavor: flavor + - name: flavor_name + - id: flavor_id_body + - ram: flavor_ram + - disk: flavor_disk + - vcpus: flavor_cpus + - links: links + - OS-FLV-EXT-DATA:ephemeral: flavor_ephem_disk + - OS-FLV-DISABLED:disabled: flavor_disabled + - swap: flavor_swap + - rxtx_factor: flavor_rxtx_factor + - os-flavor-access:is_public: flavor_is_public + + +**Example Create Flavor** + +.. literalinclude:: ../../doc/api_samples/flavor-manage/flavor-create-post-resp.json + :language: javascript + +List Flavors With Details +========================= + +.. rest_method:: GET /flavors/detail + +Lists flavors with details. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - sort_key: sort_key_flavor + - sort_dir: sort_dir_flavor + - limit: limit + - marker: marker + - minDisk: minDisk + - minRam: minRam + - is_public: flavor_is_public_query + + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - flavors: flavors + - name: flavor_name + - id: flavor_id_body + - ram: flavor_ram + - disk: flavor_disk + - vcpus: flavor_cpus + - links: links + - OS-FLV-EXT-DATA:ephemeral: flavor_ephem_disk + - OS-FLV-DISABLED:disabled: flavor_disabled + - swap: flavor_swap + - rxtx_factor: flavor_rxtx_factor + - os-flavor-access:is_public: flavor_is_public + +**Example List Flavors With Details** + +.. literalinclude:: ../../doc/api_samples/flavors/flavors-detail-resp.json + :language: javascript + +Show Flavor Details +=================== + +.. rest_method:: GET /flavors/{flavor_id} + +Shows details for a flavor. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - flavor_id: flavor_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - flavor: flavor + - name: flavor_name + - id: flavor_id_body + - ram: flavor_ram + - disk: flavor_disk + - vcpus: flavor_cpus + - links: links + - OS-FLV-EXT-DATA:ephemeral: flavor_ephem_disk + - OS-FLV-DISABLED:disabled: flavor_disabled + - swap: flavor_swap + - rxtx_factor: flavor_rxtx_factor + - os-flavor-access:is_public: flavor_is_public + +**Example Show Flavor Details** + +.. literalinclude:: ../../doc/api_samples/flavors/flavor-get-resp.json + :language: javascript + +Delete Flavor +============= + +.. rest_method:: DELETE /flavors/{flavor_id} + +Deletes a flavor. + +This is typically an admin only action. Deleting a flavor that is in use by +existing servers is not recommended as it can cause incorrect data to +be returned to the user under some operations. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - flavor_id: flavor_id + +Response +-------- + +No body content is returned on a successful DELETE. diff --git a/api-ref/source/images.inc b/api-ref/source/images.inc new file mode 100644 index 00000000000..2c2feff35bb --- /dev/null +++ b/api-ref/source/images.inc @@ -0,0 +1,379 @@ +.. -*- rst -*- + +==================== + Images (DEPRECATED) +==================== + +.. warning:: + + These APIs are proxy calls to the Image service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Image APIs + `__. + +Lists, shows details and deletes images. +Also sets, lists, shows details, create, update and deletes image metadata. + +An image is a collection of files that you use to create and rebuild a +server. By default, operators provide pre-built operating system images. +You can also create custom images. See: `Create Image Action +`__. + +By default, the ``policy.json`` file authorizes all users to view the +image size in the ``OS-EXT-IMG-SIZE:size`` extended attribute. + +List Images +=========== + +.. rest_method:: GET /images + +List images. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - changes-since: changes-since + - server: image_server_query + - name: image_name_query + - status: image_status_query + - minDisk: minDisk + - minRam: minRam + - type : image_type_query + - limit : limit + - marker : marker + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - images: images + - id: image_id_body + - name: image_name + - links: links + +**Example List Images: JSON response** + +.. literalinclude:: ../../doc/api_samples/images/images-list-get-resp.json + :language: javascript + +List Images With Details +======================== + +.. rest_method:: GET /images/detail + +List images with details. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - changes-since: changes-since + - server: image_server_query + - name: image_name_query + - status: image_status_query + - minDisk: minDisk + - minRam: minRam + - type : image_type_query + - limit : limit + - marker : marker + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - images: images + - id: image_id_body + - name: image_name + - minRam: minRam_body + - minDisk: minDisk_body + - metadata: metadata_object + - created: created + - updated: updated + - status: image_status + - progress: image_progress + - links: links + - server: image_server + - OS-EXT-IMG-SIZE:size: image_size + - OS-DCF:diskConfig: OS-DCF:diskConfig + +**Example List Images Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/images/images-details-get-resp.json + :language: javascript + + +Show Image Details +================== + +.. rest_method:: GET /images/{image_id} + +Shows details for an image. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - image_id: image_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - images: images + - id: image_id_body + - name: image_name + - minRam: minRam_body + - minDisk: minDisk_body + - metadata: metadata_object + - created: created + - updated: updated + - status: image_status + - progress: image_progress + - links: links + - server: image_server + - OS-EXT-IMG-SIZE:size: image_size + - OS-DCF:diskConfig: OS-DCF:diskConfig + +**Example Show Image Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/images/image-get-resp.json + :language: javascript + +Delete Image +============ + +.. rest_method:: DELETE /images/{image_id} + +Deletes an image. + +Normal response codes: 204 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - image_id: image_id + +Response +-------- + +There is no body content for the response of a successful DELETE action. + +List Image Metadata +=================== + +.. rest_method:: GET /images/{image_id}/metadata + +List metadata of an image. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - image_id: image_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - metadata: metadata_object + +**Example List Image Metadata Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/images/image-metadata-get-resp.json + :language: javascript + +Create Image Metadata +===================== + +.. rest_method:: POST /images/{image_id}/metadata + +Create an image metadata. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - image_id: image_id + - metadata: metadata_object + +**Example Create Image Metadata: JSON request** + +.. literalinclude:: ../../doc/api_samples/images/image-metadata-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - metadata: metadata_object + +**Example Create Image Metadata: JSON response** + +.. literalinclude:: ../../doc/api_samples/images/image-metadata-post-resp.json + :language: javascript + +Update Image Metadata +===================== + +.. rest_method:: PUT /images/{image_id}/metadata + +Update an image metadata + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - image_id: image_id + - metadata: metadata_object + +**Example Update Image Metadata: JSON request** + +.. literalinclude:: ../../doc/api_samples/images/image-metadata-put-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - metadata: metadata_object + +**Example Update Image Metadata: JSON response** + +.. literalinclude:: ../../doc/api_samples/images/image-metadata-put-resp.json + :language: javascript + +Show Image Metadata Item +======================== + +.. rest_method:: GET /images/{image_id}/metadata/{key} + +Shows metadata item, by key, for an image. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - image_id: image_id + - key: key + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - meta: meta + +**Example Show Image Metadata Item Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/images/image-meta-key-get.json + :language: javascript + +Create Or Update Image Metadata Item +==================================== + +.. rest_method:: PUT /images/{image_id}/metadata/{key} + +Creates or updates a metadata item, by key, for an image. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - image_id: image_id + - key: key + - meta: meta + +**Example Create Or Update Image Metadata Item: JSON request** + +.. literalinclude:: ../../doc/api_samples/images/image-meta-key-put-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - meta: meta + +**Example Create Or Update Image Metadata Item: JSON response** + +.. literalinclude:: ../../doc/api_samples/images/image-meta-key-put-resp.json + :language: javascript + +Delete Image Metadata Item +========================== + +.. rest_method:: DELETE /images/{image_id}/metadata/{key} + +Deletes a metadata item, by key, for an image. + +Normal response codes: 204 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - image_id: image_id + - key: key + +Response +-------- + +There is no body content for the response of a successful DELETE action. diff --git a/api-ref/source/index.rst b/api-ref/source/index.rst new file mode 100644 index 00000000000..3e651881b2b --- /dev/null +++ b/api-ref/source/index.rst @@ -0,0 +1,83 @@ +:tocdepth: 2 + +============= + Compute API +============= + +This is a reference for the OpenStack Compute API which is provided by the Nova +project. To learn more about the OpenStack Compute API concepts, please refer to +the `API guide `_. + +.. rest_expand_all:: + +.. include:: versions.inc +.. include:: urls.inc +.. include:: request-ids.inc +.. include:: servers.inc +.. include:: servers-actions.inc +.. include:: servers-action-fixed-ip.inc +.. include:: servers-action-evacuate.inc +.. include:: servers-action-deferred-delete.inc +.. include:: servers-action-console-output.inc +.. include:: servers-action-shelve.inc +.. include:: servers-action-crash-dump.inc +.. include:: servers-action-remote-consoles.inc +.. include:: servers-admin-action.inc +.. include:: servers-remote-consoles.inc +.. include:: server-security-groups.inc +.. include:: diagnostics.inc +.. include:: ips.inc +.. include:: metadata.inc +.. include:: os-instance-actions.inc +.. include:: os-interface.inc +.. include:: os-server-password.inc +.. include:: os-volume-attachments.inc +.. include:: flavors.inc +.. include:: os-flavor-access.inc +.. include:: os-flavor-extra-specs.inc +.. include:: os-keypairs.inc +.. include:: limits.inc +.. include:: os-agents.inc +.. include:: os-aggregates.inc +.. include:: os-assisted-volume-snapshots.inc +.. include:: os-availability-zone.inc +.. include:: os-cells.inc +.. include:: os-consoles.inc +.. include:: os-hypervisors.inc +.. include:: os-instance-usage-audit-log.inc +.. include:: os-migrations.inc +.. include:: server-migrations.inc +.. include:: os-quota-sets.inc +.. include:: os-quota-class-sets.inc +.. include:: os-server-groups.inc +.. include:: os-server-tags.inc +.. include:: os-services.inc +.. include:: os-simple-tenant-usage.inc +.. include:: os-server-external-events.inc +.. include:: extensions.inc +.. include:: os-networks.inc +.. include:: os-volumes.inc +.. include:: images.inc +.. include:: os-baremetal-nodes.inc +.. include:: os-tenant-network.inc +.. include:: os-fixed-ips.inc +.. include:: os-floating-ip-dns.inc +.. include:: os-floating-ip-pools.inc +.. include:: os-floating-ips.inc +.. include:: os-floating-ips-bulk.inc +.. include:: os-fping.inc +.. include:: os-security-groups.inc +.. include:: os-security-group-default-rules.inc +.. include:: os-security-group-rules.inc +.. include:: os-hosts.inc +.. include:: os-virtual-interfaces.inc + +============= +Obsolete APIs +============= + +This section contains the reference for APIs that were part of the OpenStack +Compute API in the past, but no longer exist. + +.. include:: os-certificates.inc +.. include:: os-cloudpipe.inc diff --git a/api-ref/source/ips.inc b/api-ref/source/ips.inc new file mode 100644 index 00000000000..0552a25b225 --- /dev/null +++ b/api-ref/source/ips.inc @@ -0,0 +1,82 @@ +.. -*- rst -*- + +============================ + Servers IPs (servers, ips) +============================ + +Lists the IP addresses for an instance and shows details for an IP +address. + +List Ips +======== + +.. rest_method:: GET /servers/{server_id}/ips + +Lists IP addresses that are assigned to an instance. + +Policy defaults enable only users with the administrative role or the owner of +the server to perform this operation. Cloud providers can change these +permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - addresses: addresses_obj + - network_label: network_label_body + - addr: ip_address + - version: version_ip + +**Example List Ips** + +.. literalinclude:: ../../doc/api_samples/server-ips/server-ips-resp.json + :language: javascript + +Show Ip Details +=============== + +.. rest_method:: GET /servers/{server_id}/ips/{network_label} + +Shows IP addresses details for a network label of a server instance. + +Policy defaults enable only users with the administrative role or the owner of +the server to perform this operation. Cloud providers can change these +permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - network_label: network_label + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - network_label: network_label_body + - addr: ip_address + - version: version_ip + +**Example Show Ip Details** + +.. literalinclude:: ../../doc/api_samples/server-ips/server-ips-network-resp.json + :language: javascript diff --git a/api-ref/source/limits.inc b/api-ref/source/limits.inc new file mode 100644 index 00000000000..2fb3030780f --- /dev/null +++ b/api-ref/source/limits.inc @@ -0,0 +1,59 @@ +.. -*- rst -*- + +================= + Limits (limits) +================= + +Shows rate and absolute limits for the project. + +Show Rate And Absolute Limits +============================= + +.. rest_method:: GET /limits + +Shows rate and absolute limits for the project. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - reserved: reserved_query + - tenant_id: tenant_id_query + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - limits: limits + - absolute: limits_absolutes + - maxImageMeta: image_metadata_items + - maxPersonality: injected_files + - maxPersonalitySize: injected_file_content_bytes + - maxSecurityGroupRules: security_group_rules_quota + - maxSecurityGroups: security_groups_quota + - maxServerGroupMembers: server_group_members + - maxServerGroups: server_groups + - maxServerMeta: metadata_items + - maxTotalCores: cores + - maxTotalFloatingIps: floating_ips + - maxTotalInstances: instances + - maxTotalKeypairs: key_pairs + - maxTotalRAMSize: ram + - totalCoresUsed: total_cores_used + - totalFloatingIpsUsed: total_floatingips_used + - totalInstancesUsed: total_instances_used + - totalRAMUsed: total_ram_used + - totalSecurityGroupsUsed: total_security_groups_used + - totalServerGroupsUsed: total_server_groups_used + - rate: limits_rates + +**Example Show Rate And Absolute Limits: JSON response** + +.. literalinclude:: ../../doc/api_samples/limits/limit-get-resp.json + :language: javascript diff --git a/api-ref/source/metadata.inc b/api-ref/source/metadata.inc new file mode 100644 index 00000000000..22beebc1d8b --- /dev/null +++ b/api-ref/source/metadata.inc @@ -0,0 +1,234 @@ +.. -*- rst -*- + +===================================== + Server metadata (servers, metadata) +===================================== + +Lists metadata, creates or replaces one or more metadata items, and +updates one or more metadata items for a server. + +Shows details for, creates or replaces, and updates a metadata item, by +key, for a server. + +List All Metadata +================= + +.. rest_method:: GET /servers/{server_id}/metadata + +Lists all metadata for a server. + +Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - metadata: metadata_object + +**Example List All Metadata** + +.. literalinclude:: ../../doc/api_samples/server-metadata/server-metadata-all-resp.json + :language: javascript + +Create or Update Metadata Items +=============================== + +.. rest_method:: POST /servers/{server_id}/metadata + +Create or update one or more metadata items for a server. + +Creates any metadata items that do not already exist in the server, replaces +exists metadata items that match keys. Does not modify items that are not in +the request. + +Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - metadata: metadata_object + +**Example Update Metadata Items** + +.. literalinclude:: ../../doc/api_samples/server-metadata/server-metadata-all-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - metadata: metadata_object + +**Example Update Metadata Items** + +.. literalinclude:: ../../doc/api_samples/server-metadata/server-metadata-all-resp.json + :language: javascript + +Replace Metadata Items +====================== + +.. rest_method:: PUT /servers/{server_id}/metadata + +Replaces one or more metadata items for a server. + +Creates any metadata items that do not already exist in the server. Removes and completely replaces any metadata items that already exist in the server with the metadata items in the request. + +Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - metadata: metadata_object + +**Example Create Or Replace Metadata Items** + +.. literalinclude:: ../../doc/api_samples/server-metadata/server-metadata-all-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - metadata: metadata_object + +**Example Create Or Replace Metadata Items** + +.. literalinclude:: ../../doc/api_samples/server-metadata/server-metadata-all-resp.json + :language: javascript + +Show Metadata Item Details +========================== + +.. rest_method:: GET /servers/{server_id}/metadata/{key} + +Shows details for a metadata item, by key, for a server. + +Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - key: key + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - meta: metadata_object + +**Example Show Metadata Item Details** + +.. literalinclude:: ../../doc/api_samples/server-metadata/server-metadata-resp.json + :language: javascript + +Create Or Update Metadata Item +============================== + +.. rest_method:: PUT /servers/{server_id}/metadata/{key} + +Creates or replaces a metadata item, by key, for a server. + +Creates a metadata item that does not already exist in the server. Replaces +existing metadata items that match keys with the metadata item in the request. + +Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - key: key + +**Example Create Or Update Metadata Item** + +.. literalinclude:: ../../doc/api_samples/server-metadata/server-metadata-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - meta: metadata_object + +**Example Create Or Update Metadata Item** + +.. literalinclude:: ../../doc/api_samples/server-metadata/server-metadata-resp.json + :language: javascript + +Delete Metadata Item +==================== + +.. rest_method:: DELETE /servers/{server_id}/metadata/{key} + +Deletes a metadata item, by key, from a server. + +Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. + +Normal response codes: 204 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - key: key + +Response +-------- + +If successful, this method does not return content in the response body. diff --git a/api-ref/source/os-agents.inc b/api-ref/source/os-agents.inc new file mode 100644 index 00000000000..33bd9fbd3a7 --- /dev/null +++ b/api-ref/source/os-agents.inc @@ -0,0 +1,166 @@ +.. -*- rst -* + +========================== + Guest agents (os-agents) +========================== + +Creates, lists, updates, and deletes guest agent builds. Use guest +agents to access files on the disk, configure networking, or run other +applications or scripts in the guest while the agent is running. This +hypervisor-specific extension is currently only for the Xen driver. Use of +guest agents is possible only if the underlying service provider uses +the Xen driver. + +List Agent Builds +================= + +.. rest_method:: GET /os-agents + +Lists agent builds. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - hypervisor: hypervisor_query + + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - agents: agents + - agent_id: agent_id + - architecture: architecture + - hypervisor: hypervisor_type + - md5hash: md5hash + - os: os + - url: url + - version: version + +**Example List Agent Builds: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-agents/agents-get-resp.json + :language: javascript + +Create Agent Build +================== + +.. rest_method:: POST /os-agents + +Creates an agent build. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - agent: agent + - hypervisor: hypervisor_type + - os: os + - architecture: architecture + - version: version + - md5hash: md5hash + - url: url + +**Example Create Agent Build: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-agents/agent-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - agent: agent + - agent_id: agent_id + - architecture: architecture + - hypervisor: hypervisor_type + - md5hash: md5hash + - os: os + - url: url + - version: version + +**Example Create Agent Build: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-agents/agent-post-resp.json + :language: javascript + +Update Agent Build +================== + +.. rest_method:: PUT /os-agents/{agent_build_id} + +Updates an agent build. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - agent_build_id: agent_build_id + - para: para + - url: url + - md5hash: md5hash + - version: version + +**Example Update Agent Build: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-agents/agent-update-put-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - agent: agent + - agent_id: agent_id_str + - md5hash: md5hash + - url: url + - version: version + +**Example Update Agent Build: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-agents/agent-update-put-resp.json + :language: javascript + +Delete Agent Build +================== + +.. rest_method:: DELETE /os-agents/{agent_build_id} + +Deletes an existing agent build. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - agent_build_id: agent_build_id + +Response +-------- + +There is no body content for the response of a successful DELETE query diff --git a/api-ref/source/os-aggregates.inc b/api-ref/source/os-aggregates.inc new file mode 100644 index 00000000000..ecfefe59ac6 --- /dev/null +++ b/api-ref/source/os-aggregates.inc @@ -0,0 +1,352 @@ +.. -*- rst -*- + +================================ + Host aggregates (os-aggregates) +================================ + +Creates and manages host aggregates. An aggregate assigns metadata to +groups of compute nodes. Aggregates are only visible to the cloud +provider. + +List Aggregates +=============== + +.. rest_method:: GET /os-aggregates + +Lists all aggregates. Includes the ID, name, and availability zone for each aggregate. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - aggregates: aggregates + - availability_zone: aggregate_az + - created_at: created + - deleted_at: deleted_at + - deleted: deleted + - hosts: aggregate_host_list + - id: aggregate_id_body + - metadata: aggregate_metadata + - name: aggregate_name + - updated_at: updated_consider_null + - uuid: aggregate_uuid + +**Example List Aggregates (v2.41): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json + :language: javascript + +Create Aggregate +================ + +.. rest_method:: POST /os-aggregates + +Creates an aggregate. If specifying an option availability_zone, the aggregate is +created as an availability zone and the availability zone is visible to normal users. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - aggregate: aggregate + - name: aggregate_name + - availability_zone: aggregate_az_optional + +**Example Create Aggregate: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/aggregate-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - aggregate: aggregate + - availability_zone: aggregate_az + - created_at: created + - deleted_at: deleted_at + - deleted: deleted + - id: aggregate_id_body + - name: aggregate_name + - updated_at: updated_consider_null + - uuid: aggregate_uuid + +**Example Create Aggregate (v2.41): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/v2.41/aggregate-post-resp.json + :language: javascript + +Show Aggregate Details +====================== + +.. rest_method:: GET /os-aggregates/{aggregate_id} + +Shows details for an aggregate. Details include hosts and metadata. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - aggregate_id: aggregate_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - aggregate: aggregate + - availability_zone: aggregate_az + - created_at: created + - deleted_at: deleted_at + - deleted: deleted + - hosts: hosts + - id: aggregate_id_body + - metadata: aggregate_metadata + - name: aggregate_name + - updated_at: updated_consider_null + - uuid: aggregate_uuid + +**Example Show Aggregate Details (v2.41): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/v2.41/aggregates-get-resp.json + :language: javascript + +Update Aggregate +================ + +.. rest_method:: PUT /os-aggregates/{aggregate_id} + +Updates either or both the name and availability zone for an aggregate. +If the aggregate to be updated has host that already in the given +availability zone, the request will fail with 400 error. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - aggregate_id: aggregate_id + - aggregate: aggregate + - name: aggregate_name_optional + - availability_zone: aggregate_az_optional + +**Example Update Aggregate: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/aggregate-update-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - aggregate: aggregate + - availability_zone: aggregate_az + - created_at: created + - deleted_at: deleted_at + - deleted: deleted + - hosts: hosts + - id: aggregate_id_body + - metadata: aggregate_metadata + - name: aggregate_name + - updated_at: updated_consider_null + - uuid: aggregate_uuid + +**Example Update Aggregate (v2.41): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json + :language: javascript + +Delete Aggregate +================ + +.. rest_method:: DELETE /os-aggregates/{aggregate_id} + +Deletes an aggregate. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - aggregate_id: aggregate_id + +Response +-------- + +There is no body content for the response of a successful DELETE action. + +Add Host +======== + +.. rest_method:: POST /os-aggregates/{aggregate_id}/action + +Adds a host to an aggregate. + +Specify the ``add_host`` action and host name in the request body. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - aggregate_id: aggregate_id + - add_host: aggregate_add_host + - host: host_name_body + +**Example Add Host: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/aggregate-add-host-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - aggregate: aggregate + - availability_zone: aggregate_az + - created_at: created + - deleted_at: deleted_at + - deleted: deleted + - hosts: hosts + - id: aggregate_id_body + - metadata: aggregate_metadata + - name: aggregate_name + - updated_at: updated_consider_null + - uuid: aggregate_uuid + +**Example Add Host (v2.41): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json + :language: javascript + +Remove Host +=========== + +.. rest_method:: POST /os-aggregates/{aggregate_id}/action + +Removes a host from an aggregate. + +Specify the ``remove_host`` action and host name in the request body. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - aggregate_id: aggregate_id + - remove_host: aggregate_remove_host + - host: host_name_body + +**Example Remove Host: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/aggregate-remove-host-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - aggregate: aggregate + - availability_zone: aggregate_az + - created_at: created + - deleted_at: deleted_at + - deleted: deleted + - hosts: hosts + - id: aggregate_id_body + - metadata: aggregate_metadata + - name: aggregate_name + - updated_at: updated_consider_null + - uuid: aggregate_uuid + +**Example Remove Host (v2.41): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json + :language: javascript + +Create Or Update Aggregate Metadata +=================================== + +.. rest_method:: POST /os-aggregates/{aggregate_id}/action + +Creates or replaces metadata for an aggregate. + +Specify the ``set_metadata`` action and metadata info in the request body. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - aggregate_id: aggregate_id + - set_metadata: set_metadata + - metadata: metadata_object + +**Example Create Or Update Aggregate Metadata: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/aggregate-metadata-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - aggregate: aggregate + - availability_zone: aggregate_az + - created_at: created + - deleted_at: deleted_at + - deleted: deleted + - hosts: hosts + - id: aggregate_id_body + - metadata: aggregate_metadata + - name: aggregate_name + - updated_at: updated_consider_null + - uuid: aggregate_uuid + +**Example Create Or Update Aggregate Metadata (v2.41): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json + :language: javascript diff --git a/api-ref/source/os-assisted-volume-snapshots.inc b/api-ref/source/os-assisted-volume-snapshots.inc new file mode 100644 index 00000000000..018d4e478df --- /dev/null +++ b/api-ref/source/os-assisted-volume-snapshots.inc @@ -0,0 +1,91 @@ +.. -*- rst -*- + +========================================================== + Assisted volume snapshots (os-assisted-volume-snapshots) +========================================================== + +Creates and deletes snapshots through an emulator/hypervisor. Only qcow2 +file format is supported. + +This API is only implemented by the libvirt compute driver. + +An internal snapshot that lacks storage such as NFS can use +an emulator/hypervisor to add the snapshot feature. +This is used to enable snapshot of volumes on backends such as NFS +by storing data as qcow2 files on these volumes. + +This API is only ever called by Cinder, where it is used to create a snapshot +for drivers that extend the remotefs Cinder driver. + +Create Assisted Volume Snapshots +================================ + +.. rest_method:: POST /os-assisted-volume-snapshots + +Creates an assisted volume snapshot. + +Normal response codes: 200 + +Error response codes: badRequest(400),unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - snapshot: snapshot + - volume_id: volume_id + - create_info: create_info + - create_info.snapshot_id: snapshot_id + - create_info.type: type-os-assisted-volume-snapshot + - create_info.new_file: new_file + - create_info.id: create_info_id + +**Example Create Assisted Volume Snapshots: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - snapshot: snapshot + - id: create_info_id_resp + - volumeId: volume_id + +**Example Create Assisted Volume Snapshots: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-resp.json + :language: javascript + +Delete Assisted Volume Snapshot +=============================== + +.. rest_method:: DELETE /os-assisted-volume-snapshots/{snapshot_id} + +Deletes an assisted volume snapshot. + +To make this request, add the ``delete_info`` query parameter to the URI, as follows: + +DELETE /os-assisted-volume-snapshots/421752a6-acf6-4b2d-bc7a-119f9148cd8c?delete_info='{"volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c"}' + +Normal response codes: 204 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - snapshot_id: snapshot_id_path + - delete_info: delete_info + +Response +-------- + +There is no body content for the response of a successful DELETE query diff --git a/api-ref/source/os-availability-zone.inc b/api-ref/source/os-availability-zone.inc new file mode 100644 index 00000000000..7a54b390b70 --- /dev/null +++ b/api-ref/source/os-availability-zone.inc @@ -0,0 +1,74 @@ +.. -*- rst -*- + +=========================================== + Availability zones (os-availability-zone) +=========================================== + +Lists and gets detailed availability zone information. + +An availability zone is created or updated by setting the +availability_zone parameter in the ``create``, ``update``, or +``create or update`` methods of the Host Aggregates API. +See `Host Aggregates `_ +for more details. + + +Get Availability Zone Information +================================= + +.. rest_method:: GET /os-availability-zone + +Lists availability zone information. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - availabilityZoneInfo: availability_zone_info + - hosts: hosts.availability_zone_none + - zoneName: OS-EXT-AZ:availability_zone + - zoneState: availability_zone_state + - available: available + +| + +**Example Get availability zone information** + +.. literalinclude:: ../../doc/api_samples/os-availability-zone/availability-zone-list-resp.json + :language: javascript + +Get Detailed Availability Zone Information +========================================== + +.. rest_method:: GET /os-availability-zone/detail + +Gets detailed availability zone information. +Policy defaults enable only users with the administrative role to perform this operation. +Cloud providers can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - availabilityZoneInfo: availability_zone_info + - hosts: hosts.availability_zone + - zoneName: OS-EXT-AZ:availability_zone + - zoneState: availability_zone_state + - available: available + +| + +**Example Get detailed availability zone information** + +.. literalinclude:: ../../doc/api_samples/os-availability-zone/availability-zone-detail-resp.json + :language: javascript diff --git a/api-ref/source/os-baremetal-nodes.inc b/api-ref/source/os-baremetal-nodes.inc new file mode 100644 index 00000000000..0208bb453c8 --- /dev/null +++ b/api-ref/source/os-baremetal-nodes.inc @@ -0,0 +1,87 @@ +.. -*- rst -*- + +=================================================== + Bare metal nodes (os-baremetal-nodes) (DEPRECATED) +=================================================== + +.. warning:: + + These APIs are proxy calls to the Ironic service. They exist for + legacy compatibility, but no new applications should use them. + Nova has deprecated all the proxy APIs and users should use the native + APIs instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Bare metal APIs + `__. + +Bare metal nodes. + +List Bare Metal Nodes +===================== + +.. rest_method:: GET /os-baremetal-nodes + +Lists the bare metal nodes known by the compute environment. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), +notImplemented(501) + + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - nodes: baremetal_nodes + - id: baremetal_id + - interfaces: baremetal_interfaces + - host: baremetal_host + - task_state: baremetal_taskstate + - cpus: baremetal_cpus + - memory_mb: baremetal_mem + - disk_gb: baremetal_disk + +**Example List Bare Metal Nodes** + +.. literalinclude:: ../../doc/api_samples/os-baremetal-nodes/baremetal-node-list-resp.json + :language: javascript + +Show Bare Metal Node Details +============================ + +.. rest_method:: GET /os-baremetal-nodes/{node_id} + +Shows details for a bare metal node. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), +itemNotFound(404), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - node_id: node_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - node: baremetal_node + - id: baremetal_id + - instance_uuid: baremetal_instance_uuid + - interfaces: baremetal_interfaces + - host: baremetal_host + - task_state: baremetal_taskstate + - cpus: baremetal_cpus + - memory_mb: baremetal_mem + - disk_gb: baremetal_disk + +**Example Show Bare Metal Node Details** + +.. literalinclude:: ../../doc/api_samples/os-baremetal-nodes/baremetal-node-get-resp.json + :language: javascript diff --git a/api-ref/source/os-cells.inc b/api-ref/source/os-cells.inc new file mode 100644 index 00000000000..cbc20ad42b4 --- /dev/null +++ b/api-ref/source/os-cells.inc @@ -0,0 +1,204 @@ +.. -*- rst -*- +.. needs:parameter_verification +.. needs:example_verification +.. needs:body_verification + + +============================== + Cells (os-cells, capacities) +============================== + +Adds neighbor cells, lists neighbor cells, and shows the capabilities of +the local cell. By default, only administrators can manage cells. + +.. note:: These APIs refer to a Cells v1 deployment which is optional and not + recommended for new deployments of Nova. These are not used with Cells v2 + which is required beginning with the 15.0.0 Ocata release where all Nova + deployments consist of at least one Cells v2 cell. + +List Cells +========== + +.. rest_method:: GET /os-cells + +Lists cells. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - tenant_id: tenant_id + - limit: limit_simple + - offset: offset_simple + +.. TODO(cdent): How do we indicate optionality of a URI parameter? + +Response +-------- + +**Example List Cells: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-cells/cells-list-resp.json + :language: javascript + +Create Cell +=========== + +.. rest_method:: POST /os-cells + +Create a new cell. + +Normal response code: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +NotImplemented(501) + +.. TODO(cdent): need to figure out body stuff for request and response + +.. TODO(cdent): need a sample + +Capacities +========== + +.. rest_method:: GET /os-cells/capacities + +Retrieve capacities. + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +NotImplemented(501) + +.. TODO(cdent): Need to do more digging, no idea. + +List Cells With Details +======================= + +.. rest_method:: GET /os-cells/detail + +Lists cells with details of capabilities. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - limit: limit_simple + - offset: offset_simple + +Response +-------- + +**Example List Cells With Details: JSON response** + +.. TODO(cdent): This sample was initially list with an empty list of cells. + The newly listed sample does not yet exist. + +.. TODO(cdent): literal-include: ../../doc/api_samples/os-cells/cells-list-details-resp.json + :language: javascript + +Info For This Cell +================== + +.. rest_method:: GET /os-cells/info + +Retrieve info about the current cell. + +Normal response code: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +NotImplemented(501) + +.. TODO(cdent): this is weird, data is structured entirely differently. + +Show Cell Data +============== + +.. rest_method:: GET /os-cells/{cell_id} + +Shows data for a cell. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - cell_id: cell_id + +Response +-------- + +**Example Show Cell Data: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-cells/cells-get-resp.json + :language: javascript + +Update a Cell +============= + +.. rest_method:: PUT /os-cells/{cell_od} + + +Update an existing cell. + +Normal response code: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), NotImplemented(501) + +.. TODO(cdent): Figure out what's going on here. + +Delete a Cell +============= + +.. rest_method:: DELETE /os-cells/{cell_id} + +Remove a cell. + +Normal response code: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), NotImplemented(501) + +Show Cell Capacities +==================== + +.. rest_method:: GET /os-cells/{cell_id}/capacities + +Shows capacities for a cell. + +.. TODO(cdent): What's a capacities. + +Normal response codes: 200,501 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - cell_id: cell_id + +Response +-------- + +**Example Show Cell Capacities: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-cells/cells-capacities-resp.json + :language: javascript diff --git a/api-ref/source/os-certificates.inc b/api-ref/source/os-certificates.inc new file mode 100644 index 00000000000..5511836a8b5 --- /dev/null +++ b/api-ref/source/os-certificates.inc @@ -0,0 +1,68 @@ +.. -*- rst -*- + +================================================= + Root certificates (os-certificates) (DEPRECATED) +================================================= + +Creates and shows details for a root certificate. + +.. warning:: + + This API existed solely because of the need to build euca bundles + when Nova had an in tree EC2 API. It no longer interacts with any + parts of the system besides its own certificate daemon. It was + removed in the 16.0.0 Pike release. + +Create Root Certificate +======================= + +.. rest_method:: POST /os-certificates + +Creates a root certificate. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - certificate: certificate + - data: data + - private_key: private_key + +| + +**Example Create Root Certificate** + +.. literalinclude:: ../../doc/api_samples/os-certificates/certificate-create-resp.json + :language: javascript + +Show Root Certificate Details +============================= + +.. rest_method:: GET /os-certificates/root + +Shows details for a root certificate. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), notImplemented(501) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - certificate: certificate + - data: data + - private_key: private_key + +| + +**Example Show Root Certificate Details** + +.. literalinclude:: ../../doc/api_samples/os-certificates/certificate-get-root-resp.json + :language: javascript diff --git a/api-ref/source/os-cloudpipe.inc b/api-ref/source/os-cloudpipe.inc new file mode 100644 index 00000000000..54af4f183ea --- /dev/null +++ b/api-ref/source/os-cloudpipe.inc @@ -0,0 +1,110 @@ +.. -*- rst -*- + +====================================== + Cloudpipe (os-cloudpipe) (DEPRECATED) +====================================== + +.. warning:: + + This API only works with ``nova-network`` which is + deprecated in favor of Neutron. It should be avoided + in any new applications. It was removed in the 16.0.0 + Pike release. + +Manages virtual VPNs for projects. + +List Cloudpipes +=============== + +.. rest_method:: GET /os-cloudpipe + +Lists cloudpipes. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound (404) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - cloudpipes: cloudpipes + - created_at: created + - instance_id: instance_id_cloudpipe + - internal_ip: fixed_ip + - project_id: project_id_instance_action + - public_ip: vpn_public_ip_resp + - public_port: vpn_public_port_resp + - state: vpn_state + +**Example List Cloudpipes: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-cloudpipe/cloud-pipe-get-resp.json + :language: javascript + +Create Cloudpipe +================ + +.. rest_method:: POST /os-cloudpipe + +Creates a cloudpipe. + +Normal response codes: 200 + +Error response codes: badRequest(400),unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - cloudpipe: cloudpipe + - project_id: project_id + +**Example Create Cloudpipe: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-cloudpipe/cloud-pipe-create-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - instance_id: instance_id_cloudpipe + +**Example Create Cloudpipe: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-cloudpipe/cloud-pipe-create-resp.json + :language: javascript + +Update Cloudpipe +================ + +.. rest_method:: PUT /os-cloudpipe/configure-project + +Updates the virtual private network (VPN) IP address and port for a cloudpipe instance. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - configure_project: configure_project_cloudpipe + - vpn_ip: vpn_public_ip + - vpn_port: vpn_public_port + +**Example Update Cloudpipe: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-cloudpipe/cloud-pipe-update-req.json + :language: javascript + +Response +-------- + +There is no body content for the response of a successful PUT request diff --git a/api-ref/source/os-consoles.inc b/api-ref/source/os-consoles.inc new file mode 100644 index 00000000000..b19bf706659 --- /dev/null +++ b/api-ref/source/os-consoles.inc @@ -0,0 +1,179 @@ +.. -*- rst -*- + +=============================================================== + Server consoles (servers, os-consoles, os-console-auth-tokens) +=============================================================== + +Manages server consoles. + +.. note:: This is only used in Xenserver VNC Proxy. + +Lists Consoles +============== + +.. rest_method:: GET /servers/{server_id}/consoles + +Lists all consoles for a server. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - consoles: consoles + - console: console + - console_type: console_type + - id: console_id_in_body + +| + +**Example List Consoles** + +.. literalinclude:: ../../doc/api_samples/consoles/consoles-list-get-resp.json + :language: javascript + + +Create Console +============== + +.. rest_method:: POST /servers/{server_id}/consoles + +Creates a console for a server. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +If successful, this method does not return a response body. + + +Show Console Details +==================== + +.. rest_method:: GET /servers/{server_id}/consoles/{console_id} + +Shows console details for a server. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - console_id: console_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - console: console + - console_type: console_type + - host: console_host + - id: console_id_in_body + - instance_name: instance_name + - password: console_password + - port: port_number + +| + +**Example Show Console Details** + +.. literalinclude:: ../../doc/api_samples/consoles/consoles-get-resp.json + :language: javascript + + +Delete Console +============== + +.. rest_method:: DELETE /servers/{server_id}/consoles/{console_id} + +Deletes a console for a server. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - console_id: console_id + + +Response +-------- + +If successful, this method does not return a response body. + + +Show Console Connection Information +=================================== + +.. rest_method:: GET /os-console-auth-tokens/{console_token} + +Given the console authentication token for a server, +shows the related connection information. + +This method used to be available only for the ``rdp-html5`` console type before +microversion 2.31. Starting from microversion 2.31 it's available for all +console types. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - console_token: console_token + +| + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - console: console + - instance_uuid: instance_id_body + - host: console_host + - port: port_number + - internal_access_path: internal_access_path + +| + +**Example Show Console Authentication Token** + +.. literalinclude:: ../../doc/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json + :language: javascript diff --git a/api-ref/source/os-fixed-ips.inc b/api-ref/source/os-fixed-ips.inc new file mode 100644 index 00000000000..b44307c2ff4 --- /dev/null +++ b/api-ref/source/os-fixed-ips.inc @@ -0,0 +1,84 @@ +.. -*- rst -*- + +====================================== + Fixed IPs (os-fixed-ips) (DEPRECATED) +====================================== + +.. warning:: + + These APIs are proxy calls to the Network service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + +Shows data for a fixed IP, such as host name, CIDR, and address. Also, +reserves and releases a fixed IP address. + +Show Fixed Ip Details +===================== + +.. rest_method:: GET /os-fixed-ips/{fixed_ip} + +Shows details for a fixed IP address. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - fixed_ip: fixed_ip_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - fixed_ip: fixed_ip_obj + - address: ip_address + - cidr: cidr + - host: fixed_ip_host + - hostname: fixed_ip_hostname + - reserved: reserved_fixedip + + +**Example Show Fixed Ip Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-fixed-ips/fixedips-get-resp.json + :language: javascript + +Reserve Or Release A Fixed Ip +============================= + +.. rest_method:: POST /os-fixed-ips/{fixed_ip}/action + +Reserves or releases a fixed IP. + +To reserve a fixed IP address, specify ``reserve`` in the request body. +To release a fixed IP address, specify ``unreserve`` in the request body. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - fixed_ip: fixed_ip_path + - reserve: action_reserve + - unreserve: action_unreserve + + +**Example Reserve Or Release A Fixed Ip: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-fixed-ips/fixedip-post-req.json + :language: javascript + +Response +-------- + +There is no body content for the response of a successful POST operation. diff --git a/api-ref/source/os-flavor-access.inc b/api-ref/source/os-flavor-access.inc new file mode 100644 index 00000000000..5d4b21420c1 --- /dev/null +++ b/api-ref/source/os-flavor-access.inc @@ -0,0 +1,136 @@ +.. -*- rst -*- + +============================================ + Flavors access (flavors, os-flavor-access) +============================================ + +Lists tenants who have access to a private flavor and adds private +flavor access to and removes private flavor access from tenants. By +default, only administrators can manage private flavor access. A private +flavor has ``is_public`` set to ``false`` while a public flavor has +``is_public`` set to ``true``. + +List Flavor Access Information For Given Flavor +=============================================== + +.. rest_method:: GET /flavors/{flavor_id}/os-flavor-access + +Lists flavor access information. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - flavor_id: flavor_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - flavor_access: flavor_access + - tenant_id: tenant_id_body + - flavor_id: flavor_id_body + +**Example List Flavor Access Information For Given Flavor: JSON response** + +.. literalinclude:: ../../doc/api_samples/flavor-access/flavor-access-list-resp.json + :language: javascript + +Add Flavor Access To Tenant (addTenantAccess Action) +==================================================== + +.. rest_method:: POST /flavors/{flavor_id}/action + +Adds flavor access to a tenant and flavor. + +Specify the ``addTenantAccess`` action and the ``tenant`` in the request body. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +- 400 - BadRequest - if the `tenant` is not found in your OpenStack + deployment, a 400 is returned to prevent typos on the API call. + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - flavor_id: flavor_id + - addTenantAccess: addTenantAccess + - tenant: tenant_id_body + +**Example Add Flavor Access To Tenant: JSON request** + +.. literalinclude:: ../../doc/api_samples/flavor-access/flavor-access-add-tenant-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - flavor_access: flavor_access + - tenant_id: tenant_id_body + - flavor_id: flavor_id_body + +**Example Add Flavor Access To Tenant: JSON response** + +.. literalinclude:: ../../doc/api_samples/flavor-access/flavor-access-add-tenant-resp.json + :language: javascript + +Remove Flavor Access From Tenant (removeTenantAccess Action) +============================================================ + +.. rest_method:: POST /flavors/{flavor_id}/action + +Removes flavor access from a tenant and flavor. + +Specify the ``removeTenantAccess`` action and the ``tenant`` in the request body. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +- 400 - BadRequest - if the `tenant` is not found in your OpenStack + deployment, a 400 is returned to prevent typos on the API call. + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - flavor_id: flavor_id + - removeTenantAccess: removeTenantAccess + - tenant: tenant_id_body + +**Example Remove Flavor Access From Tenant: JSON request** + +.. literalinclude:: ../../doc/api_samples/flavor-access/flavor-access-remove-tenant-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - flavor_access: flavor_access + - tenant_id: tenant_id_body + - flavor_id: flavor_id_body + +**Example Remove Flavor Access From Tenant: JSON response** + +.. literalinclude:: ../../doc/api_samples/flavor-access/flavor-access-remove-tenant-resp.json + :language: javascript diff --git a/api-ref/source/os-flavor-extra-specs.inc b/api-ref/source/os-flavor-extra-specs.inc new file mode 100644 index 00000000000..4ea220a9f64 --- /dev/null +++ b/api-ref/source/os-flavor-extra-specs.inc @@ -0,0 +1,190 @@ +.. -*- rst -*- + +====================================================== + Flavors extra-specs (flavors, os-flavor-extra-specs) +====================================================== + +Lists, creates, deletes, and updates the extra-specs or keys for a +flavor. + +List Extra Specs For A Flavor +============================= + +.. rest_method:: GET /flavors/{flavor_id}/os-extra_specs + +Lists all extra specs for a flavor, by ID. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - flavor_id: flavor_id + +Response +-------- + + +.. rest_parameters:: parameters.yaml + + - extra_specs: extra_specs + - key: flavor_extra_spec_key2 + - value: flavor_extra_spec_value + +**Example List Extra Specs For A Flavor: JSON response** + +.. literalinclude:: ../../doc/api_samples/flavor-extra-specs/flavor-extra-specs-list-resp.json + :language: javascript + +Create Extra Specs For A Flavor +=============================== + +.. rest_method:: POST /flavors/{flavor_id}/os-extra_specs + +Creates extra specs for a flavor, by ID. + +.. note:: Please reference: + `Compute Flavors + `__ + for available built-in extra specs under ``Extra Specs`` section. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - flavor_id: flavor_id + - extra_specs: extra_specs + - key: flavor_extra_spec_key2 + - value: flavor_extra_spec_value + +**Example Create Extra Specs For A Flavor: JSON request** + +.. literalinclude:: ../../doc/api_samples/flavor-extra-specs/flavor-extra-specs-create-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - extra_specs: extra_specs + - key: flavor_extra_spec_key2 + - value: flavor_extra_spec_value + +**Example Create Extra Specs For A Flavor: JSON response** + +.. literalinclude:: ../../doc/api_samples/flavor-extra-specs/flavor-extra-specs-create-resp.json + :language: javascript + +Show An Extra Spec For A Flavor +=============================== + +.. rest_method:: GET /flavors/{flavor_id}/os-extra_specs/{flavor_extra_spec_key} + +Shows an extra spec, by key, for a flavor, by ID. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - flavor_id: flavor_id + - flavor_extra_spec_key: flavor_extra_spec_key + + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - key: flavor_extra_spec_key2 + - value: flavor_extra_spec_value + +**Example Show An Extra Spec For A Flavor: JSON response** + +.. literalinclude:: ../../doc/api_samples/flavor-extra-specs/flavor-extra-specs-get-resp.json + :language: javascript + +Update An Extra Spec For A Flavor +================================= + +.. rest_method:: PUT /flavors/{flavor_id}/os-extra_specs/{flavor_extra_spec_key} + +Updates an extra spec, by key, for a flavor, by ID. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - flavor_id: flavor_id + - flavor_extra_spec_key: flavor_extra_spec_key + - key: flavor_extra_spec_key2 + - value: flavor_extra_spec_value + + +**Example Update An Extra Spec For A Flavor: JSON request** + +.. literalinclude:: ../../doc/api_samples/flavor-extra-specs/flavor-extra-specs-update-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + + - key: flavor_extra_spec_key2 + - value: flavor_extra_spec_value + +**Example Update An Extra Spec For A Flavor: JSON response** + +.. literalinclude:: ../../doc/api_samples/flavor-extra-specs/flavor-extra-specs-update-resp.json + :language: javascript + +Delete An Extra Spec For A Flavor +================================= + +.. rest_method:: DELETE /flavors/{flavor_id}/os-extra_specs/{flavor_extra_spec_key} + +Deletes an extra spec, by key, for a flavor, by ID. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - flavor_id: flavor_id + - flavor_extra_spec_key: flavor_extra_spec_key + +Response +-------- + +There is no body content for the response of a successful DELETE action. diff --git a/api-ref/source/os-floating-ip-dns.inc b/api-ref/source/os-floating-ip-dns.inc new file mode 100644 index 00000000000..860b40affc2 --- /dev/null +++ b/api-ref/source/os-floating-ip-dns.inc @@ -0,0 +1,203 @@ +.. -*- rst -*- +.. needs:parameter_verification +.. needs:example_verification +.. needs:body_verification + +========================================================== + Floating IP DNS records (os-floating-ip-dns) (DEPRECATED) +========================================================== + +.. warning:: + + These APIs are proxy calls to the Network service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Network APIs + `__. + +Manages DNS records associated with floating IP addresses. The API +dispatches requests to a DNS driver that is selected at startup. + +List Dns Domains +================ + +.. rest_method:: GET /os-floating-ip-dns + +Lists registered DNS domains published by the DNS drivers. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), notImplemented(501) + +Response +-------- + +**Example List Dns Domains: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ip-dns/floating-ip-dns-list-resp.json + :language: javascript + +Create Or Update Dns Domain +=========================== + +.. rest_method:: PUT /os-floating-ip-dns/{domain} + +Creates or updates a DNS domain. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), +forbidden(403), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - domain: domain + +**Example Create Or Update Dns Domain: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-req.json + :language: javascript + +Response +-------- + +**Example Create Or Update Dns Domain: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-resp.json + :language: javascript + +Delete Dns Domain +================= + +.. rest_method:: DELETE /os-floating-ip-dns/{domain} + +Deletes a DNS domain and all associated host entries. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), +itemNotFound(404), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - domain: domain + +Response +-------- + +List Dns Entries +================ + +.. rest_method:: GET /os-floating-ip-dns/{domain}/entries/{ip} + +Lists DNS entries for a domain and IP. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), +itemNotFound(404), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - domain: domain + - ip: ip + +Response +-------- + +**Example List Dns Entries: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ip-dns/floating-ip-dns-entry-list-resp.json + :language: javascript + +Find Unique Dns Entry +===================== + +.. rest_method:: GET /os-floating-ip-dns/{domain}/entries/{name} + +Finds a unique DNS entry for a domain and name. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), +itemNotFound(404), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - domain: domain + - name: name + +Response +-------- + +**Example Find Unique Dns Entry: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ip-dns/floating-ip-dns-entry-get-resp.json + :language: javascript + +Create Or Update Dns Entry +========================== + +.. rest_method:: PUT /os-floating-ip-dns/{domain}/entries/{name} + +Creates or updates a DNS entry. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - domain: domain + - name: name + +**Example Create Or Update Dns Entry: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-req.json + :language: javascript + +Response +-------- + +**Example Create Or Update Dns Entry: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-resp.json + :language: javascript + +Delete Dns Entry +================ + +.. rest_method:: DELETE /os-floating-ip-dns/{domain}/entries/{name} + +Deletes a DNS entry. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), +itemNotFound(404), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - domain: domain + - name: name + +Response +-------- diff --git a/api-ref/source/os-floating-ip-pools.inc b/api-ref/source/os-floating-ip-pools.inc new file mode 100644 index 00000000000..0740d4cb2b7 --- /dev/null +++ b/api-ref/source/os-floating-ip-pools.inc @@ -0,0 +1,44 @@ +.. -*- rst -*- + +====================================================== + Floating IP pools (os-floating-ip-pools) (DEPRECATED) +====================================================== + +.. warning:: + + These APIs are proxy calls to the Network service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Network APIs + `__. + +Manages groups of floating IPs. + +List Floating Ip Pools +====================== + +.. rest_method:: GET /os-floating-ip-pools + +Lists floating IP pools. + +Policy defaults enable only users with the administrative role or user +who is authorized to operate on tenant to perform this +operation. Cloud providers can change these permissions through the +``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - floating_ip_pools: floating_ip_pools + - name: floating_ip_pool_name + +**Example List Floating Ip Pools: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ip-pools/floatingippools-list-resp.json + :language: javascript diff --git a/api-ref/source/os-floating-ips-bulk.inc b/api-ref/source/os-floating-ips-bulk.inc new file mode 100644 index 00000000000..fcdfd8f03e3 --- /dev/null +++ b/api-ref/source/os-floating-ips-bulk.inc @@ -0,0 +1,163 @@ +.. -*- rst -*- + +====================================================== + Floating IPs bulk (os-floating-ips-bulk) (DEPRECATED) +====================================================== + +.. warning:: + + These APIs are proxy calls to the Network service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Network APIs + `__. + +(nova-network only) Bulk-creates, deletes, and lists floating IPs. +Default pool name is ``nova``. + +To view available pools, use the ``os-floating-ip-pools`` extension. + +List Floating Ips +================= + +.. rest_method:: GET /os-floating-ips-bulk + + +Lists all floating IPs. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - floating_ip_info : floating_ips_list + - address : floating_ip + - fixed_ip : fixed_ip_address + - instance_uuid : server_id + - interface : virtual_interface + - pool: floating_ip_pool_name + - project_id : project_id_value + +**Example List Floating Ips: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.json + :language: javascript + +Create Floating Ips +=================== + +.. rest_method:: POST /os-floating-ips-bulk + +Bulk-creates floating IPs. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - floating_ips_bulk_create : floating_ip_bulk_object + - ip_range : ip_range + - interface : virtual_interface_id_optional + - pool: floating_ip_pool_name_optional + + +**Example Create Floating Ips: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - floating_ips_bulk_create : floating_ip_bulk_object + - interface : virtual_interface + - ip_range : ip_range + - pool: floating_ip_pool_name + +**Example Create Floating Ips: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.json + :language: javascript + +Bulk-Delete Floating Ips +======================== + +.. rest_method:: PUT /os-floating-ips-bulk/delete + +Bulk-deletes floating IPs. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - ip_range: ip_range_delete + +**Example Bulk-Delete Floating Ips: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - floating_ips_bulk_delete : ip_range_delete + +**Example Bulk-Delete Floating Ips: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.json + :language: javascript + +List Floating Ips By Host +========================= + +.. rest_method:: GET /os-floating-ips-bulk/{host_name} + +Lists all floating IPs for a host. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host_name: host_name + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - floating_ip_info : floating_ips_list + - address : floating_ip + - fixed_ip : fixed_ip_address + - instance_uuid : server_id + - interface : virtual_interface + - pool: floating_ip_pool_name + - project_id : project_id_value + + +**Example List Floating Ips By Host: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.json + :language: javascript diff --git a/api-ref/source/os-floating-ips.inc b/api-ref/source/os-floating-ips.inc new file mode 100644 index 00000000000..ad7588ca9da --- /dev/null +++ b/api-ref/source/os-floating-ips.inc @@ -0,0 +1,191 @@ +.. -*- rst -*- + +============================================ + Floating IPs (os-floating-ips) (DEPRECATED) +============================================ + +.. warning:: + + These APIs are proxy calls to the Network service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Network APIs + `__. + +Lists floating IP addresses for a project. Also, creates (allocates) a +floating IP address for a project, shows floating IP address details, +and deletes (deallocates) a floating IP address from a project. + +The cloud administrator configures a pool of floating IP addresses in +OpenStack Compute. The project quota defines the maximum number of +floating IP addresses that you can allocate to the project. After you +`allocate a floating IP +address `__ +for a project, you can: + +- `Add (associate) the floating IP + address `__ + with an instance in the project. You can associate only one floating + IP address with an instance at a time. + +- `Remove (disassociate) the floating IP + address `__ + from an instance in the project. + +- Delete, or deallocate, a floating IP from the project, which + automatically deletes any associations for that IP address. + +List Floating Ip Addresses +========================== + +.. rest_method:: GET /os-floating-ips + +Lists floating IP addresses associated with the tenant or account. + +Policy defaults enable only users with the administrative role +or the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - floating_ips: floating_ips_list + - fixed_ip: fixed_ip_address + - id: floating_ip_id_value + - instance_id: server_id + - ip: floating_ip + - pool: floating_ip_pool_name + +**Example List Floating Ip Addresses** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips/floating-ips-list-resp.json + :language: javascript + +Create (Allocate) Floating Ip Address +===================================== + +.. rest_method:: POST /os-floating-ips + +Creates, or allocates, a floating IP address for the current project. +By default, the floating IP address is allocated from the public pool. + +If more than one floating IP address pool is available, use the +``pool`` parameter to specify from which pool to allocate the IP address. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - pool: floating_ip_pool_name + +**Example Create (Allocate) Floating Ip Address** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips/floating-ips-create-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - floating_ip: floating_ip_obj + - fixed_ip: fixed_ip_address + - id: floating_ip_id_value + - instance_id: server_id + - ip: floating_ip + - pool: floating_ip_pool_name + +**Example Create (Allocate) Floating Ip Address: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips/floating-ips-create-resp.json + :language: javascript + +Show Floating Ip Address Details +================================ + +.. rest_method:: GET /os-floating-ips/{floating_ip_id} + +Shows details for a floating IP address, by ID, that is associated with the tenant or account. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - floating_ip_id: floating_ip_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - floating_ip: floating_ip_obj + - fixed_ip: fixed_ip_address + - id: floating_ip_id_value + - instance_id: server_id + - ip: floating_ip + - pool: floating_ip_pool_name + +**Example Show Floating Ip Address Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-floating-ips/floating-ips-get-resp.json + :language: javascript + +Delete (Deallocate) Floating Ip Address +======================================= + +.. rest_method:: DELETE /os-floating-ips/{floating_ip_id} + +Deletes, or deallocates, a floating IP address from the current project and +returns it to the pool from which it was allocated. + +If the IP address is still associated with a running instance, +it is automatically disassociated from that instance. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), + itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - floating_ip_id: floating_ip_id + +Response +-------- + +There is no body content for the response of a successful DELETE action. diff --git a/api-ref/source/os-fping.inc b/api-ref/source/os-fping.inc new file mode 100644 index 00000000000..46b2a0c3d76 --- /dev/null +++ b/api-ref/source/os-fping.inc @@ -0,0 +1,108 @@ +.. -*- rst -*- + +======================================= + Ping instances (os-fping) (DEPRECATED) +======================================= + +.. warning:: + + This API only works with ``nova-network`` which is + deprecated. It should be avoided in any new applications. + These will fail with a 404 starting from microversion 2.36. + +Pings instances and reports which instances are alive. + +Ping Instances +============== + +.. rest_method:: GET /os-fping + +Runs the fping utility to ping instances and reports which instances are alive. + +Specify the ``all_tenants=1`` query parameter to ping instances for all tenants. For example: + +:: + + GET /os-fping?all_tenants=1 + +Specify the ``include`` and ``exclude`` query parameters to filter the results. For example: + +:: + + GET /os-fping?all_tenants=1&include=uuid1,uuid2&exclude=uuid3,uuid4 + +Policy defaults enable only users with the administrative role or the +owner of the server to perform this operation. Cloud providers can +change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: serviceUnavailable(503), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - all_tenants: all_tenants + - include: include + - exclude: exclude + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - servers: servers + - alive: alive + - id: server_id + - project_id: project_id + +| + +**Example Ping Instances** + +.. literalinclude:: ../../doc/api_samples/os-fping/fping-get-resp.json + :language: javascript + +Ping An Instance +================ + +.. rest_method:: GET /os-fping/{instance_id} + +Runs the fping utility to ping an instance and reports whether the instance is alive. + +Policy defaults enable only users with the administrative role or the +owner of the server to perform this operation. Cloud providers can +change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: serviceUnavailable(503), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - instance_id: instance_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - server: server + - alive: alive + - id: server_id + - project_id: project_id + +| + +**Example Ping An Instance** + +.. literalinclude:: ../../doc/api_samples/os-fping/fping-get-details-resp.json + :language: javascript diff --git a/api-ref/source/os-hosts.inc b/api-ref/source/os-hosts.inc new file mode 100644 index 00000000000..89b93db94bc --- /dev/null +++ b/api-ref/source/os-hosts.inc @@ -0,0 +1,264 @@ +.. -*- rst -*- + +=============================== + Hosts (os-hosts) (DEPRECATED) +=============================== + +.. warning:: + + The ``os-hosts`` API is deprecated as of the 2.43 microversion. Requests + made with microversion >= 2.43 will result in a 404 error. To list and show + host details, use the :ref:`os-hypervisors` API. To enable or disable a + service, use the :ref:`os-services` API. There is no replacement for the + `shutdown`, `startup`, `reboot`, or `maintenance_mode` actions as those are + system-level operations which should be outside of the control of the + compute service. + +Manages physical hosts. Some virt drivers do not support all host +functions. For more information, see `nova virt support +matrix `__ + +Policy defaults enable only users with the administrative role to perform +all os-hosts related operations. Cloud providers can change these permissions +through the ``policy.json`` file. + +List Hosts +========== + +.. rest_method:: GET /os-hosts + +Lists hosts. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - hosts: hosts + - zone: host_zone + - host_name: host_name_body + - service: host_service + +**Example List Hosts** + +.. literalinclude:: ../../doc/api_samples/os-hosts/hosts-list-resp.json + :language: javascript + +Show Host Details +================= + +.. rest_method:: GET /os-hosts/{host_name} + +Shows details for a host. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host_name: host_name + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - host: host_resource_array + - resource: host_resource + - resource.project: host_project + - resource.cpu: host_cpu + - resource.memory_mb: host_memory_mb + - resource.disk_gb: host_disk_gb + - resource.host: host_name_body + +**Example Show Host Details** + +.. literalinclude:: ../../doc/api_samples/os-hosts/host-get-resp.json + :language: javascript + +Update Host status +================== + +.. rest_method:: PUT /os-hosts/{host_name} + +Enables, disables a host or put a host in maintenance or normal mode. + +.. warning:: + + Putting a host into maintenance mode is only implemented by the XenServer + compute driver and it has been reported that it does not actually evacuate + all of the guests from the host, it just sets a flag in the Xen management + console, and is therefore useless. There are other APIs that allow you to do + the same thing which are supported across all compute drivers, which would be + disabling a service and then migrating the instances off that host. See the + `Operations Guide `_ + for more information on maintenance. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host_name: host_name + - status: host_status_body_in + - maintenance_mode: host_maintenance_mode_in + +**Example Enable Host: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-hosts/host-put-maintenance-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - host: host_name_body + - status: host_status_body + - maintenance_mode: host_maintenance_mode + +**Example Enable Host** + +.. literalinclude:: ../../doc/api_samples/os-hosts/host-put-maintenance-resp.json + :language: javascript + +Reboot Host +=========== + +.. rest_method:: GET /os-hosts/{host_name}/reboot + +Reboots a host. + +.. warning:: + + This is only supported by the XenServer and Hyper-v drivers. The backing + drivers do no orchestration of dealing with guests in the nova database when + performing a reboot of the host. The nova-compute service for that host may + be temporarily disabled by the service group health check which would take it + out of scheduling decisions, and the guests would be down, but the periodic + task which checks for unexpectedly stopped instances runs in the nova-compute + service, which might be dead now so the nova API would show the instances as + running when in fact they are actually stopped. This API is also not tested + in a live running OpenStack environment. Needless to say, it is not + recommended to use this API and it is deprecated as of the 2.43 microversion. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host_name: host_name + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - host: host_name_body + - power_action: host_power_action + +**Example Reboot Host: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hosts/host-get-reboot.json + :language: javascript + +Shut Down Host +============== + +.. rest_method:: GET /os-hosts/{host_name}/shutdown + +Shuts down a host. + +.. warning:: + + This is only supported by the XenServer and Hyper-v drivers. The backing + drivers do no orchestration of dealing with guests in the nova database when + performing a shutdown of the host. The nova-compute service for that host may + be temporarily disabled by the service group health check which would take it + out of scheduling decisions, and the guests would be down, but the periodic + task which checks for unexpectedly stopped instances runs in the nova-compute + service, which might be dead now so the nova API would show the instances as + running when in fact they are actually stopped. This API is also not tested + in a live running OpenStack environment. Needless to say, it is not + recommended to use this API and it is deprecated as of the 2.43 microversion. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host_name: host_name + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - host: host_name_body + - power_action: host_power_action + +**Example Shut Down Host** + +.. literalinclude:: ../../doc/api_samples/os-hosts/host-get-shutdown.json + :language: javascript + +Start Host +========== + +.. rest_method:: GET /os-hosts/{host_name}/startup + +Starts a host. + +.. warning:: + + This is not implemented by any in-tree compute drivers and therefore will + always fail with a `501 NotImplemented` error. Needless to say, it is not + recommended to use this API and it is deprecated as of the 2.43 microversion. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host_name: host_name + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - host: host_name_body + - power_action: host_power_action + +**Example Start Host** + +.. literalinclude:: ../../doc/api_samples/os-hosts/host-get-startup.json + :language: javascript diff --git a/api-ref/source/os-hypervisors.inc b/api-ref/source/os-hypervisors.inc new file mode 100644 index 00000000000..5309bbdf024 --- /dev/null +++ b/api-ref/source/os-hypervisors.inc @@ -0,0 +1,381 @@ +.. -*- rst -*- + +.. _os-hypervisors: + +============================== + Hypervisors (os-hypervisors) +============================== + +Lists all hypervisors, shows summary statistics for all hypervisors over +all compute nodes, shows details for a hypervisor, shows the uptime +for a hypervisor, lists all servers on hypervisors that match the given +``hypervisor_hostname_pattern`` or searches for hypervisors by the given +``hypervisor_hostname_pattern``. + +List Hypervisors +================ + +.. rest_method:: GET /os-hypervisors + +Lists hypervisors. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through +the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - limit: hypervisor_limit + - marker: hypervisor_marker + - marker: hypervisor_marker_uuid + - hypervisor_hostname_pattern: hypervisor_hostname_pattern_query + - with_servers: hypervisor_with_servers_query + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - hypervisors: hypervisors + - hypervisor_hostname: hypervisor_hostname + - id: hypervisor_id_body + - id: hypervisor_id_body_uuid + - state: hypervisor_state + - status: hypervisor_status + - hypervisor_links: hypervisor_links + - servers: hypervisor_servers + - servers.uuid: hypervisor_servers_uuid + - servers.name: hypervisor_servers_name + +**Example List Hypervisors (v2.33): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.33/hypervisors-list-resp.json + :language: javascript + +**Example List Hypervisors With Servers (v2.53): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.53/hypervisors-with-servers-resp.json + :language: javascript + +List Hypervisors Details +======================== + +.. rest_method:: GET /os-hypervisors/detail + +Lists hypervisors details. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through +the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - limit: hypervisor_limit + - marker: hypervisor_marker + - marker: hypervisor_marker_uuid + - hypervisor_hostname_pattern: hypervisor_hostname_pattern_query + - with_servers: hypervisor_with_servers_query + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - hypervisors: hypervisors + - cpu_info: cpu_info + - current_workload: current_workload + - status: hypervisor_status + - state: hypervisor_state + - disk_available_least: disk_available_least + - host_ip: host_ip + - free_disk_gb: hypervisor_free_disk_gb + - free_ram_mb: free_ram_mb + - hypervisor_hostname: hypervisor_hostname + - hypervisor_type: hypervisor_type_body + - hypervisor_version: hypervisor_version + - id: hypervisor_id_body + - id: hypervisor_id_body_uuid + - local_gb: local_gb + - local_gb_used: local_gb_used + - memory_mb: memory_mb + - memory_mb_used: memory_mb_used + - running_vms: running_vms + - servers: hypervisor_servers + - servers.uuid: hypervisor_servers_uuid + - servers.name: hypervisor_servers_name + - service: hypervisor_service + - service.host: host_name_body + - service.id: service_id_body_2_52 + - service.id: service_id_body_2_53 + - service.disable_reason: service_disable_reason + - vcpus: hypervisor_vcpus + - vcpus_used: hypervisor_vcpus_used + - hypervisor_links: hypervisor_links + +**Example List Hypervisors Details (v2.33): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.33/hypervisors-detail-resp.json + :language: javascript + +**Example List Hypervisors Details (v2.53): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.53/hypervisors-detail-resp.json + :language: javascript + +Show Hypervisor Statistics +========================== + +.. rest_method:: GET /os-hypervisors/statistics + +Shows summary statistics for all enabled hypervisors over all compute nodes. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through +the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - hypervisor_statistics: hypervisor_statistics + - count: hypervisor_count + - current_workload: current_workload + - disk_available_least: disk_available_least + - free_disk_gb: hypervisor_free_disk_gb + - free_ram_mb: free_ram_mb + - local_gb: local_gb + - local_gb_used: local_gb_used + - memory_mb: memory_mb + - memory_mb_used: memory_mb_used + - running_vms: running_vms_total + - vcpus: hypervisor_vcpus + - vcpus_used: hypervisor_vcpus_used + +**Example Show Hypervisor Statistics: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/hypervisors-statistics-resp.json + :language: javascript + +Show Hypervisor Details +======================= + +.. rest_method:: GET /os-hypervisors/{hypervisor_id} + +Shows details for a given hypervisor. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through +the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - hypervisor_id: hypervisor_id + - hypervisor_id: hypervisor_id_uuid + - with_servers: hypervisor_with_servers_query + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - hypervisor: hypervisor + - cpu_info: cpu_info + - state: hypervisor_state + - status: hypervisor_status + - current_workload: current_workload + - disk_available_least: disk_available_least + - host_ip: host_ip + - free_disk_gb: hypervisor_free_disk_gb + - free_ram_mb: free_ram_mb + - hypervisor_hostname: hypervisor_hostname + - hypervisor_type: hypervisor_type_body + - hypervisor_version: hypervisor_version + - id: hypervisor_id_body + - id: hypervisor_id_body_uuid + - local_gb: local_gb + - local_gb_used: local_gb_used + - memory_mb: memory_mb + - memory_mb_used: memory_mb_used + - running_vms: running_vms + - servers: hypervisor_servers + - servers.uuid: hypervisor_servers_uuid + - servers.name: hypervisor_servers_name + - service: hypervisor_service + - service.host: host_name_body + - service.id: service_id_body_2_52 + - service.id: service_id_body_2_53 + - service.disable_reason: service_disable_reason + - vcpus: hypervisor_vcpus + - vcpus_used: hypervisor_vcpus_used + +**Example Show Hypervisor Details (v2.28): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.28/hypervisors-show-resp.json + :language: javascript + +**Example Show Hypervisor Details With Servers (v2.53): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.53/hypervisors-show-with-servers-resp.json + :language: javascript + +Show Hypervisor Uptime +====================== + +.. rest_method:: GET /os-hypervisors/{hypervisor_id}/uptime + +Shows the uptime for a given hypervisor. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through +the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - hypervisor_id: hypervisor_id + - hypervisor_id: hypervisor_id_uuid + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - hypervisor: hypervisor + - hypervisor_hostname: hypervisor_hostname + - id: hypervisor_id_body + - id: hypervisor_id_body_uuid + - state: hypervisor_state + - status: hypervisor_status + - uptime: uptime + +**Example Show Hypervisor Uptime: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/hypervisors-uptime-resp.json + :language: javascript + +**Example Show Hypervisor Uptime (v2.53): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.53/hypervisors-uptime-resp.json + :language: javascript + +Search Hypervisor +================= + +.. rest_method:: GET /os-hypervisors/{hypervisor_hostname_pattern}/search + max_version: 2.52 + +Search hypervisor by a given hypervisor host name or portion of it. + +.. warning:: This API is deprecated starting with microversion 2.53. Use + `List Hypervisors`_ with the ``hypervisor_hostname_pattern`` query + parameter with microversion 2.53 and later. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through +the ``policy.json`` file. + +Normal response code: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - hypervisor_hostname_pattern: hypervisor_hostname_pattern + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - hypervisors: hypervisors + - hypervisor_hostname: hypervisor_hostname + - id: hypervisor_id_body_no_version + - state: hypervisor_state + - status: hypervisor_status + +**Example Search Hypervisor: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/hypervisors-search-resp.json + :language: javascript + +List Hypervisor Servers +======================= + +.. rest_method:: GET /os-hypervisors/{hypervisor_hostname_pattern}/servers + max_version: 2.52 + +List all servers belong to each hypervisor whose host name is matching +a given hypervisor host name or portion of it. + +.. warning:: This API is deprecated starting with microversion 2.53. Use + `List Hypervisors`_ with the ``hypervisor_hostname_pattern`` and + ``with_servers`` query parameters with microversion 2.53 and later. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through +the ``policy.json`` file. + +Normal response code: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - hypervisor_hostname_pattern: hypervisor_hostname_pattern + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - hypervisors: hypervisors + - hypervisor_hostname: hypervisor_hostname + - id: hypervisor_id_body_no_version + - state: hypervisor_state + - status: hypervisor_status + - servers: servers + - servers.uuid: server_uuid + - servers.name: server_name + +**Example List Hypervisor Servers: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-hypervisors/hypervisors-with-servers-resp.json + :language: javascript diff --git a/api-ref/source/os-instance-actions.inc b/api-ref/source/os-instance-actions.inc new file mode 100644 index 00000000000..c2ac18b5f78 --- /dev/null +++ b/api-ref/source/os-instance-actions.inc @@ -0,0 +1,114 @@ +.. -*- rst -*- + +================================================ + Servers actions (servers, os-instance-actions) +================================================ + +List actions and action details for a server. + +List Actions For Server +======================= + +.. rest_method:: GET /servers/{server_id}/os-instance-actions + +Lists actions for a server. + +Action information of deleted instances can be returned for requests later +than microversion 2.21. + +Policy defaults enable only users with the administrative role or the owner of +the server to perform this operation. Cloud providers can change these permissions +through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + + - instanceActions: instanceActions + - action: action + - instance_uuid: instance_id_body + - message: message + - project_id: project_id_instance_action + - request_id: request_id_body + - start_time: start_time + - user_id: user_id + +**Example List Actions For Server: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-instance-actions/instance-actions-list-resp.json + :language: javascript + + +Show Server Action Details +========================== + +.. rest_method:: GET /servers/{server_id}/os-instance-actions/{request_id} + +Shows details for a server action. + +Action details of deleted instances can be returned for requests later +than microversion 2.21. + +Policy defaults enable only users with the administrative role or the owner of +the server to perform this operation. Cloud providers can change these permissions +through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - request_id: request_id + +Response +-------- + + +.. rest_parameters:: parameters.yaml + + + - instanceAction: instanceAction + - action: action + - instance_uuid: instance_id_body + - message: message + - project_id: project_id_instance_action + - request_id: request_id_body + - start_time: start_time + - user_id: user_id + - events: instance_action_events_2_50 + - events: instance_action_events_2_51 + - events.event: event + - events.start_time: event_start_time + - events.finish_time: event_finish_time + - events.result: event_result + - events.traceback: event_traceback + +**Example Show Server Action Details For Admin (v2.1)** + +.. literalinclude:: ../../doc/api_samples/os-instance-actions/instance-action-get-resp.json + :language: javascript + +**Example Show Server Action Details For Non-Admin (v2.51)** + +.. literalinclude:: ../../doc/api_samples/os-instance-actions/v2.51/instance-action-get-non-admin-resp.json + :language: javascript diff --git a/api-ref/source/os-instance-usage-audit-log.inc b/api-ref/source/os-instance-usage-audit-log.inc new file mode 100644 index 00000000000..e7de56a9c54 --- /dev/null +++ b/api-ref/source/os-instance-usage-audit-log.inc @@ -0,0 +1,99 @@ +.. -*- rst -*- + +======================================================== + Server usage audit log (os-instance-usage-audit-log) +======================================================== + +Audit server usage of the cloud. This API is dependent on the +``instance_usage_audit`` configuration option being set on all compute hosts +where usage auditing is required. + +Policy defaults enable only users with the administrative role to perform +all os-instance-usage-audit-log related operations. Cloud providers can change +these permissions through the ``policy.json`` file. + +List Server Usage Audits +======================== + +.. rest_method:: GET /os-instance_usage_audit_log + +Lists usage audits for all servers on all compute hosts where usage auditing +is configured. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - instance_usage_audit_logs: instance_usage_audit_logs + - hosts_not_run: host_not_run + - log: instance_usage_audit_log + - errors: errors + - instances: instances_usage_audit + - message: instance_usage_audit_log_message + - state: instance_usage_audit_task_state + - num_hosts: host_num + - num_hosts_done: host_done_num + - num_hosts_not_run: host_not_run_num + - num_hosts_running: host_running_num + - overall_status: overall_status + - period_beginning: period_beginning + - period_ending: period_ending + - total_errors: total_errors + - total_instances: total_instances + + +**Example List Usage Audits For All Servers** + +.. literalinclude:: ../../doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-index-get-resp.json + :language: javascript + +List Usage Audits Before Specified Time +======================================= + +.. rest_method:: GET /os-instance_usage_audit_log/{before_timestamp} + +Lists usage audits that occurred before a specified time. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - before_timestamp: before_timestamp + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - instance_usage_audit_log: instance_usage_audit_logs + - hosts_not_run: host_not_run + - log: instance_usage_audit_log + - errors: errors + - instances: instances_usage_audit + - message: instance_usage_audit_log_message + - state: instance_usage_audit_task_state + - num_hosts: host_num + - num_hosts_done: host_done_num + - num_hosts_not_run: host_not_run_num + - num_hosts_running: host_running_num + - overall_status: overall_status + - period_beginning: period_beginning + - period_ending: period_ending + - total_errors: total_errors + - total_instances: total_instances + + +**Example List Usage Audits Before Specified Time** + +.. literalinclude:: ../../doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-show-get-resp.json + :language: javascript diff --git a/api-ref/source/os-interface.inc b/api-ref/source/os-interface.inc new file mode 100644 index 00000000000..62151cbc3eb --- /dev/null +++ b/api-ref/source/os-interface.inc @@ -0,0 +1,175 @@ +.. -*- rst -*- + +========================================= + Port interfaces (servers, os-interface) +========================================= + +List port interfaces, show port interface details of the given server. +Create a port interface and uses it to attach a port to the given server, +detach a port interface from the given server. + +List Port Interfaces +==================== + +.. rest_method:: GET /servers/{server_id}/os-interface + +Lists port interfaces that are attached to a server. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - interfaceAttachments: interfaceAttachments + - port_state: port_state + - fixed_ips: fixed_ips_resp + - ip_address: ip_address + - subnet_id: subnet_id + - mac_addr: mac_addr + - net_id: net_id_resp + - port_id: port_id_resp + +**Example List Port Interfaces: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-attach-interfaces/attach-interfaces-list-resp.json + :language: javascript + +Create Interface +================ + +.. rest_method:: POST /servers/{server_id}/os-interface + +Creates a port interface and uses it to attach a port to a server. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409), computeFault(500), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - interfaceAttachment: interfaceAttachment + - port_id: port_id + - net_id: net_id + - fixed_ips: fixed_ips + - ip_address: ip_address_req + - tag: device_tag_nic_attachment + +**Example Create Interface: JSON request** + +Create interface with ``net_id`` and ``fixed_ips``. + +.. literalinclude:: ../../doc/api_samples/os-attach-interfaces/attach-interfaces-create-net_id-req.json + :language: javascript + +Create interface with ``port_id``. + +.. literalinclude:: ../../doc/api_samples/os-attach-interfaces/attach-interfaces-create-req.json + :language: javascript + +**Example Create Tagged Interface (v2.49): JSON request** + +.. literalinclude:: ../../doc/api_samples/os-attach-interfaces/v2.49/attach-interfaces-create-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - interfaceAttachment: interfaceAttachment_resp + - fixed_ips: fixed_ips_resp + - ip_address: ip_address + - subnet_id: subnet_id + - mac_addr: mac_addr + - net_id: net_id_resp + - port_id: port_id_resp + - port_state: port_state + +**Example Create Interface: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-attach-interfaces/attach-interfaces-create-resp.json + :language: javascript + +Show Port Interface Details +=========================== + +.. rest_method:: GET /servers/{server_id}/os-interface/{port_id} + +Shows details for a port interface that is attached to a server. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - port_id: port_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - interfaceAttachment: interfaceAttachment_resp + - port_state: port_state + - fixed_ips: fixed_ips_resp + - ip_address: ip_address + - subnet_id: subnet_id + - mac_addr: mac_addr + - net_id: net_id_resp + - port_id: port_id_resp + +**Example Show Port Interface Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-attach-interfaces/attach-interfaces-show-resp.json + :language: javascript + +Detach Interface +================ + +.. rest_method:: DELETE /servers/{server_id}/os-interface/{port_id} + +Detaches a port interface from a server. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - port_id: port_id_path + +Response +-------- + +No body is returned on successful request. diff --git a/api-ref/source/os-keypairs.inc b/api-ref/source/os-keypairs.inc new file mode 100644 index 00000000000..e728ef64357 --- /dev/null +++ b/api-ref/source/os-keypairs.inc @@ -0,0 +1,164 @@ +.. -*- rst -*- + +===================== + Keypairs (keypairs) +===================== + +Generates, imports, and deletes SSH keys. + +List Keypairs +============= + +.. rest_method:: GET /os-keypairs + +Lists keypairs that are associated with the account. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - user_id: keypair_user + - limit: keypair_limit + - marker: keypair_marker + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - keypairs: keypairs + - keypair: keypair + - name: keypair_name + - public_key: keypair_public_key + - fingerprint: keypair_fingerprint + - type: keypair_type + - keypairs_links: keypair_links + +**Example List Keypairs (v2.35): JSON response** + +.. literalinclude:: ../../doc/api_samples/keypairs/v2.35/keypairs-list-resp.json + :language: javascript + +Create Or Import Keypair +======================== + +.. rest_method:: POST /os-keypairs + +Generates or imports a keypair. + +Normal response codes: 200, 201 + +.. note:: + + The success status code was changed from 200 to 201 in version 2.2 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - keypair: keypair + - name: keypair_name + - public_key: keypair_public_key_in + - type: keypair_type_in + - user_id: keypair_userid_in + +**Example Create Or Import Keypair (v2.10): JSON request** + +.. literalinclude:: ../../doc/api_samples/keypairs/v2.10/keypairs-import-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - keypair: keypair + - name: keypair_name + - public_key: keypair_public_key + - fingerprint: keypair_fingerprint + - user_id: keypair_userid + - private_key: keypair_private_key + - type: keypair_type + +**Example Create Or Import Keypair (v2.10): JSON response** + +.. literalinclude:: ../../doc/api_samples/keypairs/v2.10/keypairs-import-post-resp.json + :language: javascript + +Show Keypair Details +==================== + +.. rest_method:: GET /os-keypairs/{keypair_name} + +Shows details for a keypair that is associated with the account. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - keypair_name: keypair_name_path + - user_id: keypair_user + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - keypair: keypair + - created_at: created + - deleted: keypair_deleted + - deleted_at: keypair_updated_deleted_at + - fingerprint: keypair_fingerprint + - id: keypair_id + - name: keypair_name + - public_key: keypair_public_key + - updated_at: keypair_updated_deleted_at + - user_id: keypair_userid + - type: keypair_type + +**Example Show Keypair Details (v2.10): JSON response** + +.. literalinclude:: ../../doc/api_samples/keypairs/v2.10/keypairs-get-resp.json + :language: javascript + +Delete Keypair +============== + +.. rest_method:: DELETE /os-keypairs/{keypair_name} + +Deletes a keypair. + +Normal response codes: 202, 204 + +.. note:: + + The normal return code is 204 in version 2.2 to match the fact that + no body content is returned. + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - keypair_name: keypair_name_path + - user_id: keypair_user + +Response +-------- + +There is no body content for the response of a successful DELETE query diff --git a/api-ref/source/os-migrations.inc b/api-ref/source/os-migrations.inc new file mode 100644 index 00000000000..28a2fe48076 --- /dev/null +++ b/api-ref/source/os-migrations.inc @@ -0,0 +1,63 @@ +.. -*- rst -*- + +========================================= + Migrations (os-migrations) (frozen) +========================================= + +Shows data on migrations. + +.. warning:: The old top-level resource `/os-migrations` is frozen, + it won't be extended anymore. Use /servers/{uuid}/migrations instead. + +List Migrations +=============== + +.. rest_method:: GET /os-migrations + +Lists migrations. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through the +``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - hidden: migration_hidden + - host: migration_host + - instance_uuid: migration_instance_uuid + - migration_type: migration_type + - source_compute: migration_source_compute + - status: migration_status + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - created_at: created + - dest_compute: migrate_dest_compute + - dest_host: migrate_dest_host + - dest_node: migrate_dest_node + - id: migration_id + - instance_uuid: server_id + - new_instance_type_id: migration_new_flavor_id + - old_instance_type_id: migration_old_flavor_id + - source_compute: migrate_source_compute + - source_node: migrate_source_node + - status: migrate_status + - updated_at: updated + - migration_type: migration_type_2_23 + - links: migration_links_2_23 + +**Example List Migrations: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-migrations/migrations-get.json + :language: javascript + diff --git a/api-ref/source/os-networks.inc b/api-ref/source/os-networks.inc new file mode 100644 index 00000000000..450cd4981ad --- /dev/null +++ b/api-ref/source/os-networks.inc @@ -0,0 +1,319 @@ +.. -*- rst -*- +.. needs:parameter_verification +.. needs:example_verification +.. needs:body_verification +.. NOTE(sdague): for future verification only worry about the non + deprecated methods in this file. Let's not spend a ton of brain + power on the associate/disassociate that's going away. + +===================================== + Networks (os-networks) (DEPRECATED) +===================================== + +.. warning:: The networks API was designed to work with + ``nova-network``. Some features are proxied to + ``neutron`` when appropriate, but as with all translation + proxies, this is far from perfect compatibility. These + APIs should be avoided in new applications in favor of + using ``neutron`` directly. These will fail with a 404 + starting from microversion 2.36. + See: `Relevant Network APIs + `__. + + +Creates, lists, shows information for, and deletes networks. + +Adds network to a project, disassociates a network from a project, and +disassociates a project from a network. + + +Associates host with and disassociates host from a network. + +List Networks +============= + +.. rest_method:: GET /os-networks + +Lists networks for the project. + +Policy defaults enable all users to perform this operation. Cloud +providers can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +**Example List Networks: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-networks/networks-list-resp.json + :language: javascript + +Create Network +============== + +.. rest_method:: POST /os-networks + +Creates a network. + +Policy defaults enable only users with the administrative role or the +owner of the network to perform this operation. Cloud providers can change +these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), conflict(409), NotImplemented(501) + +Request +------- + +**Example Create Network: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-networks/network-create-req.json + :language: javascript + +Response +-------- + +**Example Create Network: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-networks/network-create-resp.json + :language: javascript + +Add Network +=========== + +.. rest_method:: POST /os-networks/add + +Adds a network to a project. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through the +``policy.json`` file. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), NotImplemented(501) + +Request +------- + +**Example Add Network: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-networks/network-add-req.json + :language: javascript + +Response +-------- + +Show Network Details +==================== + +.. rest_method:: GET /os-networks/{network_id} + +Shows details for a network. + +Policy defaults enable all users to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - network_id: network_id + +Response +-------- + +**Example Show Network Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-networks/network-show-resp.json + :language: javascript + +Delete Network +============== + +.. rest_method:: DELETE /os-networks/{network_id} + +Deletes a network. + +Policy defaults enable only users with the administrative role or the +owner of the network to perform this operation. Cloud providers can change +these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - network_id: network_id + +Response +-------- + +There is no body content for the response of a successful DELETE query. + +Associate Host (DEPRECATED) +=========================== + +.. rest_method:: POST /os-networks/{network_id}/action + +.. warning:: + This API is only available with ``nova-network`` which is + deprecated. It should be avoided in any new applications. + +Associates a network with a host. + +Specify the ``associate_host`` action in the request body. + +Policy defaults enable only users with the administrative role or the owner +of the network to perform this operation. Cloud providers can change these +permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - network_id: network_id + - associate_host: associate_host + +**Example Associate Host to Network: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-networks-associate/network-associate-host-req.json + :language: javascript + +Response +-------- + +There is no body content for the response of a successful POST query. + +Disassociate Network (DEPRECATED) +================================= + +.. rest_method:: POST /os-networks/{network_id}/action + +.. warning:: + This API is only available with ``nova-network`` which is + deprecated. It should be avoided in any new applications. + +Disassociates a network from a project. You can then reuse the network. + +Specify the ``disassociate`` action in the request body. + +Policy defaults enable only users with the administrative role or the +owner of the network to perform this operation. Cloud providers can change +these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - network_id: network_id + +**Example Disassociate Network: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-networks-associate/network-disassociate-req.json + :language: javascript + +Response +-------- + +There is no body content for the response of a successful POST query. + +Disassociate Host (DEPRECATED) +============================== + +.. rest_method:: POST /os-networks/{network_id}/action + +.. warning:: + This API is only available with ``nova-network`` which is + deprecated. It should be avoided in any new applications. + +Disassociates a host from a network. + +Specify the ``disassociate_host`` action in the request body. + +Policy defaults enable only users with the administrative role or the +owner of the network to perform this operation. Cloud providers can change +these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - network_id: network_id + +**Example Disassociate Host from Network: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-networks-associate/network-disassociate-host-req.json + :language: javascript + +Response +-------- + +There is no body content for the response of a successful POST query. + + +Disassociate Project (DEPRECATED) +================================= + +.. rest_method:: POST /os-networks/{network_id}/action + +.. warning:: + This API is only available with ``nova-network`` which is + deprecated. It should be avoided in any new applications. + +Disassociates a project from a network. + +Specify the ``disassociate_project`` action in the request body. + +Policy defaults enable only users with the administrative role or the +owner of the network to perform this operation. Cloud providers can change +these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - network_id: network_id + +**Example Disassociate Project from Network: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-networks-associate/network-disassociate-project-req.json + :language: javascript + +Response +-------- + +There is no body content for the response of a successful POST query. diff --git a/api-ref/source/os-quota-class-sets.inc b/api-ref/source/os-quota-class-sets.inc new file mode 100644 index 00000000000..fbb17c2c28d --- /dev/null +++ b/api-ref/source/os-quota-class-sets.inc @@ -0,0 +1,156 @@ +.. -*- rst -*- + +======================================= + Quota class sets (os-quota-class-sets) +======================================= + +Show, Create or Update the quotas for a Quota Class. +Nova supports implicit 'default' Quota Class only. + +.. note:: + Once a default limit is set via the ``default`` quota class via the API, + that takes precedence over any changes to that resource limit in the + configuration options. In other words, once you've changed things via + the API, you either have to keep those synchronized with the configuration + values or remove the default limit from the database manually as there is + no REST API for removing quota class values from the database. + + For Example: If you updated default quotas for instances, to 20, but + didn't change ``quota_instances`` in your ``nova.conf``, you'd now + have default quota for instances as 20 for all projects. + If you then change ``quota_instances=5`` in nova.conf, but didn't + update the ``default`` quota class via the API, you'll still have + a default quota of 20 for instances regardless of ``nova.conf``. + Refer: `Quotas + `__ + for more details. + +.. warning:: + There is a bug in the v2.1 API until microversion 2.49 and + the legacy v2 compatible API which does not return the + ``server_groups`` and ``server_group_members`` quotas in + GET and PUT ``os-quota-class-sets`` API response, whereas the v2 API + used to return those keys in the API response. + There is workaround to get the ``server_groups`` and + ``server_group_members`` quotas using + "List Default Quotas For Tenant" API in :ref:`os-quota-sets` + but that is per project quota. + This issue is fixed in microversion 2.50, here onwards + ``server_groups`` and ``server_group_members`` keys are + returned in API response body. + +Show the quota for Quota Class +============================== + +.. rest_method:: GET /os-quota-class-sets/{id} + +Show the quota for the Quota Class. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - id: quota_class_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - quota_class_set: quota_class_set + - cores: cores_quota_class + - fixed_ips: fixed_ips_quota_class + - floating_ips: floating_ips_quota_class + - id: quota_class_id_body + - injected_file_content_bytes: injected_file_content_bytes + - injected_file_path_bytes: injected_file_path_bytes + - injected_files: injected_files_quota_class + - instances: instances_quota_class + - key_pairs: key_pairs_quota_class + - metadata_items: metadata_items + - ram: ram_quota_class + - security_group_rules: security_group_rules_quota_class + - security_groups: security_groups_quota_class + - server_groups: server_groups_quota_class + - server_group_members: server_group_members_quota_class + - networks: networks_quota_optional + +**Example Show A Quota Class: JSON response(2.50)** + +.. literalinclude:: ../../doc/api_samples/os-quota-class-sets/v2.50/quota-classes-show-get-resp.json + :language: javascript + +Create or Update Quotas for Quota Class +======================================= + +.. rest_method:: PUT /os-quota-class-sets/{id} + +Update the quotas for the Quota Class. + +If the requested Quota Class is not found in the DB, then the API will create the one. +Only 'default' quota class is valid and used to set the default quotas, all other quota class +would not be used anywhere. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - id: quota_class_id + - quota_class_set: quota_class_set + - cores: cores_quota_class_optional + - fixed_ips: fixed_ips_quota_class_optional + - floating_ips: floating_ips_quota_class_optional + - injected_file_content_bytes: injected_file_content_bytes_quota_optional + - injected_file_path_bytes: injected_file_path_bytes_quota_optional + - injected_files: injected_files_quota_class_optional + - instances: instances_quota_class_optional + - key_pairs: key_pairs_quota_class_optional + - metadata_items: metadata_items_quota_optional + - ram: ram_quota_class_optional + - security_group_rules: security_group_rules_quota_class_optional + - security_groups: security_groups_quota_class_optional + - server_groups: server_groups_quota_class_optional + - server_group_members: server_group_members_quota_optional + - networks: networks_quota_optional + +**Example Update Quotas: JSON request(2.50)** + +.. literalinclude:: ../../doc/api_samples/os-quota-class-sets/v2.50/quota-classes-update-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - quota_class_set: quota_class_set + - cores: cores_quota_class + - fixed_ips: fixed_ips_quota_class + - floating_ips: floating_ips_quota_class + - injected_file_content_bytes: injected_file_content_bytes + - injected_file_path_bytes: injected_file_path_bytes + - injected_files: injected_files_quota_class + - instances: instances_quota_class + - key_pairs: key_pairs_quota_class + - metadata_items: metadata_items + - ram: ram_quota_class + - security_group_rules: security_group_rules_quota_class + - security_groups: security_groups_quota_class + - server_groups: server_groups_quota_class + - server_group_members: server_group_members_quota_class + - networks: networks_quota_optional + +**Example Update Quotas: JSON response(2.50)** + +.. literalinclude:: ../../doc/api_samples/os-quota-class-sets/v2.50/quota-classes-update-post-resp.json + :language: javascript diff --git a/api-ref/source/os-quota-sets.inc b/api-ref/source/os-quota-sets.inc new file mode 100644 index 00000000000..30fef320eca --- /dev/null +++ b/api-ref/source/os-quota-sets.inc @@ -0,0 +1,266 @@ +.. -*- rst -*- + +.. _os-quota-sets: + +============================ + Quota sets (os-quota-sets) +============================ + +Permits administrators, depending on policy settings, to view default +quotas, view details for quotas, revert quotas to defaults, and update +the quotas for a project or a project and user. + +Show A Quota +============ + +.. rest_method:: GET /os-quota-sets/{tenant_id} + +Show the quota for a project or a project and a user. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +- 400 - BadRequest - the tenant_id is not valid in your cloud, perhaps + because it was typoed. + +Request +------- + +.. rest_parameters:: parameters.yaml + + - tenant_id: tenant_id + - user_id: user_id_query_quota + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - quota_set: quota_set + - cores: cores + - fixed_ips: fixed_ips_quota + - floating_ips: floating_ips + - id: quota_tenant_or_user_id_body + - injected_file_content_bytes: injected_file_content_bytes + - injected_file_path_bytes: injected_file_path_bytes + - injected_files: injected_files + - instances: instances + - key_pairs: key_pairs + - metadata_items: metadata_items + - ram: ram + - security_group_rules: security_group_rules_quota + - security_groups: security_groups_quota + - server_groups: server_groups + - server_group_members: server_group_members + - networks: networks_quota_set_optional + +**Example Show A Quota: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-quota-sets/user-quotas-show-get-resp.json + :language: javascript + +Update Quotas +============= + +.. rest_method:: PUT /os-quota-sets/{tenant_id} + +Update the quotas for a project or a project and a user. + +Users can force the update even if the quota has already been used and +the reserved quota exceeds the new quota. To force the update, specify +the ``"force": True`` attribute in the request body, the default value +is ``false``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +- 400 - BadRequest - the tenant_id is not valid in your cloud, perhaps + because it was typoed. + +Request +------- + +.. rest_parameters:: parameters.yaml + + - tenant_id: tenant_id + - user_id: user_id_query_set_quota + - quota_set: quota_set + - force: force + - cores: cores_quota_optional + - fixed_ips: fixed_ips_quota_optional + - floating_ips: floating_ips_quota_optional + - injected_file_content_bytes: injected_file_content_bytes_quota_optional + - injected_file_path_bytes: injected_file_path_bytes_quota_optional + - injected_files: injected_files_quota_optional + - instances: instances_quota_optional + - key_pairs: key_pairs_quota_optional + - metadata_items: metadata_items_quota_optional + - ram: ram_quota_optional + - security_group_rules: security_group_rules + - security_groups: security_groups_quota_optional + - server_groups: server_groups_quota_optional + - server_group_members: server_group_members_quota_optional + - networks: networks_quota_set_optional + +**Example Update Quotas: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-quota-sets/quotas-update-post-req.json + :language: javascript + +**Example Update Quotas with the optional ``force`` attribute: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-quota-sets/quotas-update-force-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - quota_set: quota_set + - cores: cores + - fixed_ips: fixed_ips_quota + - floating_ips: floating_ips + - injected_file_content_bytes: injected_file_content_bytes + - injected_file_path_bytes: injected_file_path_bytes + - injected_files: injected_files + - instances: instances + - key_pairs: key_pairs + - metadata_items: metadata_items + - ram: ram + - security_group_rules: security_group_rules_quota + - security_groups: security_groups_quota + - server_groups: server_groups + - server_group_members: server_group_members + - networks: networks_quota_set_optional + +**Example Update Quotas: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-quota-sets/quotas-update-post-resp.json + :language: javascript + +Revert Quotas To Defaults +========================= + +.. rest_method:: DELETE /os-quota-sets/{tenant_id} + +Reverts the quotas to default values for a project or a project and a user. + +To revert quotas for a project and a user, specify the ``user_id`` query parameter. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - tenant_id: tenant_id + - user_id: user_id_query_quota_delete + +Response +-------- + +There is no body content for the response of a successful DELETE operation. + +List Default Quotas For Tenant +============================== + +.. rest_method:: GET /os-quota-sets/{tenant_id}/defaults + +Lists the default quotas for a project. + +Normal response codes: 200 + +Error response codes: badrequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - tenant_id: tenant_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - quota_set: quota_set + - cores: cores + - fixed_ips: fixed_ips_quota + - floating_ips: floating_ips + - id: quota_tenant_or_user_id_body + - injected_file_content_bytes: injected_file_content_bytes + - injected_file_path_bytes: injected_file_path_bytes + - injected_files: injected_files + - instances: instances + - key_pairs: key_pairs + - metadata_items: metadata_items + - ram: ram + - security_group_rules: security_group_rules_quota + - security_groups: security_groups_quota + - server_groups: server_groups + - server_group_members: server_group_members + - networks: networks_quota_set_optional + +**Example List Default Quotas For Tenant: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-quota-sets/quotas-show-defaults-get-resp.json + :language: javascript + +Show The Detail of Quota +======================== + +.. rest_method:: GET /os-quota-sets/{tenant_id}/detail + +Show the detail of quota for a project or a project and a user. + +To show a quota for a project and a user, specify the ``user_id`` query parameter. + +Normal response codes: 200 + +Error response codes: badrequest(400), unauthorized(401), forbidden(403) + +- 400 - BadRequest - the {tenant_id} is not valid in your cloud, perhaps + because it was typoed. + +Request +------- + +.. rest_parameters:: parameters.yaml + + - tenant_id: tenant_id + - user_id: user_id_query_quota + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - quota_set: quota_set + - cores: cores_quota_details + - fixed_ips: fixed_ips_quota_details + - floating_ips: floating_ips_quota_details + - id: quota_tenant_or_user_id_body + - injected_file_content_bytes: injected_file_content_bytes_quota_details + - injected_file_path_bytes: injected_file_path_bytes_quota_details + - injected_files: injected_files_quota_details + - instances: instances_quota_details + - key_pairs: key_pairs_quota_details + - metadata_items: metadata_items_quota_details + - ram: ram_quota_details + - security_group_rules: security_group_rules_quota_details + - security_groups: security_groups_quota_details + - server_groups: server_groups_quota_details + - server_group_members: server_group_members_quota_details + - networks: networks_quota_set_optional + +**Example Show A Quota: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-quota-sets/quotas-show-detail-get-resp.json + :language: javascript diff --git a/api-ref/source/os-security-group-default-rules.inc b/api-ref/source/os-security-group-default-rules.inc new file mode 100644 index 00000000000..4ed6f309d2c --- /dev/null +++ b/api-ref/source/os-security-group-default-rules.inc @@ -0,0 +1,149 @@ +.. -*- rst -*- + +.. needs:body_verification + +================================================================================ + Rules for default security group (os-security-group-default-rules) (DEPRECATED) +================================================================================ + +.. warning:: + + This API only available with ``nova-network`` which is + deprecated. It should be avoided in any new applications. + These will fail with a 404 starting from microversion 2.36. + +Lists, shows information for, and creates default security group rules. + +List Default Security Group Rules +================================= + +.. rest_method:: GET /os-security-group-default-rules + +Lists default security group rules. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), notImplemented(501) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - security_group_default_rules: security_group_default_rules + - from_port: from_port + - id: secgroup_default_rule_id + - ip_protocol: ip_protocol + - ip_range: secgroup_rule_ip_range + - ip_range.cidr: secgroup_rule_cidr + - to_port: to_port + +**Example List default security group rules: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-security-group-default-rules/security-group-default-rules-list-resp.json + :language: javascript + +Show Default Security Group Rule Details +======================================== + +.. rest_method:: GET /os-security-group-default-rules/{security_group_default_rule_id} + +Shows details for a security group rule. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - security_group_default_rule_id: security_group_default_rule_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - security_group_default_rule: security_group_default_rule + - from_port: from_port + - id: secgroup_default_rule_id + - ip_protocol: ip_protocol + - ip_range: secgroup_rule_ip_range + - ip_range.cidr: secgroup_rule_cidr + - to_port: to_port + +**Example Show default security group rule: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-security-group-default-rules/security-group-default-rules-show-resp.json + :language: javascript + +Create Default Security Group Rule +================================== + +.. rest_method:: POST /os-security-group-default-rules + +Creates a default security group rule. + +If you specify a source port ( ``from_port`` ) or destination port ( ``to_port`` ) value, you must specify an +IP protocol ( ``ip_protocol`` ) value. Otherwise, the operation returns the ``Bad Request (400)`` response code. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - security_group_default_rule: security_group_default_rule + - ip_protocol: ip_protocol + - from_port: from_port + - to_port: to_port + - cidr: secgroup_rule_cidr + +**Example Create default security group rule: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-security-group-default-rules/security-group-default-rules-create-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - security_group_default_rule: security_group_default_rule + - from_port: from_port + - id: secgroup_default_rule_id + - ip_protocol: ip_protocol + - ip_range: secgroup_rule_ip_range + - ip_range.cidr: secgroup_rule_cidr + - to_port: to_port + +**Example Create default security group rule: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-security-group-default-rules/security-group-default-rules-create-resp.json + :language: javascript + +Delete Default Security Group Rule +================================== + +.. rest_method:: DELETE /os-security-group-default-rules/{security_group_default_rule_id} + +Deletes a security group rule. + +Normal response codes: 204 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - security_group_default_rule_id: security_group_default_rule_id + +Response +-------- diff --git a/api-ref/source/os-security-group-rules.inc b/api-ref/source/os-security-group-rules.inc new file mode 100644 index 00000000000..5b7437c0c14 --- /dev/null +++ b/api-ref/source/os-security-group-rules.inc @@ -0,0 +1,104 @@ +.. -*- rst -*- +.. needs:example_verification +.. needs:body_verification + +================================================================ + Rules for security group (os-security-group-rules) (DEPRECATED) +================================================================ + +.. warning:: + + These APIs are proxy calls to the Network service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Network APIs + `__. + +Creates and deletes security group rules. + +Create Security Group Rule +========================== + +.. rest_method:: POST /os-security-group-rules + +Creates a rule for a security group. Either ``cidr`` or ``group_id`` must be +specified when creating a rule. + +.. note:: + nova-network only supports ingress rules. If you want to define egress + rules you must use the Neutron networking service. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - security_group_rule: security_group_rule + - parent_group_id: parent_group_id + - ip_protocol: ip_protocol + - from_port: from_port + - to_port: to_port + - cidr: secgroup_rule_cidr + - group_id: group_id + +.. TODO(sdague): we currently have no samples here + **Example Create security group rule: JSON request** + + .. literalinclude:: ../../doc/api_samples/os-security-group-rules/security-group-rule-create-req.json + :language: javascript + +Response +-------- + +The ``group`` is empty if ``group_id`` was not provided on the request. + +The ``ip_range`` is empty if ``cidr`` was not provided on the request. + +.. rest_parameters:: parameters.yaml + + - security_group_rule: security_group_rule + - ip_protocol: ip_protocol + - from_port: from_port + - to_port: to_port + - ip_range: secgroup_rule_ip_range + - cidr: secgroup_rule_cidr + - id: secgroup_rule_id + - group: group + - parent_group_id: parent_group_id + - name: name + - tenant_id: secgroup_tenant_id_body + +.. TODO(sdague): we currently have no samples here + **Example Create security group rule: JSON response** + + .. literalinclude:: ../../doc/api_samples/os-security-group-rules/security-group-rule-create-resp.json + :language: javascript + +Delete Security Group Rule +========================== + +.. rest_method:: DELETE /os-security-group-rules/{security_group_rule_id} + +Deletes a security group rule. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - security_group_rule_id: security_group_rule_id + +Response +-------- + +There is no body content for the response of a successful DELETE query. diff --git a/api-ref/source/os-security-groups.inc b/api-ref/source/os-security-groups.inc new file mode 100644 index 00000000000..9714dff23ae --- /dev/null +++ b/api-ref/source/os-security-groups.inc @@ -0,0 +1,200 @@ +.. -*- rst -*- +.. needs:parameter_verification +.. needs:example_verification +.. needs:body_verification + +================================================== + Security groups (os-security-groups) (DEPRECATED) +================================================== + +.. warning:: + + These APIs are proxy calls to the Network service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Network APIs + `__. + +Lists, shows information for, creates, updates and deletes security groups. + +List Security Groups +==================== + +.. rest_method:: GET /os-security-groups + +Lists security groups. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - limit: limit_simple + - offset: offset_simple + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - security_groups: security_groups_obj + - description: description + - id: security_group_id_body + - name: name + - rules: rules + - tenant_id: tenant_id_body + + +**Example List security groups: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-security-groups/security-groups-list-get-resp.json + :language: javascript + +Create Security Group +===================== + +.. rest_method:: POST /os-security-groups + +Creates a security group. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - security_group: security_group + - name: name + - description: description + +**Example Create security group: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-security-groups/security-group-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - security_group: security_group + - description: description + - id: security_group_id_body + - name: name + - rules: rules + - tenant_id: tenant_id_body + + +**Example Create security group: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-security-groups/security-groups-create-resp.json + :language: javascript + +Show Security Group Details +=========================== + +.. rest_method:: GET /os-security-groups/{security_group_id} + +Shows details for a security group. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - security_group_id: security_group_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - security_group: security_group + - description: description + - id: security_group_id_body + - name: name + - rules: rules + - tenant_id: tenant_id_body + + +**Example Show security group: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-security-groups/security-groups-get-resp.json + :language: javascript + +Update Security Group +===================== + +.. rest_method:: PUT /os-security-groups/{security_group_id} + +Updates a security group. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - security_group_id: security_group_id + - name: name + - description: description + +**Example Update security group: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-security-groups/security-group-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - security_group: security_group + - description: description + - id: security_group_id_body + - name: name + - rules: rules + - tenant_id: tenant_id_body + + +**Example Update security group: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-security-groups/security-groups-create-resp.json + :language: javascript + +Delete Security Group +===================== + +.. rest_method:: DELETE /os-security-groups/{security_group_id} + +Deletes a security group. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - security_group_id: security_group_id + +Response +-------- + +There is no body content for the response of a successful DELETE query. diff --git a/api-ref/source/os-server-external-events.inc b/api-ref/source/os-server-external-events.inc new file mode 100644 index 00000000000..95eefe67521 --- /dev/null +++ b/api-ref/source/os-server-external-events.inc @@ -0,0 +1,73 @@ +.. -*- rst -*- + +==================================================== + Create external events (os-server-external-events) +==================================================== + +.. warning:: + This is an ``admin`` level service API only designed to be used by + other OpenStack services. The point of this API is to coordinate + between Nova and Neutron (and potentially future services) on + activities they both need to be involved in, such as network + hotplugging. + + Unless you are writing Neutron code you **should not** be using + this API. + +Creates one or more external events. The API dispatches each event to a +server instance. + +Run Events +========== + +.. rest_method:: POST /os-server-external-events + +Creates one or more external events, which the API dispatches to the +host a server is assigned to. If the server is not currently assigned +to a host the event will not be delivered. + +You will receive back the list of events that you submitted, with an +updated ``code`` and ``status`` indicating their level of success. + +Normal response codes: 200, 207 + +A 200 will be returned if all events succeeded, 207 will be returned +if some events could not be processed. The ``code`` attribute for the +event will explain further what went wrong. + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - events: events + - name: event_name + - server_uuid: server_uuid + - status: event_status + - tag: event_tag + +**Example Run Events** + +.. literalinclude:: ../../doc/api_samples/os-server-external-events/event-create-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - events: events + - code: code + - name: event_name + - server_uuid: server_uuid + - status: event_status + - tag: event_tag + +**Example Run Events** + +.. literalinclude:: ../../doc/api_samples/os-server-external-events/event-create-resp.json + :language: javascript diff --git a/api-ref/source/os-server-groups.inc b/api-ref/source/os-server-groups.inc new file mode 100644 index 00000000000..295e11bf38a --- /dev/null +++ b/api-ref/source/os-server-groups.inc @@ -0,0 +1,152 @@ +.. -*- rst -*- + +================================== + Server groups (os-server-groups) +================================== + +Lists, shows information for, creates, and deletes server groups. + +List Server Groups +================== + +.. rest_method:: GET /os-server-groups + +Lists all server groups for the tenant. + +Administrative users can use the ``all_projects`` query parameter to list all server groups for all projects. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - all_projects: all_projects + - limit: limit_simple + - offset: offset_simple + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - server_groups: server_groups_list + - id: server_group_id_body + - name: name_server_group + - policies: policies + - members: members + - metadata: metadata_object + - project_id: project_id_server_group + - user_id: user_id_server_group + +**Example List Server Groups: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-server-groups/server-groups-list-resp.json + :language: javascript + +Create Server Group +=================== + +.. rest_method:: POST /os-server-groups + +Creates a server group. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_group: server_group + - name: name_server_group + - policies: policies + +**Example Create Server Group: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-server-groups/server-groups-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - server_group: server_group + - id: server_group_id_body + - name: name_server_group + - policies: policies + - members: members + - metadata: metadata_object + - project_id: project_id_server_group + - user_id: user_id_server_group + +**Example Create Server Group: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-server-groups/server-groups-post-resp.json + :language: javascript + +Show Server Group Details +========================= + +.. rest_method:: GET /os-server-groups/{server_group_id} + +Shows details for a server group. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_group_id: server_group_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - server_group: server_group + - id: server_group_id_body + - name: name_server_group + - policies: policies + - members: members + - metadata: metadata_object + - project_id: project_id_server_group + - user_id: user_id_server_group + +**Example Show Server Group Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-server-groups/server-groups-get-resp.json + :language: javascript + +Delete Server Group +=================== + +.. rest_method:: DELETE /os-server-groups/{server_group_id} + +Deletes a server group. + +Normal response codes: 204 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_group_id: server_group_id + +Response +-------- + +There is no body content for the response of a successful DELETE action. diff --git a/api-ref/source/os-server-password.inc b/api-ref/source/os-server-password.inc new file mode 100644 index 00000000000..a59c9e02095 --- /dev/null +++ b/api-ref/source/os-server-password.inc @@ -0,0 +1,80 @@ +.. -*- rst -*- + +================================================ + Servers password (servers, os-server-password) +================================================ + +Shows the encrypted administrative password. Also, clears the encrypted +administrative password for a server, which removes it from the metadata +server. + +Show Server Password +==================== + +.. rest_method:: GET /servers/{server_id}/os-server-password + +Shows the administrative password for a server. + +This operation calls the metadata service to query metadata information and +does not read password information from the server itself. + +The password saved in the metadata service is typically encrypted using the +public SSH key injected into this server, so the SSH private key is needed to +read the password. + +Policy defaults enable only users with the administrative role or the owner +of the server to perform this operation. Cloud providers can change these +permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - password: password + +**Example Show Server Password** + +.. literalinclude:: ../../doc/api_samples/os-server-password/get-password-resp.json + :language: javascript + +Clear Admin Password +==================== + +.. rest_method:: DELETE /servers/{server_id}/os-server-password + +Clears the encrypted administrative password for a server, which removes it +from the database. + +This action does not actually change the instance server password. + +Policy defaults enable only users with the administrative role or the owner +of the server to perform this operation. Cloud providers can change these +permissions through the ``policy.json`` file. + +Normal response codes: 204 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +If successful, this method does not return content in the response body. diff --git a/api-ref/source/os-server-tags.inc b/api-ref/source/os-server-tags.inc new file mode 100644 index 00000000000..96fbad73fba --- /dev/null +++ b/api-ref/source/os-server-tags.inc @@ -0,0 +1,187 @@ +.. -*- rst -*- + +============================= + Server tags (servers, tags) +============================= + +Lists tags, creates, replaces or deletes one or more tags for a server, checks +the existence of a tag for a server. + +Available since version 2.26 + +Tags have the following restrictions: + +- Tag is a Unicode bytestring no longer than 60 characters. + +- Tag is a non-empty string. + +- '/' is not allowed to be in a tag name + +- Comma is not allowed to be in a tag name in order to simplify requests that + specify lists of tags + +- All other characters are allowed to be in a tag name + +- Each server can have up to 50 tags. + +List Tags +========= + +.. rest_method:: GET /servers/{server_id}/tags + +Lists all tags for a server. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - tags: tags + +**Example List Tags:** + +.. literalinclude:: ../../doc/api_samples/os-server-tags/v2.26/server-tags-index-resp.json + :language: javascript + +Replace Tags +============ + +.. rest_method:: PUT /servers/{server_id}/tags + +Replaces all tags on specified server with the new set of tags. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - tags: tags + +**Example Replace Tags:** + +.. literalinclude:: ../../doc/api_samples/os-server-tags/v2.26/server-tags-put-all-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - tags: tags + +**Example Replace Tags:** + +.. literalinclude:: ../../doc/api_samples/os-server-tags/v2.26/server-tags-put-all-resp.json + :language: javascript + +Delete All Tags +=============== + +.. rest_method:: DELETE /servers/{server_id}/tags + +Deletes all tags from the specified server. + +Normal response codes: 204 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +There is no body content for the response of a successful DELETE query + +Check Tag Existence +=================== + +.. rest_method:: GET /servers/{server_id}/tags/{tag} + +Checks tag existence on the server. If tag exists response with 204 status code +will be returned. Otherwise returns 404. + +Normal response codes: 204 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - tag: tag + +Add a Single Tag +================ + +.. rest_method:: PUT /servers/{server_id}/tags/{tag} + +Adds a single tag to the server if server has no specified tag. Response code +in this case is 201. + +If the server has specified tag just returns 204. + +Normal response codes: 201, 204 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - tag: tag + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - Location: tag_location + +Delete a Single Tag +=================== + +.. rest_method:: DELETE /servers/{server_id}/tags/{tag} + +Deletes a single tag from the specified server. + +Normal response codes: 204 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - tag: tag + +Response +-------- + +There is no body content for the response of a successful DELETE query diff --git a/api-ref/source/os-services.inc b/api-ref/source/os-services.inc new file mode 100644 index 00000000000..9e9df545b67 --- /dev/null +++ b/api-ref/source/os-services.inc @@ -0,0 +1,336 @@ +.. -*- rst -*- + +.. _os-services: + +================================ + Compute services (os-services) +================================ + +Lists all running Compute services in a region, enables or disables +scheduling for a Compute service and deletes a Compute service. + +For an overview of Compute services, see `OpenStack +Compute `__. + +List Compute Services +===================== + +.. rest_method:: GET /os-services + +Lists all running Compute services. + +Provides details why any services were disabled. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - binary: binary_query + - host: host_query_service + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - services: services + - id: service_id_body_2_52 + - id: service_id_body_2_53 + - binary: binary + - disabled_reason: disabled_reason_body + - host: host_name_body + - state: service_state + - status: service_status + - updated_at: updated + - zone: OS-EXT-AZ:availability_zone + - forced_down: forced_down_2_11 + +**Example List Compute Services** + +.. literalinclude:: ../../doc/api_samples/os-services/v2.11/services-list-get-resp.json + :language: javascript + +Disable Scheduling For A Compute Service +======================================== + +.. rest_method:: PUT /os-services/disable + +Disables scheduling for a Compute service. + +Specify the service by its host name and binary name. + +.. note:: Starting with microversion 2.53 this API is superseded by + ``PUT /os-services/{service_id}``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host: host_name_body + - binary: binary + +**Example Disable Scheduling For A Compute Service** + +.. literalinclude:: ../../doc/api_samples/os-services/service-disable-put-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - service: service + - binary: binary + - host: host_name_body + - status: service_status + +**Example Disable Scheduling For A Compute Service** + +.. literalinclude:: ../../doc/api_samples/os-services/service-disable-put-resp.json + :language: javascript + +Disable Scheduling For A Compute Service and Log Disabled Reason +================================================================ + +.. rest_method:: PUT /os-services/disable-log-reason + +Disables scheduling for a Compute service and logs information to the Compute +service table about why a Compute service was disabled. + +Specify the service by its host name and binary name. + +.. note:: Starting with microversion 2.53 this API is superseded by + ``PUT /os-services/{service_id}``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host: host_name_body + - binary: binary + - disabled_reason: disabled_reason_body + +**Example Disable Scheduling For A Compute Service and Log Disabled Reason** + +.. literalinclude:: ../../doc/api_samples/os-services/service-disable-log-put-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - service: service + - binary: binary + - disabled_reason: disabled_reason_body + - host: host_name_body + - status: service_status + +**Example Disable Scheduling For A Compute Service and Log Disabled Reason** + +.. literalinclude:: ../../doc/api_samples/os-services/service-disable-log-put-resp.json + :language: javascript + +Enable Scheduling For A Compute Service +======================================= + +.. rest_method:: PUT /os-services/enable + +Enables scheduling for a Compute service. + +Specify the service by its host name and binary name. + +.. note:: Starting with microversion 2.53 this API is superseded by + ``PUT /os-services/{service_id}``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host: host_name_body + - binary: binary + +**Example Enable Scheduling For A Compute Service** + +.. literalinclude:: ../../doc/api_samples/os-services/service-enable-put-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - service: service + - binary: binary + - host: host_name_body + - status: service_status + +**Example Enable Scheduling For A Compute Service** + +.. literalinclude:: ../../doc/api_samples/os-services/service-enable-put-resp.json + :language: javascript + +Update Forced Down +================== + +.. rest_method:: PUT /os-services/force-down + +Set or unset ``forced_down`` flag for the service. + +Action ``force-down`` available as of microversion 2.11. + +Specify the service by its host name and binary name. + +.. note:: Starting with microversion 2.53 this API is superseded by + ``PUT /os-services/{service_id}``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host: host_name_body + - binary: binary + - forced_down: forced_down_2_11 + +**Example Update Forced Down** + +.. literalinclude:: ../../doc/api_samples/os-services/v2.11/service-force-down-put-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - service: service + - binary: binary + - host: host_name_body + - forced_down: forced_down_2_11 + +| + +**Example Update Forced Down** + +.. literalinclude:: ../../doc/api_samples/os-services/v2.11/service-force-down-put-resp.json + :language: javascript + +Update Compute Service +====================== + +.. rest_method:: PUT /os-services/{service_id} + +Update a compute service to enable or disable scheduling, including recording a +reason why a compute service was disabled from scheduling. Set or unset the +``forced_down`` flag for the service. + +This API is available starting with microversion 2.53. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - service_id: service_id_path_2_53_no_version + - status: service_status_2_53_in + - disabled_reason: disabled_reason_2_53_in + - forced_down: forced_down_2_53_in + +**Example Disable Scheduling For A Compute Service (v2.53)** + +.. literalinclude:: ../../doc/api_samples/os-services/v2.53/service-disable-log-put-req.json + :language: javascript + +**Example Enable Scheduling For A Compute Service (v2.53)** + +.. literalinclude:: ../../doc/api_samples/os-services/v2.53/service-enable-put-req.json + :language: javascript + +**Example Update Forced Down (v2.53)** + +.. literalinclude:: ../../doc/api_samples/os-services/v2.53/service-force-down-put-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - service: service + - id: service_id_body_2_53_no_version + - binary: binary + - disabled_reason: disabled_reason_body + - host: host_name_body + - state: service_state + - status: service_status + - updated_at: updated + - zone: OS-EXT-AZ:availability_zone + - forced_down: forced_down_2_53_out + +**Example Disable Scheduling For A Compute Service (v2.53)** + +.. literalinclude:: ../../doc/api_samples/os-services/v2.53/service-disable-log-put-resp.json + :language: javascript + +**Example Enable Scheduling For A Compute Service (v2.53)** + +.. literalinclude:: ../../doc/api_samples/os-services/v2.53/service-enable-put-resp.json + :language: javascript + +**Example Update Forced Down (v2.53)** + +.. literalinclude:: ../../doc/api_samples/os-services/v2.53/service-force-down-put-resp.json + :language: javascript + +Delete Compute Service +====================== + +.. rest_method:: DELETE /os-services/{service_id} + +Deletes a Compute service. If it's a nova-compute service, then the +corresponding host will be removed from all the host aggregates as well. + +Normal response codes: 204 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - service_id: service_id_path_2_52 + - service_id: service_id_path_2_53 + +Response +-------- + +If successful, this method does not return content in the response body. diff --git a/api-ref/source/os-simple-tenant-usage.inc b/api-ref/source/os-simple-tenant-usage.inc new file mode 100644 index 00000000000..39c4af81513 --- /dev/null +++ b/api-ref/source/os-simple-tenant-usage.inc @@ -0,0 +1,148 @@ +.. -*- rst -*- + +======================================== + Usage reports (os-simple-tenant-usage) +======================================== + +Reports usage statistics of compute and storage resources periodically +for an individual tenant or all tenants. The usage statistics will include +all instances' CPU, memory and local disk during a specific period. + +Microversion 2.40 added pagination (and ``next`` links) to the usage +statistics via optional ``limit`` and ``marker`` query parameters. If +``limit`` isn't provided, the configurable ``max_limit`` will be used which +currently defaults to 1000. Older microversions will not accept these new +paging query parameters, but they will start to silently limit by +``max_limit``. + +.. code-block:: none + + /os-simple-tenant-usage?limit={limit}&marker={instance_uuid} + /os-simple-tenant-usage/{tenant_id}?limit={limit}&marker={instance_uuid} + +.. note:: + + A tenant's usage statistics may span multiple pages when the number of + instances exceeds ``limit``, and API consumers will need to stitch together + the aggregate results if they still want totals for all instances in a + specific time window, grouped by tenant. + +List Tenant Usage Statistics For All Tenants +============================================ + +.. rest_method:: GET /os-simple-tenant-usage + +Lists usage statistics for all tenants. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - detailed: detailed_simple_tenant_usage + - end: end_simple_tenant_usage + - start: start_simple_tenant_usage + - limit: usage_limit + - marker: usage_marker + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - tenant_usages: tenant_usages + - start: start_simple_tenant_usage_body + - stop: stop_simple_tenant_usage + - tenant_id: tenant_id_body + - total_hours: total_hours + - total_local_gb_usage: total_local_gb_usage + - total_memory_mb_usage: total_memory_mb_usage + - total_vcpus_usage: total_vcpus_usage + - server_usages: server_usages_optional + - server_usages.ended_at: ended_at_optional + - server_usages.flavor: flavor_name_optional + - server_usages.hours: hours_optional + - server_usages.instance_id: server_id_optional + - server_usages.local_gb: local_gb_simple_tenant_usage_optional + - server_usages.memory_mb: memory_mb_simple_tenant_usage_optional + - server_usages.name: server_name_optional + - server_usages.started_at: started_at_optional + - server_usages.state: vm_state_optional + - server_usages.tenant_id: tenant_id_optional + - server_usages.uptime: uptime_simple_tenant_usage_optional + - server_usages.vcpus: vcpus_optional + - tenant_usages_links: usage_links + +**Example List Tenant Usage For All Tenants (v2.40): JSON response** + +If the ``detailed`` query parameter is not specified or +is set to other than 1 (e.g. ``detailed=0``), the response is as follows: + +.. literalinclude:: ../../doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get.json + :language: javascript + +If the ``detailed`` query parameter is set to one (``detailed=1``), +the response includes ``server_usages`` information for each tenant. +The response is as follows: + +.. literalinclude:: ../../doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get-detail.json + :language: javascript + +Show Usage Statistics For Tenant +================================ + +.. rest_method:: GET /os-simple-tenant-usage/{tenant_id} + +Shows usage statistics for a tenant. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - tenant_id: tenant_id + - end: end_simple_tenant_usage + - start: start_simple_tenant_usage + - limit: usage_limit + - marker: usage_marker + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - tenant_usage: tenant_usage + - server_usages: server_usages + - server_usages.ended_at: ended_at + - server_usages.flavor: flavor_name + - server_usages.hours: hours + - server_usages.instance_id: server_id + - server_usages.local_gb: local_gb_simple_tenant_usage + - server_usages.memory_mb: memory_mb_simple_tenant_usage + - server_usages.name: server_name + - server_usages.started_at: started_at + - server_usages.state: OS-EXT-STS:vm_state + - server_usages.tenant_id: tenant_id_body + - server_usages.uptime: uptime_simple_tenant_usage + - server_usages.vcpus: vcpus + - start: start_simple_tenant_usage_body + - stop: stop_simple_tenant_usage + - tenant_id: tenant_id_body + - total_hours: total_hours + - total_local_gb_usage: total_local_gb_usage + - total_memory_mb_usage: total_memory_mb_usage + - total_vcpus_usage: total_vcpus_usage + - tenant_usage_links: usage_links + +**Example Show Usage Details For Tenant (v2.40): JSON response** + +.. literalinclude:: ../../doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get-specific.json + :language: javascript diff --git a/api-ref/source/os-tenant-network.inc b/api-ref/source/os-tenant-network.inc new file mode 100644 index 00000000000..c464bdc33cf --- /dev/null +++ b/api-ref/source/os-tenant-network.inc @@ -0,0 +1,137 @@ +.. -*- rst -*- +.. needs:parameter_verification +.. needs:example_verification +.. needs:body_verification + +=================================================== + Project networks (os-tenant-networks) (DEPRECATED) +=================================================== + +.. warning:: + + These APIs are proxy calls to the Network service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Network APIs + `__. + +Creates, lists, shows information for, and deletes project networks. + +List Project Networks +===================== + +.. rest_method:: GET /os-tenant-networks + +Lists all project networks. + +Policy defaults enable only users with the administrative role or +the owner of the network to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +**Example List Project Networks: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-tenant-networks/networks-list-res.json + :language: javascript + +Create Project Network +====================== + +.. rest_method:: POST /os-tenant-networks + +.. note:: + This API is only implemented for the nova-network service and will result + in a 503 error response if the cloud is using the Neutron networking + service. Use the Neutron ``networks`` API to create a new network. + +Creates a project network. + +Policy defaults enable only users with the administrative role to +perform this operation. Cloud providers can change these permissions +through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), conflict(409), serviceUnavailable(503) + +**Example Create Project Network: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-tenant-networks/networks-post-req.json + :language: javascript + +Response +-------- + +**Example Create Project Network: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-tenant-networks/networks-post-res.json + :language: javascript + +Show Project Network Details +============================ + +.. rest_method:: GET /os-tenant-networks/{network_id} + +Shows details for a project network. + +Policy defaults enable only users with the administrative role or +the owner of the network to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - network_id: network_id + +Response +-------- + +**Example Show Project Network Details: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-tenant-networks/networks-post-res.json + :language: javascript + +Delete Project Network +====================== + +.. rest_method:: DELETE /os-tenant-networks/{network_id} + +.. note:: + This API is only implemented for the nova-network service and will result + in a 500 error response if the cloud is using the Neutron networking + service. Use the Neutron ``networks`` API to delete an existing network. + +Deletes a project network. + +Policy defaults enable only users with the administrative role or +the owner of the network to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - network_id: network_id + +Response +-------- + +There is no body content for the response of a successful DELETE query. diff --git a/api-ref/source/os-virtual-interfaces.inc b/api-ref/source/os-virtual-interfaces.inc new file mode 100644 index 00000000000..3390bdf1adb --- /dev/null +++ b/api-ref/source/os-virtual-interfaces.inc @@ -0,0 +1,69 @@ +.. -*- rst -*- + +========================================================================= + Servers virtual interfaces (servers, os-virtual-interfaces) (DEPRECATED) +========================================================================= + +Lists virtual interfaces for a server. + +.. warning:: Since this API is only implemented for the nova-network, the API + is deprecated from the Microversion 2.44. This API will fail with + a 404 starting from microversion 2.44. To query the server attached + neutron interface, please use the API + ``GET /servers/{server_uuid}/os-interface``. + +.. note:: + This API is only implemented for the nova-network service and will result + in a 400 error response if the cloud is using the Neutron networking + service. Use the Neutron ``ports`` API to list ports for a given server by + filtering ports based on the port ``device_id`` which is the + ``{server_id}``. + +List Virtual Interfaces +======================= + +.. rest_method:: GET /servers/{server_id}/os-virtual-interfaces + +Lists the virtual interfaces for an instance. + +Policy defaults enable only users with the administrative role or the owner of +the server to perform this operation. Change these permissions through the +``policy.json`` file. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - limit: limit_simple + - offset: offset_simple + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - virtual_interfaces: virtual_interfaces + - id: virtual_interface_id + - mac_address: mac_address + - net_id: net_id_resp_2_12 + +.. note:: + + The API v2 returns the network ID in the "OS-EXT-VIF-NET:net_id" response + attribute. But API v2.1 base version does not return the network ID. + Network ID has been added in v2.12 micro-version and returns it in the + "net_id" attribute. + + +**Example List Virtual Interfaces: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-virtual-interfaces/v2.12/vifs-list-resp.json + :language: javascript + diff --git a/api-ref/source/os-volume-attachments.inc b/api-ref/source/os-volume-attachments.inc new file mode 100644 index 00000000000..ab2d9b40e4b --- /dev/null +++ b/api-ref/source/os-volume-attachments.inc @@ -0,0 +1,192 @@ +.. -*- rst -*- + +=================================================================== + Servers with volume attachments (servers, os-volume\_attachments) +=================================================================== + +Attaches volumes that are created through the volume API to server +instances. Also, lists volume attachments for a server, shows +details for a volume attachment, and detaches a volume. + +List volume attachments for an instance +======================================= + +.. rest_method:: GET /servers/{server_id}/os-volume_attachments + +List volume attachments for an instance. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - limit: limit_simple + - offset: offset_simple + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - volumeAttachments: volumeAttachments + - device: device_resp + - id: attachment_id_required + - serverId: server_id + - volumeId: volumeId_resp + +**Example List volume attachments for an instance: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-volumes/list-volume-attachments-resp.json + :language: javascript + +Attach a volume to an instance +============================== + +.. rest_method:: POST /servers/{server_id}/os-volume_attachments + +Attach a volume to an instance. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +.. note:: From v2.20 attach a volume to an instance in SHELVED or SHELVED_OFFLOADED + state is allowed. + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - volumeAttachment: volumeAttachment_post + - volumeId: volumeId + - device: device + - tag: device_tag_bdm_attachment + +**Example Attach a volume to an instance: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-volumes/attach-volume-to-server-req.json + :language: javascript + +**Example Attach a volume to an instance and tag it (v2.49): JSON request** + +.. literalinclude:: ../../doc/api_samples/os-volumes/v2.49/attach-volume-to-server-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - volumeAttachment: volumeAttachment + - device: device_resp + - id: attachment_id_required + - serverId: server_id + - volumeId: volumeId_resp + +**Example Attach a volume to an instance: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-volumes/attach-volume-to-server-resp.json + :language: javascript + +Show a detail of a volume attachment +==================================== + +.. rest_method:: GET /servers/{server_id}/os-volume_attachments/{volume_id} + +Show a detail of a volume attachment. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - volume_id: volume_id + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - volumeAttachment: volumeAttachment + - device: device_resp + - id: attachment_id_required + - serverId: server_id + - volumeId: volumeId_resp + +**Example Show a detail of a volume attachment: JSON response** + +.. literalinclude:: ../../doc/api_samples/os-volumes/volume-attachment-detail-resp.json + :language: javascript + +Update a volume attachment +========================== + +.. rest_method:: PUT /servers/{server_id}/os-volume_attachments/{attachment_id} + +Update a volume attachment. + +.. note:: This action only valid when the server is in ACTIVE, PAUSED and RESIZED state, + or a conflict(409) error will be returned. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - attachment_id: attachment_id + - volumeAttachment: volumeAttachment_put + - volumeId: volumeId_swap + +**Example Update a volume attachment: JSON request** + +.. literalinclude:: ../../doc/api_samples/os-volumes/update-volume-req.json + :language: javascript + +Response +-------- + +No body is returned on successful request. + +Detach a volume from an instance +================================ + +.. rest_method:: DELETE /servers/{server_id}/os-volume_attachments/{volume_id} + +Detach a volume from an instance. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +.. note:: From v2.20 detach a volume from an instance in SHELVED or SHELVED_OFFLOADED + state is allowed. + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - volume_id: volume_id + +Response +-------- + +No body is returned on successful request. diff --git a/api-ref/source/os-volumes.inc b/api-ref/source/os-volumes.inc new file mode 100644 index 00000000000..1b99a80896f --- /dev/null +++ b/api-ref/source/os-volumes.inc @@ -0,0 +1,434 @@ +.. -*- rst -*- + +========================================================= + Volume extension (os-volumes, os-snapshots) (DEPRECATED) +========================================================= + +.. warning:: + + These APIs are proxy calls to the Volume service. Nova has + deprecated all the proxy APIs and users should use the native APIs + instead. These will fail with a 404 starting from microversion 2.36. + See: `Relevant Volume APIs + `__. + +Manages volumes and snapshots for use with the Compute API. +Lists, shows details, creates, and deletes volumes and snapshots. + +List Volumes +============ + +.. rest_method:: GET /os-volumes + +Lists the volumes associated with the account. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - limit: limit_simple + - offset: offset_simple + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - volumes: volumes + - attachments: volumeAttachments + - attachments.device: attachment_device_resp + - attachments.id: attachment_id_resp + - attachments.serverId: attachment_server_id_resp + - attachments.volumeId: attachment_volumeId_resp + - availabilityZone: OS-EXT-AZ:availability_zone + - createdAt: created + - displayDescription: display_description + - displayName: display_name + - id: volume_id_resp + - metadata: metadata_object + - size: size + - snapshotId: snapshot_id + - status: volume_status + - volumeType: volume_type + +| + +**Example List Volumes** + +.. literalinclude:: ../../doc/api_samples/os-volumes/os-volumes-index-resp.json + :language: javascript + +Create Volume +============= + +.. rest_method:: POST /os-volumes + +Creates a new volume. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - volume: volume + - size: size + - availability_zone: OS-EXT-AZ:availability_zone_optional + - display_name: display_name_optional + - display_description: display_description_optional + - metadata: metadata + - volume_type: volume_type_optional + - snapshot_id: snapshot_id_optional + +**Example Create Volume** + +.. literalinclude:: ../../doc/api_samples/os-volumes/os-volumes-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - volume: volume + - attachments: volumeAttachments + - attachments.device: attachment_device_resp + - attachments.id: attachment_id_resp + - attachments.serverId: attachment_server_id_resp + - attachments.volumeId: attachment_volumeId_resp + - availabilityZone: OS-EXT-AZ:availability_zone + - createdAt: created + - displayName: display_name + - displayDescription: display_description + - id: volume_id_resp + - metadata: metadata_object + - size: size + - snapshotId: snapshot_id + - status: volume_status + - volumeType: volume_type + +| + +**Example Create Volume** + +.. literalinclude:: ../../doc/api_samples/os-volumes/os-volumes-post-resp.json + :language: javascript + +List Volumes With Details +========================= + +.. rest_method:: GET /os-volumes/detail + +Lists all volumes with details. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - limit: limit_simple + - offset: offset_simple + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - volumes: volumes + - attachments: volumeAttachments + - attachments.device: attachment_device_resp + - attachments.id: attachment_id_resp + - attachments.serverId: attachment_server_id_resp + - attachments.volumeId: attachment_volumeId_resp + - availabilityZone: OS-EXT-AZ:availability_zone + - createdAt: created + - displayName: display_name + - displayDescription: display_description + - id: volume_id_resp + - metadata: metadata_object + - size: size + - snapshotId: snapshot_id + - status: volume_status + - volumeType: volume_type + +| + +**Example List Volumes With Details** + +.. literalinclude:: ../../doc/api_samples/os-volumes/os-volumes-detail-resp.json + :language: javascript + +Show Volume Details +=================== + +.. rest_method:: GET /os-volumes/{volume_id} + +Shows details for a given volume. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - volume_id: volume_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - volume: volume + - attachments: volumeAttachments + - attachment.device: attachment_device_resp + - attachments.id: attachment_id_resp + - attachments.serverId: attachment_server_id_resp + - attachments.volumeId: attachment_volumeId_resp + - availabilityZone: OS-EXT-AZ:availability_zone + - createdAt: created + - displayName: display_name + - displayDescription: display_description + - id: volume_id_resp + - metadata: metadata_object + - size: size + - snapshotId: snapshot_id + - status: volume_status + - volumeType: volume_type + +| + +**Example Show Volume Details** + +.. literalinclude:: ../../doc/api_samples/os-volumes/os-volumes-get-resp.json + :language: javascript + +Delete Volume +============= + +.. rest_method:: DELETE /os-volumes/{volume_id} + +Deletes a volume. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - volume_id: volume_id_path + +Response +-------- + +There is no body content for the response of a successful DELETE query + + +List Snapshots +============== + +.. rest_method:: GET /os-snapshots + +Lists snapshots. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - limit: limit_simple + - offset: offset_simple + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - snapshots: snapshots + - id: snapshot_id + - createdAt: created + - displayName: snapshot_name + - displayDescription: snapshot_description + - size: size + - status: snapshot_status + - volumeId: volume_id + +| + +**Example List Snapshots** + +.. literalinclude:: ../../doc/api_samples/os-volumes/snapshots-list-resp.json + :language: javascript + +Create Snapshot +=============== + +.. rest_method:: POST /os-snapshots + +Creates a new snapshot. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - snapshot: snapshot + - volume_id: volume_id + - display_description: snapshot_description_optional + - display_name: snapshot_name_optional + - force: force_snapshot + +**Example Create Snapshot** + +.. literalinclude:: ../../doc/api_samples/os-volumes/snapshot-create-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - snapshot: snapshot + - id: snapshot_id + - createdAt: created + - displayName: snapshot_name + - displayDescription: snapshot_description + - volumeId: volume_id + - size: size + - status: snapshot_status + +**Example Create Snapshot** + +.. literalinclude:: ../../doc/api_samples/os-volumes/snapshot-create-resp.json + :language: javascript + +List Snapshots With Details +=========================== + +.. rest_method:: GET /os-snapshots/detail + +Lists all snapshots with details. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - limit: limit_simple + - offset: offset_simple + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - snapshots: snapshots + - id: snapshot_id + - createdAt: created + - displayName: snapshot_name + - displayDescription: snapshot_description + - volumeId: volume_id + - size: size + - status: snapshot_status + +| + +**Example List Snapshots With Details** + +.. literalinclude:: ../../doc/api_samples/os-volumes/snapshots-detail-resp.json + :language: javascript + +Show Snapshot Details +===================== + +.. rest_method:: GET /os-snapshots/{snapshot_id} + +Shows details for a given snapshot. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - snapshot_id: snapshot_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - snapshot: snapshot + - id: snapshot_id + - createdAt: created + - displayName: snapshot_name + - displayDescription: snapshot_description + - volumeId: volume_id + - size: size + - status: snapshot_status + +| + +**Example Show Snapshot Details** + +.. literalinclude:: ../../doc/api_samples/os-volumes/snapshots-show-resp.json + :language: javascript + +Delete Snapshot +=============== + +.. rest_method:: DELETE /os-snapshots/{snapshot_id} + +Deletes a snapshot from the account. + +This operation is asynchronous. You must list snapshots repeatedly to determine whether the snapshot was deleted. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - snapshot_id: snapshot_id_path + + +Response +-------- + +There is no body content for the response of a successful DELETE query diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml new file mode 100644 index 00000000000..7e225e6bfef --- /dev/null +++ b/api-ref/source/parameters.yaml @@ -0,0 +1,5957 @@ +# variables in header +image_location: + description: | + The image location URL of the image or backup created, HTTP header + "Location: " will be returned. + + .. note:: The URL returned may not be accessible to users and should not + be relied upon. Use microversion 2.45 or simply parse the image ID out + of the URL in the Location response header. + in: header + required: true + type: string + max_version: 2.44 +server_location: + description: | + The location URL of the server, HTTP header + "Location: " will be returned. + in: header + required: true + type: string +tag_location: + description: | + The location of the tag. It's individual tag URL which can be used for + checking the existence of the tag on the server or deleting the tag from the server. + in: header + required: true + type: string + min_version: 2.26 +x-compute-request-id_resp: + description: | + The local request ID, which is a unique ID generated automatically + for tracking each request to nova. + It is associated with the request and appears in the log lines + for that request. + By default, the middleware configuration + ensures that the local request ID appears in the log files. + + .. note:: + + This header exists for backward compatibility. + in: header + required: true + type: string +x-openstack-request-id_req: + description: | + The global request ID, which is a unique common ID + for tracking each request in OpenStack components. + The format of the global request ID must be ``req-`` + UUID (UUID4). + If not in accordance with the format, it is ignored. + It is associated with the request and appears in the log lines + for that request. + By default, the middleware configuration ensures that + the global request ID appears in the log files. + in: header + required: false + type: string + min_version: 2.46 +x-openstack-request-id_resp: + description: | + The local request ID, which is a unique ID generated automatically + for tracking each request to nova. + It is associated with the request and appears in the log lines + for that request. + By default, the middleware configuration + ensures that the local request ID appears in the log files. + in: header + required: true + type: string + min_version: 2.46 + +# variables in path +agent_build_id: + description: | + The id of the agent build. + in: path + required: true + type: string +aggregate_id: + description: | + The aggregate ID. + in: path + required: true + type: integer +api_version: + in: path + required: true + type: string + description: > + The API version as returned in the links from the ``GET /`` call. +attachment_id: + description: | + The UUID of the attachment. + in: path + required: true + type: string +before_timestamp: + description: | + Filters the response by the date and time before which to list usage + audits. + The date and time stamp format is as follows: + + :: + + CCYY-MM-DD hh:mm:ss.NNNNNN + + For example, ``2015-08-27 09:49:58`` or ``2015-08-27 09:49:58.123456``. + in: path + required: true + type: string +cell_id: + description: | + The UUID of the cell. + in: path + required: true + type: string +console_id: + description: | + The UUID of the console. + in: path + required: true + type: string +console_token: + description: | + Console authentication token. + in: path + required: true + type: string +# Used in the request path for PUT /os-services/disable-log-reason before +# microversion 2.53. +disabled_reason: + description: | + The reason for disabling a service. + in: path + required: false + type: string +domain: + description: | + The registered DNS domain that the DNS drivers publish. + in: path + required: true + type: string +fixed_ip_path: + description: | + The fixed IP of interest to you. + in: path + required: true + type: string +flavor_extra_spec_key: + description: | + The extra spec key for the flavor. + in: path + required: true + type: string +flavor_id: + description: | + The ID of the flavor. + in: path + required: true + type: string +floating_ip_id: + description: | + The ID of the floating IP address. + in: path + required: true + type: string +host_name: + description: | + The name of the host. + in: path + required: true + type: string +hypervisor_hostname_pattern: + description: | + The hypervisor host name or a portion of it. + The hypervisor hosts are selected with the host name matching this pattern. + in: path + required: true + type: string +hypervisor_id: + description: | + The ID of the hypervisor. + in: path + required: true + type: integer + max_version: 2.52 +hypervisor_id_uuid: + description: | + The ID of the hypervisor as a UUID. + in: path + required: true + type: string + min_version: 2.53 +image_id: + description: | + The UUID of the image. + in: path + required: true + type: string +instance_id: + description: | + The UUID of the instance. + in: path + required: true + type: string +ip: + description: | + The IP address. + in: path + required: true + type: string +key: + description: | + The metadata item key, as a string. Maximum length is 255 characters. + in: path + required: true + type: string +keypair_name_path: + description: | + The keypair name. + in: path + required: true + type: string +migration_id_path: + description: | + The ID of the server migration. + in: path + required: true + type: integer +network_id: + description: | + The UUID of the network. + in: path + required: true + type: string +network_label: + description: | + The network label, such as ``public`` or ``private``. + in: path + required: true + type: string +node_id: + description: | + The node ID. + in: path + required: true + type: string +port_id_path: + description: | + The UUID of the port. + in: path + required: true + type: string +quota_class_id: "a_class_id + description: | + The ID of the quota class. + Nova supports the ``default`` Quota Class only. + in: path + required: true + type: string +request_id: + description: | + The ID of the request. + in: path + required: true + type: string +security_group_default_rule_id: + description: | + The UUID of the security group rule. + in: path + required: true + type: string +security_group_id: + description: | + The ID of the security group. + in: path + required: true + type: string +security_group_rule_id: + description: | + The ID of the security group rule. + in: path + required: true + type: string +server_group_id: + description: | + The UUID of the server group. + in: path + required: true + type: string +server_id_path: + description: | + The UUID of the server. + in: path + required: true + type: string +service_id_path_2_52: + description: | + The id of the service. + + .. note:: This may not uniquely identify a service in a multi-cell + deployment. + in: path + required: true + type: integer + max_version: 2.52 +service_id_path_2_53: + description: | + The id of the service as a uuid. This uniquely identifies the service in a + multi-cell deployment. + in: path + required: true + type: string + min_version: 2.53 +service_id_path_2_53_no_version: + description: | + The id of the service as a uuid. This uniquely identifies the service in a + multi-cell deployment. + in: path + required: true + type: string +snapshot_id_path: + description: | + The UUID of the snapshot. + in: path + required: true + type: string +tag: + description: | + The tag as a string. + in: path + required: true + type: string + min_version: 2.26 +tenant_id: + description: | + The UUID of the tenant in a multi-tenancy cloud. + in: path + required: true + type: string +volume_id_path: + description: | + The unique ID for a volume. + in: path + required: true + type: string + +# variables in query +access_ip_v4_query_server: + in: query + required: false + type: string + description: | + Filter server list result by IPv4 address that should be used + to access the server. +access_ip_v6_query_server: + in: query + required: false + type: string + description: | + Filter server list result by IPv6 address that should be used + to access the server. +all_projects: + description: | + Administrator only. Lists server groups for all projects. For example: + + ``GET /os-server-groups?all_projects=True`` + + If you specify a tenant ID for a non-administrative user with this query parameter, + the call lists all server groups for the tenant, or project, rather than for + all projects. + in: query + required: false + type: boolean +all_tenants: + description: | + Specify the ``all_tenants=1`` query parameter to ping instances + for all tenants. By default this is only allowed by admin users. + in: query + required: false + type: integer +all_tenants_query: + description: | + Specify the ``all_tenants=1`` query parameter to list all instances + for all projects. By default this is only allowed by admin users. + in: query + required: false + type: integer +availability_zone_query_server: + description: | + Filter the server list result by server availability zone. + in: query + required: false + type: string +binary_query: + description: | + Filter the service list result by binary name of the service. + in: query + required: false + type: string +changes-since: + description: | + Filters the response by a date and time when the image last changed status. + Use this query parameter to check for changes since a previous request rather + than re-downloading and re-parsing the full status at each polling interval. + If data has changed, the call returns only the items changed since the ``changes-since`` + time. If data has not changed since the ``changes-since`` time, the call returns an + empty list. + To enable you to keep track of changes, this filter also displays images + that were deleted if the ``changes-since`` value specifies a date in the last 30 days. + Items deleted more than 30 days ago might be returned, but it is not guaranteed. + The date and time stamp format is `ISO 8601 `_: + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + The ``±hh:mm`` value, if included, returns the time zone as an offset from UTC. + For example, ``2015-08-27T09:49:58-05:00``. + If you omit the time zone, the UTC time zone is assumed. + in: query + required: false + type: string +changes_since_server: + description: | + Filters the response by a date and time stamp when the server last + changed status. To help keep track of changes this may also return + recently deleted servers. + + The date and time stamp format is `ISO 8601 `_: + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + The ``±hh:mm`` value, if included, returns the time zone as an offset from UTC. + For example, ``2015-08-27T09:49:58-05:00``. + If you omit the time zone, the UTC time zone is assumed. + in: query + required: false + type: string +config_drive_query_server: + description: | + Filter the server list result by the config drive setting of the server. + in: query + required: false + type: boolean +created_at_query_server: + description: | + Filter the server list result by a date and time stamp when server was created. + + The date and time stamp format is `ISO 8601 `_: + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + The ``±hh:mm`` value, if included, returns the time zone as an offset from UTC. + For example, ``2015-08-27T09:49:58-05:00``. + If you omit the time zone, the UTC time zone is assumed. + in: query + required: false + type: string +delete_info: + description: | + Information for snapshot deletion. Include the ID of the associated volume. For + example: + + .. code-block:: javascript + + DELETE /os-assisted-volume-snapshots?delete_info='{"volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c"}' + in: query + required: true + type: string +deleted_query: + in: query + required: false + type: boolean + description: | + Show deleted items. In some circumstances deleted items will still + be accessible via the backend database, however there is no + contract on how long, so this parameter should be used with + caution. It is typically only available to administrators. +description_query_server: + description: | + Filter the server list result by description. + + .. note:: + + ``display_description`` can also be requested which is alias of + ``description`` but that is not recommended to use as that will + be removed in future. + in: query + required: false + type: string +detailed_simple_tenant_usage: + description: | + Specify the ``detailed=1`` query parameter to get detail information + ('server_usages' information). + in: query + required: false + type: integer +disk_config_query_server: + description: | + Filter the server list result by the ``disk_config`` setting of the server, + Valid values are: + + - ``AUTO`` + - ``MANUAL`` + in: query + required: false + type: string +end_simple_tenant_usage: + description: | + The ending time to calculate usage statistics on compute and + storage resources. The date and time stamp format is any of + the following ones: + + :: + + CCYY-MM-DDThh:mm:ss + + For example, ``2015-08-27T09:49:58``. + + :: + + CCYY-MM-DDThh:mm:ss.NNNNNN + + For example, ``2015-08-27T09:49:58.123456``. + + :: + + CCYY-MM-DD hh:mm:ss.NNNNNN + + For example, ``2015-08-27 09:49:58.123456``. + If you omit this parameter, the current time is used. + in: query + required: false + type: string +exclude: + description: | + Specify ``exclude=uuid[,uuid...]`` to exclude the instances from the results. + in: query + required: false + type: string +flavor_is_public_query: + in: query + required: false + type: boolean + description: | + Filters the flavor list by only public flavors. By default ``non + admin`` users only see public flavors, and ``admin`` users can see + additional non public flavors. +flavor_query: + description: | + Filters the response by a flavor, as a UUID. A flavor is a combination of memory, + disk size, and CPUs. + in: query + required: false + type: string +host_query_server: + description: | + Filter the server list result by the host name of compute node. Only allowed for Admin. + in: query + required: false + type: string +host_query_service: + description: | + Filter the service list result by the host name. + in: query + required: false + type: string +hostname_query_server: + description: | + Filter the server list result by the host name of server. + in: query + required: false + type: string +hypervisor_hostname_pattern_query: + description: | + The hypervisor host name or a portion of it. The hypervisor hosts are + selected with the host name matching this pattern. + + .. note:: ``limit`` and ``marker`` query parameters for paging are + not supported when listing hypervisors using a hostname pattern. + Also, ``links`` will not be returned in the response when using this + query parameter. + in: query + required: false + type: string + min_version: 2.53 +hypervisor_limit: + description: | + Requests a page size of items. Returns a number of items up to a limit value. + Use the ``limit`` parameter to make an initial limited request and use the ID + of the last-seen item from the response as the ``marker`` parameter value in a + subsequent limited request. + in: query + required: false + type: integer + min_version: 2.33 +hypervisor_marker: + description: | + The ID of the last-seen item. Use the ``limit`` parameter to make an initial limited + request and use the ID of the last-seen item from the response as the ``marker`` + parameter value in a subsequent limited request. + in: query + required: false + type: integer + min_version: 2.33 + max_version: 2.52 +hypervisor_marker_uuid: + description: | + The ID of the last-seen item as a UUID. Use the ``limit`` parameter to make + an initial limited request and use the ID of the last-seen item from the + response as the ``marker`` parameter value in a subsequent limited request. + in: query + required: false + type: string + min_version: 2.53 +hypervisor_query: + description: | + Filters the response by a hypervisor type. + in: query + required: false + type: string +hypervisor_with_servers_query: + description: | + Include all servers which belong to each hypervisor in the response output. + in: query + required: false + type: boolean + min_version: 2.53 +image_name_query: + description: | + Filters the response by an image name, as a string. + in: query + required: false + type: string +image_query: + description: | + Filters the response by an image, as a UUID. + + .. note:: + + 'image_ref' can also be requested which is alias of 'image' + but that is not recommended to use as that will be removed in future. + in: query + required: false + type: string +image_server_query: + description: | + Filters the response by a server, as a URL. + format: uri + in: query + required: false + type: string +image_status_query: + description: | + Filters the response by an image status, as a string. For example, ``ACTIVE``. + in: query + required: false + type: string +image_type_query: + description: | + Filters the response by an image type. For example, ``snapshot`` or ``backup``. + in: query + required: false + type: string +include: + description: | + Specify ``include=uuid[,uuid...]`` to include the instances in the results. + in: query + required: false + type: string +ip6_query: + description: | + An IPv6 address to filter results by. + in: query + required: false + type: string + min_version: 2.5 +ip_query: + description: | + An IPv4 address to filter results by. + in: query + required: false + type: string +kernel_id_query_server: + in: query + required: false + type: string + description: | + Filter the server list result by the UUID of the kernel image + when using an AMI. +key_name_query_server: + description: | + Filter the server list result by keypair name. + in: query + required: false + type: string +keypair_limit: + description: | + Requests a page size of items. Returns a number of items up to a limit value. + Use the ``limit`` parameter to make an initial limited request and use the + last-seen item from the response as the ``marker`` parameter value in a + subsequent limited request. + in: query + required: false + type: integer + min_version: 2.35 +keypair_marker: + description: | + The last-seen item. Use the ``limit`` parameter to make an initial limited + request and use the last-seen item from the response as the ``marker`` + parameter value in a subsequent limited request. + in: query + required: false + type: string + min_version: 2.35 +keypair_user: + in: query + required: false + type: string + description: | + This allows administrative users to operate key-pairs of specified + user ID. + min_version: 2.10 +launch_index_query_server: + description: | + Filter the server list result by the sequence in which the + servers were launched. + in: query + required: false + type: string +launched_at_query_server: + description: | + Filter the server list result by a date and time stamp when the instance was launched. + The date and time stamp format is `ISO 8601 `_: + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + The ``±hh:mm`` value, if included, returns the time zone as an offset from UTC. + For example, ``2015-08-27T09:49:58-05:00``. + If you omit the time zone, the UTC time zone is assumed. + in: query + required: false + type: string +limit: + description: | + Requests a page size of items. Returns a number of items up to a limit value. + Use the ``limit`` parameter to make an initial limited request and use the ID + of the last-seen item from the response as the ``marker`` parameter value in a + subsequent limited request. + in: query + required: false + type: integer +limit_simple: + description: | + Used in conjunction with ``offset`` to return a slice of items. ``limit`` + is the maximum number of items to return. If ``limit`` is not specified, + or exceeds the configurable ``max_limit``, then ``max_limit`` will be + used instead. + in: query + required: false + type: integer +locked_by_query_server: + description: | + Filter the server list result by who locked the server, possible value + could be ``admin``, ``owner`` or ``none``. + in: query + required: false + type: string +marker: + description: | + The ID of the last-seen item. Use the ``limit`` parameter to make an initial limited + request and use the ID of the last-seen item from the response as the ``marker`` + parameter value in a subsequent limited request. + in: query + required: false + type: string +migration_hidden: + description: | + The 'hidden' setting of migration to filter. + The 'hidden' flag is set if the value is 1. + The 'hidden' flag is not set if the value is 0. + But the 'hidden' setting of migration is always 0, + so this parameter is useless to filter migrations. + in: query + required: false + type: integer +migration_host: + description: | + The source/destination compute node of migration to filter. + in: query + required: false + type: string +migration_instance_uuid: + description: | + The uuid of the instance that migration is operated on to filter. + in: query + required: false + type: string +migration_source_compute: + description: | + The source compute node of migration to filter. + in: query + required: false + type: string +migration_status: + description: | + The status of migration to filter. + in: query + required: false + type: string +migration_type: + description: | + The type of migration to filter. + in: query + required: false + type: string +minDisk: + description: | + Filters the response by a minimum disk space, in GiB. For example, ``100``. + in: query + required: false + type: integer +minRam: + description: | + Filters the response by a minimum RAM, in MB. For example, ``512``. + in: query + required: false + type: integer +node_query_server: + description: | + Filter the server list result by the node. Only allowed for Admin. + in: query + required: false + type: string +not_tags_any_query: + in: query + required: false + type: string + description: | + A list of tags to filter the server list by. Servers that don't + match any tags in this list will be returned. Boolean expression in this + case is 'NOT (t1 OR t2)'. Tags in query must be separated by comma. + min_version: 2.26 +not_tags_query: + in: query + required: false + type: string + description: | + A list of tags to filter the server list by. Servers that don't + match all tags in this list will be returned. Boolean expression in this + case is 'NOT (t1 AND t2)'. Tags in query must be separated by comma. + min_version: 2.26 +offset_simple: + description: | + Used in conjunction with ``limit`` to return a slice of items. ``offset`` + is where to start in the list. + in: query + required: false + type: integer +power_state_query_server: + in: query + required: false + type: string + description: | + Filter the server list result by server power state. +progress_query_server: + description: | + Filter the server list result by the progress of the server. + in: query + required: false + type: string +ramdisk_id_query_server: + in: query + required: false + type: string + description: | + Filter the server list result by the UUID of the ramdisk image when + using an AMI. +reservation_id_query: + in: query + required: false + type: string + description: | + A reservation id as returned by a servers multiple create call. +reserved_query: + description: | + Specify whether the result of resource total includes reserved resources + or not. + + - ``0``: Not include reserved resources. + - Other than 0: Include reserved resources. + + If non integer value is specified, it is the same as ``0``. + in: query + required: false + type: integer +server_name_query: + description: | + Filters the response by a server name, as a string. You can use regular expressions + in the query. For example, the ``?name=bob`` regular expression returns both bob + and bobb. If you must match on only bob, you can use a regular expression that + matches the syntax of the underlying database server that is implemented for Compute, + such as MySQL or PostgreSQL. + + .. note:: + + 'display_name' can also be requested which is alias of 'name' + but that is not recommended to use as that will be removed in future. + format: regexp + in: query + required: false + type: string +server_root_device_name_query: + in: query + required: true + type: string + description: | + Filter the server list result by the root device name of the server +server_status_query: + description: | + Filters the response by a server status, as a string. For example, ``ACTIVE``. + in: query + required: false + type: string +server_uuid_query: + description: | + Filter the server list result by the UUID of the server. + in: query + required: false + type: string +sort_dir_flavor: + description: | + Sort direction. A valid value is ``asc`` (ascending) or ``desc`` (descending). + Default is ``asc``. You can specify multiple pairs of sort key and sort direction + query parameters. If you omit the sort direction in a pair, the API uses the natural + sorting direction of the direction of the flavor ``sort_key`` attribute. + in: query + required: false + type: string +sort_dir_server: + description: | + Sort direction. A valid value is ``asc`` (ascending) or ``desc`` (descending). + Default is ``desc``. You can specify multiple pairs of sort key and sort direction + query parameters. If you omit the sort direction in a pair, the API uses the natural + sorting direction of the direction of the server ``sort_key`` attribute. + in: query + required: false + type: string +sort_key_flavor: + description: | + Sorts by a flavor attribute. Default attribute is ``flavorid``. You can specify + multiple pairs of sort key and sort direction query parameters. If you omit the + sort direction in a pair, the API uses the natural sorting direction of the flavor + ``sort_key`` attribute. + in: query + required: false + type: string +sort_key_server: + description: | + Sorts by a server attribute. Default attribute is ``created``. You can specify + multiple pairs of sort key and sort direction query parameters. If you omit the + sort direction in a pair, the API uses the natural sorting direction of the server + ``sort_key`` attribute. The sort keys are limited to: + + - ``access_ip_v4`` + - ``access_ip_v6`` + - ``auto_disk_config`` + - ``availability_zone`` + - ``config_drive`` + - ``created_at`` + - ``display_description`` + - ``display_name`` + - ``host`` + - ``hostname`` + - ``image_ref`` + - ``instance_type_id`` + - ``kernel_id`` + - ``key_name`` + - ``launch_index`` + - ``launched_at`` + - ``locked_by`` + - ``node`` + - ``power_state`` + - ``progress`` + - ``project_id`` + - ``ramdisk_id`` + - ``root_device_name`` + - ``task_state`` + - ``terminated_at`` + - ``updated_at`` + - ``user_id`` + - ``uuid`` + - ``vm_state`` + in: query + required: false + type: string +start_simple_tenant_usage: + description: | + The beginning time to calculate usage statistics on compute and + storage resources. The date and time stamp format is any of the + following ones: + + :: + + CCYY-MM-DDThh:mm:ss + + For example, ``2015-08-27T09:49:58``. + + :: + + CCYY-MM-DDThh:mm:ss.NNNNNN + + For example, ``2015-08-27T09:49:58.123456``. + + :: + + CCYY-MM-DD hh:mm:ss.NNNNNN + + For example, ``2015-08-27 09:49:58.123456``. + If you omit this parameter, the current time is used. + in: query + required: false + type: string +tags_any_query: + in: query + required: false + type: string + description: | + A list of tags to filter the server list by. Servers that match + any tag in this list will be returned. Boolean expression in this + case is 't1 OR t2'. Tags in query must be separated by comma. + min_version: 2.26 +tags_query: + in: query + required: false + type: string + description: | + A list of tags to filter the server list by. Servers that match + all tags in this list will be returned. Boolean expression in this + case is 't1 AND t2'. Tags in query must be separated by comma. + min_version: 2.26 +task_state_query_server: + in: query + required: false + type: string + description: | + Filter the server list result by task state. +tenant_id_query: + description: | + Specify the project ID (tenant ID) to show the rate and absolute limits. + This parameter can be specified by admin only. + in: query + required: false + type: string +terminated_at_query_server: + description: | + Filter the server list result by a date and time stamp when instance was terminated. + The date and time stamp format is `ISO 8601 `_: + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + The ``±hh:mm`` value, if included, returns the time zone as an offset from UTC. + For example, ``2015-08-27T09:49:58-05:00``. + If you omit the time zone, the UTC time zone is assumed. + in: query + required: false + type: string +usage_limit: + description: | + Requests a page size of items. Calculate usage for the limited number of + instances. Use the ``limit`` parameter to make an initial limited request + and use the last-seen instance UUID from the response as the ``marker`` + parameter value in a subsequent limited request. + in: query + required: false + type: integer + min_version: 2.40 +usage_marker: + description: | + The last-seen item. Use the ``limit`` parameter to make an initial limited + request and use the last-seen instance UUID from the response as the + ``marker`` parameter value in a subsequent limited request. + in: query + required: false + type: string + min_version: 2.40 +user_id_query_quota: + description: | + ID of user to list the quotas for. + in: query + required: false + type: string +user_id_query_quota_delete: + description: | + ID of user to delete quotas for. + in: query + required: false + type: string +user_id_query_server: + description: | + Filter the list of servers by the given user ID. + in: query + required: false + type: string +user_id_query_set_quota: + description: | + ID of user to set the quotas for. + in: query + required: false + type: string +vm_state_query_server: + description: | + Filter the server list result by vm state. + in: query + required: false + type: string + +# variables in body +accessIPv4: + in: body + required: true + type: string + description: | + IPv4 address that should be used to access this server. May be + automatically set by the provider. +accessIPv4_in: + in: body + required: false + type: string + description: | + IPv4 address that should be used to access this server. +accessIPv6: + in: body + required: true + type: string + description: | + IPv6 address that should be used to access this server. May be + automatically set by the provider. +accessIPv6_in: + in: body + required: false + type: string + description: | + IPv6 address that should be used to access this server. +action: + description: | + The name of the action. + in: body + required: true + type: string +action_reserve: + description: | + The attribute to reserve an IP with a value of ``null``. + in: body + required: false + type: string +action_unreserve: + description: | + The attribute to release an IP with a value of ``null``. + in: body + required: false + type: string +addFixedIp: + description: | + The action to add a fixed ip address to a server. + in: body + required: true + type: object +addFloatingIp: + description: | + The action. Contains required floating IP ``address`` and optional + ``fixed_address``. + in: body + required: true + type: object +address: + description: | + The floating IP address. + in: body + required: true + type: string +addresses: + description: | + The addresses for the server. Addresses information is hidden for any server + in a state set in the ``hide_server_address_states`` configuration option. + By default, servers in ``building`` state hide their addresses information. + See ``nova.conf`` `configuration options + `_ + for more information. + in: body + required: true + type: object +addresses_obj: + description: | + The addresses information for the server. + in: body + required: true + type: object +addSecurityGroup: + description: | + The action to add a security group to a server. + in: body + required: true + type: object +addTenantAccess: + description: | + The action. + in: body + required: true + type: string +adminPass_change_password: + description: | + The administrative password for the server. + in: body + required: true + type: string +adminPass_evacuate: + description: | + An administrative password to access the evacuated instance. + If you set ``enable_instance_password`` configuration option to ``False``, + the API wouldn't return the ``adminPass`` field in response. + in: body + required: false + type: string + max_version: 2.13 +adminPass_evacuate_request: + description: | + An administrative password to access the evacuated server. + If you omit this parameter, the operation generates a new password. + Up to API version 2.13, if ``onSharedStorage`` is set to ``True`` and + this parameter is specified, an error is raised. + in: body + required: false + type: string +adminPass_request: + description: | + The administrative password of the server. If you omit this parameter, the operation + generates a new password. + in: body + required: false + type: string +adminPass_rescue_request: + description: | + The password for the rescued instance. If you omit this parameter, the operation + generates a new password. + in: body + required: false + type: string +adminPass_response: + description: | + The administrative password for the server. If you set ``enable_instance_password`` configuration + option to ``False``, the API wouldn't return the ``adminPass`` field in response. + in: body + required: false + type: string +agent: + description: | + The guest agent object. + in: body + required: true + type: object +agent_id: + description: | + The agent ID. + in: body + required: true + type: integer +agent_id_str: + description: | + The agent ID. (This is a bug of API, this should be integer type which is consistent with + the responses of agent create and list. This will be fixed in later microversion.) + in: body + required: true + type: string +agents: + description: | + A list of guest agent objects. + in: body + required: true + type: array +aggregate: + description: | + The host aggregate object. + in: body + required: true + type: object +aggregate_add_host: + description: | + The add_host object used to add host to aggregate. + in: body + required: true + type: object +aggregate_az: + description: | + The availability zone of the host aggregate. + in: body + required: true + type: string +aggregate_az_optional: + description: | + The availability zone of the host aggregate. The availability zone must + not include ':' in its name. + in: body + required: false + type: string +aggregate_host_list: + description: | + A list of host ids in this aggregate. + in: body + required: true + type: array +aggregate_id_body: + description: | + The ID of the host aggregate. + in: body + required: true + type: integer +aggregate_metadata: + description: | + Metadata key and value pairs associate with the aggregate. + in: body + required: true + type: object +aggregate_name: + description: | + The name of the host aggregate. + in: body + required: true + type: string +aggregate_name_optional: + description: | + The name of the host aggregate. + in: body + required: false + type: string +aggregate_remove_host: + description: | + The add_host object used to remove host from aggregate. + in: body + required: true + type: object +aggregate_uuid: + description: | + The UUID of the host aggregate. + in: body + required: true + type: string + min_version: 2.41 +aggregates: + description: | + The list of existing aggregates. + in: body + required: true + type: array +alias: + description: | + A short name by which this extension is also known. + in: body + required: true + type: string +alive: + description: | + Returns true if the instance is alive. + in: body + required: true + type: boolean +architecture: + description: | + The name of the cpu architecture. + in: body + required: true + type: string +associate_host: + description: | + The name of the host to associate. + in: body + required: true + type: string +attachment_device_resp: + description: | + Name of the device in the attachment object, such as, ``/dev/vdb``. + in: body + required: false + type: string +attachment_id_required: + description: | + The UUID of the attachment. + in: body + required: true + type: string +attachment_id_resp: + description: | + The UUID of the attachment. + in: body + required: false + type: string +attachment_server_id_resp: + description: | + The UUID of the server. + in: body + required: false + type: string +attachment_volumeId_resp: + description: | + The UUID of the attached volume. + in: body + required: false + type: string +availability_zone: + description: | + The availability zone. + in: body + required: false + type: string +availability_zone_info: + description: | + The list of availability zone information. + in: body + required: true + type: array +availability_zone_state: + description: | + The current state of the availability zone. + in: body + required: true + type: object +available: + description: | + Returns true if the availability zone is available. + in: body + required: true + type: boolean +backup_name: + description: | + The name of the image to be backed up. + in: body + required: true + type: string +backup_rotation: + description: | + The rotation of the back up image, the oldest image will be removed when image count + exceed the rotation count. + in: body + required: true + type: integer +backup_type: + description: | + The type of the backup, for example, ``daily``. + in: body + required: true + type: string +baremetal_cpus: + description: | + Number of CPUs the node has. + + .. note:: This is a JSON string, even though it will look like an int value. + in: body + required: true + type: string +baremetal_disk: + description: | + Amount of disk in GiB the node has. + + .. note:: This is a JSON string, even though it will look like an int value. + in: body + required: true + type: string +baremetal_host: + description: | + This will always have the value ``IRONIC MANAGED``. + in: body + required: true + type: string +baremetal_id: + description: | + UUID of the baremetal node. + in: body + required: true + type: string +baremetal_instance_uuid: + description: | + UUID of the server instance on this node. + in: body + required: true + type: string +baremetal_interfaces: + description: | + A list of interface objects for active interfaces on the baremetal + node. Each will have an ``address`` field with the address. + in: body + required: true + type: array +baremetal_mem: + description: | + Amount of memory in MiB the node has. + + .. note:: This is a JSON string, even though it will look like an int value. + in: body + required: true + type: string +baremetal_node: + description: | + A baremetal node object. + in: body + required: true + type: object +baremetal_nodes: + description: | + An array of baremetal node objects. + in: body + required: true + type: array +baremetal_taskstate: + description: | + The Ironic task state for the node. See Ironic project for more + details. + in: body + required: true + type: string +binary: + description: | + The binary name of the service. + in: body + required: true + type: string +block_device_mapping_v2: + description: | + Enables fine grained control of the block device mapping for an instance. This + is typically used for booting servers from volumes. An example format would look + as follows: + + .. code-block:: javascript + + "block_device_mapping_v2": [{ + "boot_index": "0", + "uuid": "ac408821-c95a-448f-9292-73986c790911", + "source_type": "image", + "volume_size": "25", + "destination_type": "volume", + "delete_on_termination": true, + "tag": "disk1" }] + + In microversion 2.32, ``tag`` is an optional string attribute that can + be used to assign a tag to the block device. This tag is then exposed to + the guest in the metadata API and the config drive and is associated to + hardware metadata for that block device, such as bus (ex: SCSI), bus + address (ex: 1:0:2:0), and serial. + + A bug has caused the ``tag`` attribute to no longer be accepted starting + with version 2.33. It has been restored in version 2.42. + in: body + required: false + type: object +block_device_uuid: + description: | + This is the uuid of source resource. The uuid is point to different resource + based on the source_type. For example, if source_type is ``image`` as the above + sample, the block device is created based on the specified image which is gotten + from the image service. + in: body + required: false + type: string +block_migration: + description: | + Set to ``True`` to migrate local disks by using block migration. If the source + or destination host uses shared storage and you set this value to ``True``, the + live migration fails. + in: body + required: true + type: boolean + max_version: 2.24 +block_migration_2_25: + description: | + Migrates local disks by using block migration. Set to ``auto`` which means + nova will detect whether source and destination hosts on shared storage. if they are + on shared storage, the live-migration won't be block migration. Otherwise the block + migration will be executed. Set to ``True``, means the request will fail when the + source or destination host uses shared storage. Set to ``False`` means the request + will fail when the source and destination hosts are not on the shared storage. + in: body + required: true + type: string + min_version: 2.25 +boot_index: + description: | + Defines the order in which a hypervisor tries devices when it attempts to boot + the guest from storage. Give each device a unique boot index starting from ``0``. + To disable a device from booting, set the boot index to a negative value or use + the default boot index value, which is ``None``. The simplest usage is, set the + boot index of the boot device to ``0`` and use the default boot index value, ``None``, + for any other devices. Some hypervisors might not support booting from multiple + devices; these hypervisors consider only the device with a boot index of ``0``. Some + hypervisors support booting from multiple devices but only if the devices are + of different types. For example, a disk and CD-ROM. + in: body + required: true + type: string +certificate: + description: | + The certificate object. + in: body + required: true + type: object +changePassword: + description: | + The action to change an administrative password of the server. + in: body + required: true + type: object +cidr: + description: | + The CIDR for address range. + in: body + required: true + type: string +cloudpipe: + description: | + The cloudpipe object. + in: body + required: true + type: object +cloudpipes: + description: | + The list of cloudpipe objects. + in: body + required: true + type: array +code: + description: | + The HTTP response code for the event. The following codes are currently used: + + * 200 - successfully submitted event + * 400 - the request is missing required parameter + * 404 - the instance specified by ``server_uuid`` was not found + * 422 - no host was found for the server specified by ``server_uuid``, + so there is no route to this server. + in: body + required: true + type: string +config_drive: + description: | + Indicates whether a configuration drive enables metadata injection. The config_drive + setting provides information about a drive that the instance can mount at boot + time. The instance reads files from the drive to get information that is normally + available through the metadata service. This metadata is different from the user + data. Not all cloud providers enable the ``config_drive``. Read more in the + `OpenStack End User Guide `_. + in: body + required: false + type: boolean +config_drive_diagnostics: + description: | + Indicates whether or not a config drive was used for this server. + in: body + required: true + type: boolean + min_version: 2.48 +config_drive_resp: + description: | + Indicates whether or not a config drive was used for this server. + in: body + required: true + type: boolean +configure_project_cloudpipe: + description: | + VPN IP and Port information to configure the cloudpipe instance.. + in: body + required: true + type: object +confirmResize: + description: | + The action to confirm a resize operation. + in: body + required: true + type: none +console: + description: | + The console object. + in: body + required: true + type: object +console_host: + description: | + The name or ID of the host. + in: body + required: false + type: string +console_id_in_body: + description: | + The UUID of the console. + in: body + required: true + type: string +console_output: + description: | + The console output as a string. Control characters will be escaped + to create a valid JSON string. + in: body + required: true + type: string +console_password: + description: | + The password for the console. + in: body + required: true + type: string +console_type: + description: | + The type of the console. + in: body + required: true + type: string +consoles: + description: | + The list of console objects. + in: body + required: true + type: array +contents: + description: | + The file contents field in the personality object. + in: body + required: true + type: string +cores: &cores + description: | + The number of allowed server cores for each tenant. + in: body + required: true + type: integer +cores_quota_class: &cores_quota_class + <<: *cores + description: | + The number of allowed server cores for the quota class. +cores_quota_class_optional: + <<: *cores_quota_class + required: false +cores_quota_details: + description: | + The object of detailed cores quota, including in_use, limit and + reserved number of cores. + in: body + required: true + type: object +cores_quota_optional: + description: | + The number of allowed server cores for each tenant. + in: body + required: false + type: integer +cpu_details_diagnostics: + description: | + The list of dictionaries with detailed information about VM CPUs. + Following fields are presented in each dictionary: + + - ``id`` - the ID of CPU (Integer) + + - ``time`` - CPU Time in nano seconds (Integer) + + - ``utilisation`` - CPU utilisation in percents (Integer) + in: body + required: true + type: array + min_version: 2.48 +cpu_info: + description: | + A dictionary that contains cpu information like ``arch``, ``model``, + ``vendor``, ``features`` and ``topology``. The content of this field is + hypervisor specific. + + .. note:: + + Since version 2.28 ``cpu_info`` field is returned as a dictionary + instead of string. + in: body + required: true + type: object +create_info: + description: | + Information for snapshot creation. + in: body + required: true + type: object +create_info_id: + description: | + Its an arbitrary string that gets passed back to the user. + in: body + required: false + type: string +create_info_id_resp: + description: | + Its the same arbitrary string which was sent in request body. + + .. note:: + + This string is passed back to user as it is and not being + used in Nova internally. So use ``snapshot_id`` instead for further + operation on this snapshot. + in: body + required: true + type: string +createBackup: + description: | + The action. + in: body + required: true + type: object +created: + description: | + The date and time when the resource was created. The date and time + stamp format is `ISO 8601 `_ + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + For example, ``2015-08-27T09:49:58-05:00``. The ``±hh:mm`` + value, if included, is the time zone as an offset from UTC. In + the previous example, the offset value is ``-05:00``. + in: body + required: true + type: string +createImage: + description: | + The action to create a snapshot of the image or + the volume(s) of the server. + in: body + required: true + type: object +current_workload: + description: | + The current_workload is the number of tasks the hypervisor is responsible for. This will be + equal or greater than the number of active VMs on the system (it can be greater when VMs + are being deleted and the hypervisor is still cleaning up). + in: body + required: true + type: integer +data: + description: | + The certificate. + in: body + required: true + type: string +delete_on_termination: + description: | + To delete the boot volume when the server is destroyed, specify ``true``. + Otherwise, specify ``false``. Default: ``false`` + in: body + required: false + type: string +deleted: + description: | + A boolean indicates whether this aggregate is deleted or not, if it has + not been deleted, ``false`` will appear. + in: body + required: true + type: boolean +deleted_at: + description: | + The date and time when the resource was deleted. If the resource has + not been deleted yet, this field will be ``null``, The date and time + stamp format is `ISO 8601 `_ + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + For example, ``2015-08-27T09:49:58-05:00``. The ``±hh:mm`` + value, if included, is the time zone as an offset from UTC. In + the previous example, the offset value is ``-05:00``. + in: body + required: true + type: string +description: + description: | + Security group description. + in: body + required: true + type: string +destination_type: + description: | + Defines where the volume comes from. A valid value is ``local`` or ``volume``. + in: body + required: false + type: string +device: + description: | + Name of the device such as, ``/dev/vdb``. Omit or set this parameter to null for + auto-assignment, if supported. If you specify this parameter, the device must + not exist in the guest operating system. Note that as of the 12.0.0 Liberty release, + the Nova libvirt driver no longer honors a user-supplied device name. This is + the same behavior as if the device name parameter is not supplied on the request. + in: body + required: false + type: string +device_name: + description: | + A path to the device for the volume that you want to use to boot the server. + Note that as of the 12.0.0 Liberty release, the Nova libvirt driver no + longer honors a user-supplied device name. This is the same behavior as if + the device name parameter is not supplied on the request. + in: body + required: true + type: string +device_resp: + description: | + Name of the device such as, ``/dev/vdb``. + in: body + required: true + type: string +device_tag_bdm: + description: | + A device role tag that can be applied to a block device. The guest OS of a + server that has devices tagged in this manner can access hardware metadata + about the tagged devices from the metadata API and on the config drive, if + enabled. + + .. note:: Due to a bug, block device tags are accepted in version 2.32 and + subsequently starting with version 2.42. + in: body + required: false + type: string + min_version: 2.32 +device_tag_bdm_attachment: + description: | + A device role tag that can be applied to a volume when attaching it to the + VM. The guest OS of a server that has devices tagged in this manner can + access hardware metadata about the tagged devices from the metadata API and + on the config drive, if enabled. + + .. note:: Tagged volume attachment is not supported for shelved-offloaded + instances. + in: body + required: false + type: string + min_version: 2.49 +device_tag_nic: + description: | + A device role tag that can be applied to a network interface. The guest OS + of a server that has devices tagged in this manner can access hardware + metadata about the tagged devices from the metadata API and on the config + drive, if enabled. + + .. note:: Due to a bug, network interface tags are accepted between 2.32 + and 2.36 inclusively, and subsequently starting with version 2.42. + in: body + required: false + type: string + min_version: 2.32 +device_tag_nic_attachment: + description: | + A device role tag that can be applied to a network interface when attaching + it to the VM. The guest OS of a server that has devices tagged in this + manner can access hardware metadata about the tagged devices from the + metadata API and on the config + drive, if enabled. + in: body + required: false + type: string + min_version: 2.49 +# Optional input parameter in the body for PUT /os-services/{service_id} added +# in microversion 2.53. +disabled_reason_2_53_in: + description: | + The reason for disabling a service. The minimum length is 1 and the + maximum length is 255. This may only be requested with ``status=disabled``. + in: body + required: false + type: string +disabled_reason_body: + description: | + The reason for disabling a service. + in: body + required: true + type: string +disk_available_least: + description: | + The actual free disk on this hypervisor(in GB). + in: body + required: true + type: integer +disk_config: + description: | + Disk configuration. The value is either: + + - ``AUTO``. The API builds the server with a single partition the size of + the target flavor disk. The API automatically adjusts the file system to + fit the entire partition. + + - ``MANUAL``. The API builds the server by using the partition scheme and + file system that is in the source image. If the target flavor disk is + larger, The API does not partition the remaining disk space. + in: body + required: true + type: string +disk_details_diagnostics: + description: | + The list of dictionaries with detailed information about VM disks. + Following fields are presented in each dictionary: + + - ``read_bytes`` - Disk reads in bytes (Integer) + + - ``read_requests`` - Read requests (Integer) + + - ``write_bytes`` - Disk writes in bytes (Integer) + + - ``write_requests`` - Write requests (Integer) + + - ``errors_count`` - Disk errors (Integer) + in: body + required: true + type: array + min_version: 2.48 +disk_over_commit: + description: | + Set to ``True`` to enable over commit when the destination host is checked for + available disk space. Set to ``False`` to disable over commit. This setting affects + only the libvirt virt driver. + in: body + required: true + type: boolean + max_version: 2.25 +display_description: + description: | + The volume description. + in: body + required: true + type: string +display_description_optional: + description: | + The volume description. + in: body + required: false + type: string +display_name: + description: | + The volume name. + in: body + required: true + type: string +display_name_optional: + description: | + The volume name. + in: body + required: false + type: string +driver_diagnostics: + description: | + The driver on which the VM is running. Possible values are: + + - ``libvirt`` + - ``xenapi`` + - ``hyperv`` + - ``vmwareapi`` + - ``ironic`` + in: body + required: true + type: string + min_version: 2.48 +ended_at: + description: | + The date and time when the server was deleted. + + The date and time stamp format is as follows: + + :: + + CCYY-MM-DDThh:mm:ss.NNNNNN + + For example, ``2015-08-27T09:49:58.123456``. + If the server hasn't been deleted yet, its value is ``null``. + in: body + required: true + type: string +ended_at_optional: + description: | + The date and time when the server was deleted. + + The date and time stamp format is as follows: + + :: + + CCYY-MM-DDThh:mm:ss.NNNNNN + + For example, ``2015-08-27T09:49:58.123456``. + If the server hasn't been deleted yet, its value is ``null``. + in: body + required: false + type: string +errors: + description: | + The number of errors. + in: body + required: true + type: integer +evacuate: + description: | + The action to evacuate a server to another host. + in: body + required: true + type: object +event: + description: | + The name of the event. + in: body + required: true + type: string +event_finish_time: + description: | + The date and time when the event was finished. The date and time + stamp format is `ISO 8601 `_ + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + For example, ``2015-08-27T09:49:58-05:00``. The ``±hh:mm`` + value, if included, is the time zone as an offset from UTC. In + the previous example, the offset value is ``-05:00``. + in: body + required: true + type: string +event_name: + description: | + The event name. A valid value is ``network-changed``, ``network-vif-plugged``, + ``network-vif-unplugged``, ``network-vif-deleted``, or ``volume-extended``. + The event name ``volume-extended`` is added since microversion ``2.51``. + in: body + required: true + type: string +event_result: + description: | + The result of the event. + in: body + required: true + type: string +event_start_time: + description: | + The date and time when the event was started. The date and time + stamp format is `ISO 8601 `_ + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + For example, ``2015-08-27T09:49:58-05:00``. The ``±hh:mm`` + value, if included, is the time zone as an offset from UTC. In + the previous example, the offset value is ``-05:00``. + in: body + required: true + type: string +event_status: + description: | + The event status. A valid value is ``failed``, ``completed``, or ``in-progress``. + Default is ``completed``. + in: body + required: false + type: string +event_tag: + description: | + A string value that identifies the event. + in: body + required: false + type: string +event_traceback: + description: | + The traceback stack if an error occurred in this event. + + Policy defaults enable only users with the administrative role to see + an instance action event traceback. Cloud providers can change these + permissions through the ``policy.json`` file. + in: body + required: true + type: string +events: + description: | + The action. + in: body + required: true + type: array +extension: + description: | + An ``extension`` object. + in: body + required: true + type: object +extension_description: + description: | + Text describing this extension's purpose. + in: body + required: true + type: string +extension_links: + description: | + Links pertaining to this extension. This is a list of dictionaries, each including + keys ``href`` and ``rel``. + in: body + required: true + type: array +extension_name: + description: | + Name of the extension. + in: body + required: true + type: string +extensions: + description: | + List of ``extension`` objects. + in: body + required: true + type: array +extra_specs: + description: | + A dictionary of the flavor's extra-specs key-and-value pairs. It appears + in the os-extra-specs' "create" REQUEST body, as well as the + os-extra-specs' "create" and "list" RESPONSE body. + in: body + required: true + type: object +extra_specs_2_47: + min_version: 2.47 + description: | + A dictionary of the flavor's extra-specs key-and-value pairs. This will + only be included if the user is allowed by policy to index flavor + extra_specs. + in: body + required: false + type: object +fault: + description: | + A fault object. Only displayed in the failed response. + Default keys are ``code``, ``created``, and ``message`` + (response code, created time, and message respectively). + In addition, the key ``details`` (stack trace) is available + if you have the administrator privilege. + in: body + required: false + type: object +fixed_address: + description: | + The fixed IP address with which you want to associate the floating IP address. + in: body + required: false + type: string +fixed_ip: + description: | + A fixed IPv4 address for the NIC. Valid with a ``neutron`` or ``nova-networks`` + network. + in: body + required: false + type: string +fixed_ip_address: + description: | + Fixed IP associated with floating IP network. + in: body + required: true + type: string +fixed_ip_host: + description: | + The hostname of the host that manages the server that is associated with + this fixed IP address. + in: body + required: true + type: string +fixed_ip_hostname: + description: | + The hostname of the server that is associated with this fixed IP address. + in: body + required: true + type: string +fixed_ip_obj: + description: | + A fixed IP address object. + in: body + required: true + type: object +fixed_ips: + description: | + Fixed IP addresses. If you request a specific fixed IP address without + a ``net_id``, the request returns a ``Bad Request (400)`` response code. + in: body + required: false + type: array +fixed_ips_quota: + description: | + The number of allowed fixed IP addresses for each tenant. Must be equal to or + greater than the number of allowed servers. + in: body + required: true + type: integer + max_version: 2.35 +fixed_ips_quota_class: &fixed_ips_quota_class + description: | + The number of allowed fixed IP addresses for the quota class. Must be + equal to or greater than the number of allowed servers. + in: body + required: true + type: integer + max_version: 2.49 +fixed_ips_quota_class_optional: + <<: *fixed_ips_quota_class + required: false +fixed_ips_quota_details: + description: | + The object of detailed fixed ips quota, including in_use, limit and + reserved number of fixed ips. + in: body + required: true + type: object + max_version: 2.35 +fixed_ips_quota_optional: + description: | + The number of allowed fixed IP addresses for each tenant. Must be equal to or + greater than the number of allowed servers. + in: body + required: false + type: integer + max_version: 2.35 +fixed_ips_resp: + description: | + Fixed IP addresses with subnet IDs. + in: body + required: true + type: array +flavor: + description: | + The ID and links for the flavor for your server instance. A flavor is a combination + of memory, disk size, and CPUs. + in: body + required: true + type: object +flavor_access: + description: | + A list of objects, each with the keys ``flavor_id`` and ``tenant_id``. + in: body + required: true + type: array +flavor_cpus: + in: body + required: true + type: integer + description: | + The number of virtual CPUs that will be allocated to the server. +flavor_cpus_2_47: + min_version: 2.47 + in: body + required: true + type: integer + description: | + The number of virtual CPUs that were allocated to the server. +flavor_disabled: + in: body + required: false + type: boolean + description: | + Whether or not the flavor has been administratively disabled. + This is typically only visible to administrative users. +flavor_disk: + in: body + required: true + type: integer + description: | + The size of the root disk that will be created in GiB. If 0 the + root disk will be set to exactly the size of the image used to + deploy the instance. However, in this case filter scheduler cannot + select the compute host based on the virtual image size. Therefore, + 0 should only be used for volume booted instances or for testing + purposes. +flavor_disk_2_47: + min_version: 2.47 + in: body + required: true + type: integer + description: | + The size of the root disk that was created in GiB. +flavor_ephem_disk: + in: body + required: true + type: integer + description: | + The size of the ephemeral disk that will be created, in + GiB. Ephemeral disks may be written over on server state + changes. So should only be used as a scratch space for + applications that are aware of its limitations. Defaults to 0. +flavor_ephem_disk_2_47: + min_version: 2.47 + in: body + required: true + type: integer + description: | + The size of the ephemeral disk that was created, in GiB. +flavor_ephem_disk_in: + in: body + required: false + type: integer + description: | + The size of the ephemeral disk that will be created, in + GiB. Ephemeral disks may be written over on server state + changes. So should only be used as a scratch space for + applications that are aware of its limitations. Defaults to 0. +flavor_extra_spec_key2: + description: | + The extra spec key of a flavor. It appears in the os-extra-specs' + "create" and "update" REQUEST body, as well as the os-extra-specs' + "create", "list", "show", and "update" RESPONSE body. + in: body + required: true + type: string +flavor_extra_spec_key_2_47: + description: | + The extra spec key of a flavor. + in: body + required: true + type: string + min_version: 2.47 +flavor_extra_spec_value: + description: | + The extra spec value of a flavor. It appears in the os-extra-specs' + "create" and "update" REQUEST body, as well as the os-extra-specs' + "create", "list", "show", and "update" RESPONSE body. + in: body + required: true + type: string +flavor_extra_spec_value_2_47: + description: | + The extra spec value of a flavor. + in: body + required: true + type: string + min_version: 2.47 +flavor_id_body: + description: | + The ID of the flavor. While people often make this look like + an int, this is really a string. + in: body + required: true + type: string +flavor_id_body_2_46: + description: | + The ID of the flavor. While people often make this look like + an int, this is really a string. + in: body + required: true + type: string + max_version: 2.46 +flavor_id_body_create: + description: | + The ID of the flavor. While people often make this look like an int, this + is really a string. If not provided, this defaults to a uuid. + in: body + required: false + type: string +flavor_is_public: + description: | + Whether the flavor is public (available to all projects) or scoped + to a set of projects. Default is True if not specified. + in: body + required: true + type: boolean +flavor_is_public_in: + description: | + Whether the flavor is public (available to all projects) or scoped + to a set of projects. Default is True if not specified. + in: body + required: false + type: boolean +flavor_links_2_46: + description: | + Links to the flavor resource. See `API Guide / Links and + References + `_ + for more info. + in: body + required: true + type: array + max_version: 2.46 +flavor_name: + description: | + The display name of a flavor. + in: body + required: true + type: string +flavor_name_optional: + description: | + The display name of a flavor. + in: body + required: false + type: string +flavor_original_name: + description: | + The display name of a flavor. + in: body + required: true + type: string + min_version: 2.47 +flavor_ram: + description: | + The amount of RAM a flavor has, in MiB. + in: body + required: true + type: integer +flavor_ram_2_47: + description: | + The amount of RAM a flavor has, in MiB. + in: body + required: true + type: integer + min_version: 2.47 +flavor_rxtx_factor: + description: | + The receive / transmit factor (as a float) that will be set on + ports if the network backend supports the QOS extension. + Otherwise it will be ignored. It defaults to 1.0. + in: body + required: true + type: float +flavor_rxtx_factor_in: + description: | + The receive / transmit factor (as a float) that will be set on + ports if the network backend supports the QOS extension. + Otherwise it will be ignored. It defaults to 1.0. + in: body + required: false + type: float +flavor_server: + description: | + Before microversion 2.47 this contains the ID and links for the flavor + used to boot the server instance. This can be an empty object in case + flavor information is no longer present in the system. + + As of microversion 2.47 this contains a subset of the actual flavor + information used to create the server instance, represented as a nested + dictionary. + in: body + required: true + type: object +flavor_swap: + description: | + The size of a dedicated swap disk that will be allocated, in + MiB. If 0 (the default), no dedicated swap disk will be created. + Currently, the empty string ('') is used to represent 0. + in: body + required: true + type: integer +flavor_swap_2_47: + description: | + The size of a dedicated swap disk that was allocated, in MiB. + in: body + required: true + type: integer + min_version: 2.47 +flavor_swap_in: + description: | + The size of a dedicated swap disk that will be allocated, in + MiB. If 0 (the default), no dedicated swap disk will be created. + in: body + required: false + type: integer +flavorRef: + description: | + The flavor reference, as an ID (including a UUID) or full URL, + for the flavor for your server instance. + in: body + required: true + type: string +flavorRef_resize: + description: | + The flavor ID for resizing the server. The size of the disk in the flavor + being resized to must be greater than or equal to the size of the disk in + the current flavor. + + If a specified flavor ID is the same as the current one of the server, + the request returns a ``Bad Request (400)`` response code. + in: body + required: true + type: string +flavors: + description: | + An array of flavor objects. + in: body + required: true + type: array +floating_ip: + description: | + The floating ip address. + in: body + required: true + type: string +floating_ip_bulk_object: + description: | + The floating ip bulk address object. + in: body + required: true + type: object +floating_ip_id_value: + description: | + The floating IP id value. + + .. note:: + + For nova-network, the value will be of type integer, whereas for + neutron, the value will be of type string. + in: body + required: true + type: string +floating_ip_obj: + description: | + A floating IP address object. + in: body + required: true + type: object +floating_ip_pool_name: + description: | + The name of the floating ip pool. + in: body + required: true + type: string +floating_ip_pool_name_optional: + description: | + The name of the floating ip pool + in: body + required: false + type: string +floating_ip_pools: + description: | + The ``floating_ip_pools`` object. + in: body + required: true + type: array +floating_ips: + description: | + The number of allowed floating IP addresses for each tenant. + in: body + required: true + type: integer + max_version: 2.35 +floating_ips_list: + description: | + An array of floating ip objects. + in: body + required: true + type: array +floating_ips_quota_class: &floating_ips_quota_class + description: | + The number of allowed floating IP addresses for the quota class. + in: body + required: true + type: integer + max_version: 2.49 +floating_ips_quota_class_optional: + <<: *floating_ips_quota_class + required: false +floating_ips_quota_details: + description: | + The object of detailed floating ips quota, including in_use, limit and + reserved number of floating ips. + in: body + required: true + type: object + max_version: 2.35 +floating_ips_quota_optional: + description: | + The number of allowed floating IP addresses for each tenant. + in: body + required: false + type: integer + max_version: 2.35 +force: + description: | + You can force the update even if the quota has already been used and the reserved + quota exceeds the new quota. To force the update, specify the ``"force": "True"``. + Default is ``False``. + in: body + required: false + type: boolean +force_evacuate: + description: | + Force an evacuation by not verifying the provided destination host by the + scheduler. + in: body + required: false + type: boolean + min_version: 2.29 +force_live_migrate: + description: | + Force a live-migration by not verifying the provided destination host by + the scheduler. + in: body + required: false + type: boolean + min_version: 2.30 +force_migration_complete: + description: | + The action. + in: body + required: true + type: string + min_version: 2.22 +force_snapshot: + description: | + Indicates whether to create a snapshot, even if the volume is attached. + in: body + required: false + type: boolean +# This is both the request and response parameter for +# PUT /os-services/force-down which was added in 2.11. +forced_down_2_11: + description: | + Whether or not this service was forced down manually by an + administrator. This value is useful to know that some 3rd party has + verified the service should be marked down. + in: body + required: true + type: boolean + min_version: 2.11 +# This is the optional request input parameter for +# PUT /os-services/{service_id} added in 2.53. +forced_down_2_53_in: + description: | + Whether or not this service was forced down manually by an + administrator. This value is useful to know that some 3rd party has + verified the service should be marked down. + in: body + required: false + type: boolean +# This is the response output parameter for +# PUT /os-services/{service_id} added in 2.53. +forced_down_2_53_out: + description: | + Whether or not this service was forced down manually by an + administrator. This value is useful to know that some 3rd party has + verified the service should be marked down. + in: body + required: true + type: boolean +forceDelete: + description: | + The action. + in: body + required: true + type: none +free_ram_mb: + description: | + The free RAM in this hypervisor(in MB). + in: body + required: true + type: integer +from_port: + description: | + The port at start of range. + in: body + required: true + type: integer +group: + description: | + A ``group`` object. Includes the ``tenant_id`` and the source security + group ``name``. + in: body + required: true + type: object +group_id: + description: | + The source security group ID. + in: body + required: false + type: string +guest_format: + description: | + Specifies the guest server disk file system format, such as ``ephemeral`` or ``swap``. + in: body + required: true + type: string +host: + description: | + The name or ID of the host to which the server is evacuated. + If you omit this parameter, the scheduler chooses a host. + in: body + required: false + type: string +host_cpu: + description: | + The number of virtual CPUs on the host. + in: body + required: true + type: integer +host_disk_gb: + description: | + The disk size on the host (in GB). + in: body + required: true + type: integer +host_done_num: + description: | + The number of the hosts whose instance audit tasks have been done. + in: body + required: true + type: integer +host_ip: + description: | + The IP address of the hypervisor's host. + in: body + required: true + type: string +host_maintenance_mode: + description: | + Mode of maintenance state, either ``on_maintenance`` or + ``off_maintenance``. + in: body + required: false + type: string +host_maintenance_mode_in: + description: | + Mode of maintenance state, either ``enable`` or ``disable``. + in: body + required: false + type: string +host_memory_mb: + description: | + The memory size on the host (in MB). + in: body + required: true + type: integer +host_migration: + description: | + The host to which to migrate the server. If this parameter is ``None``, the scheduler + chooses a host. + in: body + required: true + type: string +host_name_body: + description: | + The name of the host. + in: body + required: true + type: string +host_not_run: + description: | + A list of the hosts whose instance audit tasks have not run. + in: body + required: true + type: array +host_not_run_num: + description: | + The number of the hosts whose instance audit tasks have not run. + in: body + required: true + type: integer +host_num: + description: | + The number of the hosts. + in: body + required: true + type: integer +host_power_action: + description: | + The power action on the host. + in: body + required: true + type: string +host_project: + description: | + The project id (or special name like total, used_now, used_max). + in: body + required: true + type: string +host_resource: + description: | + The resource info of the host. + in: body + required: true + type: object +host_resource_array: + description: | + The array that includes resource info of the host. + in: body + required: true + type: array +host_running_num: + description: | + The number of the hosts whose instance audit tasks are running. + in: body + required: true + type: integer +host_service: + description: | + The name of the service which is running on the host. + in: body + required: true + type: string +host_status: + description: | + The host status. Values where next value in list can override the previous: + + - ``UP`` if nova-compute up. + + - ``UNKNOWN`` if nova-compute not reported by servicegroup driver. + + - ``DOWN`` if nova-compute forced down. + + - ``MAINTENANCE`` if nova-compute is disabled. + + - Empty string indicates there is no host for server. This attribute + appears in the response only if the policy permits. + in: body + required: true + type: string + min_version: 2.16 +host_status_body: + description: | + The status of the current host, either ``enabled`` or ``disabled``. + in: body + required: false + type: string +host_status_body_in: + description: | + The status of the host, either ``enable`` or ``disable``. + in: body + required: false + type: string +host_zone: + description: | + The available zone of the host. + in: body + required: true + type: string +hostId: + description: | + An ID string representing the host. This is a hashed value so will not actually look like + a hostname, and is hashed with data from the project_id, so the same physical host as seen + by two different project_ids, will be different. It is useful when within the same project you + need to determine if two instances are on the same or different physical hosts for the + purposes of availability or performance. + in: body + required: true + type: string +hosts: + description: | + An array of host information. + in: body + required: true + type: array +hosts.availability_zone: + description: | + An object containing a list of host information. The host information is comprised + of host and service objects. The service object returns three parameters representing + the states of the service: ``active``, ``available``, and ``updated_at``. + in: body + required: true + type: object +hosts.availability_zone_none: + description: | + It is always ``null``. + in: body + required: true + type: none +hours: + description: | + The duration that the server exists (in hours). + in: body + required: true + type: float +hours_optional: + description: | + The duration that the server exists (in hours). + in: body + required: false + type: float +hypervisor: + description: | + The hypervisor object. + in: body + required: true + type: object +hypervisor_count: + description: | + The number of hypervisors. + in: body + required: true + type: integer +hypervisor_diagnostics: + description: | + The hypervisor on which the VM is running. Examples for libvirt driver + may be: ``qemu``, ``kvm`` or ``xen``. + in: body + required: true + type: string + min_version: 2.48 +hypervisor_free_disk_gb: + description: | + The free disk remaining on this hypervisor(in GB). + in: body + required: true + type: integer +hypervisor_hostname: + description: | + The hypervisor host name provided by the Nova virt driver. For the Ironic driver, + it is the Ironic node uuid. + in: body + required: true + type: string +hypervisor_id_body: + description: | + The id of the hypervisor. + in: body + required: true + type: integer + max_version: 2.52 +hypervisor_id_body_no_version: + description: | + The id of the hypervisor. + in: body + required: true + type: integer +hypervisor_id_body_uuid: + description: | + The id of the hypervisor as a UUID. + in: body + required: true + type: string + min_version: 2.53 +hypervisor_links: + description: | + Links to the hypervisors resource. See `API Guide / Links and + References + `_ + for more info. + in: body + type: array + min_version: 2.33 + required: false +hypervisor_os_diagnostics: + description: | + The hypervisor OS. + in: body + type: string + required: true + min_version: 2.48 +hypervisor_servers: + description: | + A list of ``server`` objects. + in: body + required: false + type: array + min_version: 2.53 +hypervisor_servers_name: + description: | + The server name. + in: body + required: false + type: string + min_version: 2.53 +hypervisor_servers_uuid: + description: | + The server ID. + in: body + required: false + type: string + min_version: 2.53 +hypervisor_service: + description: | + The hypervisor service object. + in: body + required: true + type: object +hypervisor_state: + description: | + The state of the hypervisor. One of ``up`` or ``down``. + in: body + required: true + type: string +hypervisor_statistics: + description: | + The hypervisors statistics summary object. + in: body + required: true + type: object +hypervisor_status: + description: | + The status of the hypervisor. One of ``enabled`` or ``disabled``. + in: body + required: true + type: string +hypervisor_type: + in: body + required: true + type: string + description: | + The hypervisor type for the agent. Currently only ``xen`` is supported. +hypervisor_type_body: + description: | + The hypervisor type. + in: body + required: true + type: string +hypervisor_vcpus: + description: | + The number of vcpu in this hypervisor. + in: body + required: true + type: integer +hypervisor_vcpus_used: + description: | + The number of vcpu used in this hypervisor. + in: body + required: true + type: integer +hypervisor_version: + description: | + The hypervisor version. + in: body + required: true + type: integer +hypervisors: + description: | + An array of hypervisor information. + in: body + required: true + type: array +image: + description: | + The UUID and links for the image for your server instance. The ``image`` object + might be an empty string when you boot the server from a volume. + in: body + required: true + type: object +image_id_body: + description: | + The ID of the Image. + in: body + required: true + type: string +image_metadata: + description: | + Metadata key and value pairs for the image. + The maximum size for each metadata key and value pair is 255 bytes. + in: body + required: false + type: object +image_metadata_items: + description: | + The number of allowed metadata items for each image. Starting from + version 2.39 this field is dropped from 'os-limits' response, because + 'image-metadata' proxy API was deprecated. + in: body + required: true + type: integer + max_version: 2.38 +image_name: + description: | + The display name of an Image. + in: body + required: true + type: string +image_progress: + description: | + A percentage value of the image save progress. This can be one of: + + - ``ACTIVE``: 100 + - ``SAVING``: 25 or 50 + in: body + required: true + type: integer +image_server: + description: | + The server booted from image. + in: body + required: false + type: object +image_size: + description: | + The size of the image. + in: body + required: true + type: integer +image_status: + description: | + The status of image, as a string. This can be one of: + + - ``ACTIVE``: image is in active state + - ``SAVING``: image is in queued or in saving process + - ``DELETED``: image is deleted or in progress of deletion + - ``ERROR``: image is in error state + - ``UNKNOWN``: image is in unknown state + in: body + required: true + type: string +imageRef: + description: | + The UUID of the image to use for your server instance. + This is not required in case of boot from volume. + In all other cases it is required and must be a valid UUID + otherwise API will return 400. + in: body + required: true + type: string +imageRef_rebuild: + description: | + The UUID of the image to rebuild for your server instance. + It must be a valid UUID otherwise API will return 400. + in: body + required: true + type: string +images: + description: | + An array of Image objects. + in: body + required: true + type: array +injected_file_content_bytes: + description: | + The number of allowed bytes of content for each injected file. + in: body + required: true + type: integer +injected_file_content_bytes_quota_details: + description: | + The object of detailed injected file content bytes quota, + including in_use, limit and reserved number of injected + file content bytes. + in: body + required: true + type: object +injected_file_content_bytes_quota_optional: + description: | + The number of allowed bytes of content for each injected file. + in: body + required: false + type: integer +injected_file_path_bytes: + description: | + The number of allowed bytes for each injected file path. + in: body + required: true + type: integer +injected_file_path_bytes_quota_details: + description: | + The object of detailed injected file path bytes quota, + including in_use, limit and reserved number of injected + file path bytes. + in: body + required: true + type: object +injected_file_path_bytes_quota_optional: + description: | + The number of allowed bytes for each injected file path. + in: body + required: false + type: integer +injected_files: &injected_files + description: | + The number of allowed injected files for each tenant. + in: body + required: true + type: integer +injected_files_quota_class: &injected_files_quota_class + <<: *injected_files + description: | + The number of allowed injected files for the quota class. +injected_files_quota_class_optional: + <<: *injected_files_quota_class + required: false +injected_files_quota_details: + description: | + The object of detailed injected files quota, including in_use, limit and + reserved number of injected files. + in: body + required: true + type: object +injected_files_quota_optional: + description: | + The number of allowed injected files for each tenant. + in: body + required: false + type: integer +injectNetworkInfo: + description: | + The action. + in: body + required: true + type: none +instance_action_events_2_50: + description: | + The events which occurred in this action. + + Policy defaults enable only users with the administrative role to see + instance action event information. Cloud providers can change these + permissions through the ``policy.json`` file. + in: body + required: false + type: array + max_version: 2.50 +instance_action_events_2_51: + description: | + The events which occurred in this action. + + Policy defaults enable only users with the administrative role or the owner + of the server to see instance action event information. Cloud providers can + change these permissions through the ``policy.json`` file. + in: body + required: true + type: array + min_version: 2.51 +instance_id_body: + description: | + The UUID of the server. + in: body + required: true + type: string +instance_id_cloudpipe: + description: | + The UUID of the cloudpipe instance. + in: body + required: true + type: string +instance_name: + description: | + The name of the instance. + in: body + required: true + type: string +instance_usage_audit_log: + description: | + The object of instance usage audit logs. + in: body + required: true + type: object +instance_usage_audit_log_message: + description: | + The log message of the instance usage audit task. + in: body + required: true + type: string +instance_usage_audit_logs: + description: | + The object of instance usage audit log information. + in: body + required: true + type: object +instance_usage_audit_task_state: + description: | + The state of the instance usage audit task. + ``DONE`` or ``RUNNING``. + in: body + required: true + type: string +instanceAction: + description: | + The instance action object. + in: body + required: true + type: object +instanceActions: + description: | + List of the actions for the given instance. + in: body + required: true + type: array +instances: &instances + description: | + The number of allowed servers for each tenant. + in: body + required: true + type: integer +instances_quota_class: &instances_quota_class + <<: *instances + description: | + The number of allowed servers for the quota class. +instances_quota_class_optional: + <<: *instances_quota_class + required: false +instances_quota_details: + description: | + The object of detailed servers quota, including in_use, limit and + reserved number of instances. + in: body + required: true + type: object +instances_quota_optional: + description: | + The number of allowed servers for each tenant. + in: body + required: false + type: integer +instances_usage_audit: + description: | + The number of instances. + in: body + required: true + type: integer +interfaceAttachment: + description: | + Specify the ``interfaceAttachment`` action in the request body. + in: body + required: true + type: string +interfaceAttachment_resp: + description: | + The interface attachment. + in: body + required: true + type: object +interfaceAttachments: + description: | + List of the interface attachments. + in: body + required: true + type: array +internal_access_path: + description: | + The id representing the internal access path. + in: body + required: false + type: string +ip_address: + description: | + The IP address. + in: body + required: true + type: string +ip_address_req: + description: | + The IP address. It is required when ``fixed_ips`` is specified. + in: body + required: true + type: string +ip_host: + description: | + The name or ID of the host associated to the IP. + in: body + required: true + type: string +ip_protocol: + description: | + The IP protocol. A valid value is ICMP, TCP, or UDP. + in: body + required: true + type: string +ip_range: + description: | + The range of IP addresses to use for creating floating IPs. + in: body + required: true + type: string +ip_range_delete: + description: | + The range of IP addresses from which to bulk-delete floating IPs. + in: body + required: true + type: string +key_name: + description: | + Key pair name. + .. note:: + + The ``null`` value allowed in Nova legacy v2 API, but due to the strict + input validation, it isn't allowed in Nova v2.1 API. + in: body + required: false + type: string + +key_name_resp: + description: | + The name of associated key pair, if any. + in: body + required: true + type: string +key_pairs: &key_pairs + description: | + The number of allowed key pairs for each user. + in: body + required: true + type: integer +key_pairs_quota_class: &key_pairs_quota_class + <<: *key_pairs + description: | + The number of allowed key pairs for the quota class. +key_pairs_quota_class_optional: + <<: *key_pairs_quota_class + required: false +key_pairs_quota_details: + description: | + The object of detailed key pairs quota, including in_use, limit and + reserved number of key pairs. + in: body + required: true + type: object +key_pairs_quota_optional: + description: | + The number of allowed key pairs for each user. + in: body + required: false + type: integer +keypair: + in: body + type: object + required: true + description: | + Keypair object +keypair_deleted: + description: | + A boolean indicates whether this keypair is deleted or not. + The value is always ``false`` (not deleted). + in: body + required: true + type: boolean +keypair_fingerprint: + in: body + required: true + type: string + description: | + The fingerprint for the keypair. +keypair_id: + description: | + The keypair ID. + in: body + required: true + type: integer +keypair_links: + description: | + Links pertaining to keypair. See `API Guide / Links and + References + `_ + for more info. + in: body + type: array + required: false + min_version: 2.35 +keypair_name: + in: body + required: true + type: string + description: | + A name for the keypair which will be used to reference it later. +keypair_private_key: + description: | + If you do not provide a public key on create, a new keypair will + be built for you, and the private key will be returned during the + initial create call. Make sure to save this, as there is no way to + get this private key again in the future. + in: body + required: false + type: string +keypair_public_key: + description: | + The keypair public key. + in: body + required: true + type: string +keypair_public_key_in: + description: | + The public ssh key to import. If you omit this value, a keypair is + generated for you. + in: body + required: false + type: string +keypair_type: + in: body + required: true + type: string + description: | + The type of the keypair. Allowed values are ``ssh`` or ``x509``. + min_version: 2.2 +keypair_type_in: + in: body + required: false + type: string + description: | + The type of the keypair. Allowed values are ``ssh`` or ``x509``. + min_version: 2.2 +keypair_updated_deleted_at: + description: | + It is always ``null``. + in: body + required: true + type: none +# NOTE(mriedem): This is the user_id description for the keypair create/show +# response which has always been returned. +keypair_userid: + in: body + required: true + type: string + description: | + The user_id for a keypair. +keypair_userid_in: + in: body + required: false + type: string + description: | + The user_id for a keypair. This allows administrative users to + upload keys for other users than themselves. + min_version: 2.10 +keypairs: + in: body + type: array + required: true + description: | + Array of Keypair objects +length: + description: | + The number of lines to fetch from the end of console log. All + lines will be returned if this is not specified. + + .. note:: + + This parameter can be specified as not only 'integer' but also 'string'. + + in: body + required: false + type: integer +limits: + description: | + Data structure that contains both absolute limits within a deployment. + in: body + required: true + type: object +limits_absolutes: + description: | + Name/value pairs that set quota limits within a deployment and + Name/value pairs of resource usage. + in: body + required: true + type: object +limits_rate_uri: + description: | + A human readable URI that is used as a friendly description of where the api rate limit is applied. + in: body + required: true + type: string +limits_rates: + description: | + An empty list for backwards compatibility purposes. + in: body + required: true + type: array +links: + description: | + Links to the resources in question. See `API Guide / Links and + References + `_ + for more info. + in: body + required: true + type: array +local_gb: + description: | + The disk in this hypervisor(in GB). + in: body + required: true + type: integer +local_gb_simple_tenant_usage: + description: | + The sum of the root disk size of the server and + the ephemeral disk size of it (in GiB). + in: body + required: true + type: integer +local_gb_simple_tenant_usage_optional: + description: | + The sum of the root disk size of the server and + the ephemeral disk size of it (in GiB). + in: body + required: false + type: integer +local_gb_used: + description: | + The disk used in this hypervisor(in GB). + in: body + required: true + type: integer +lock: + description: | + The action to lock a server. + in: body + required: true + type: none +locked: + description: | + True if the instance is locked otherwise False. + in: body + required: true + type: boolean + min_version: 2.9 +mac_addr: + description: | + The MAC address. + in: body + required: true + type: string +mac_address: + description: | + The MAC address. + in: body + required: true + type: string +md5hash: + description: | + The MD5 hash. + in: body + required: true + type: string +media_types: + description: | + The `media types + `_. + It is an array of a fixed dict. + + .. note:: + It is vestigial and provide no useful information. + It will be deprecated and removed in the future. + in: body + required: true + type: array +members: + description: | + A list of members in the server group. + in: body + required: true + type: array +memory_details_diagnostics: + description: | + The dictionary with information about VM memory usage. + Following fields are presented in the dictionary: + + - ``maximum`` - Amount of memory provisioned for the VM in MB (Integer) + + - ``used`` - Amount of memory that is currently used by the guest operating + system and its applications in MB (Integer) + in: body + required: true + type: array + min_version: 2.48 +memory_mb: + description: | + The memory of this hypervisor(in MB). + in: body + required: true + type: integer +memory_mb_simple_tenant_usage: + description: | + The memory size of the server (in MB). + in: body + required: true + type: integer +memory_mb_simple_tenant_usage_optional: + description: | + The memory size of the server (in MB). + in: body + required: false + type: integer +memory_mb_used: + description: | + The memory used in this hypervisor(in MB). + in: body + required: true + type: integer +message: + description: | + The related error message for when an action fails. + in: body + required: true + type: string +meta: + description: | + The object of detailed key metadata items. + in: body + required: true + type: object +metadata: + description: | + Metadata key and value pairs. The maximum size of the metadata key and value is + 255 bytes each. + in: body + required: false + type: object +metadata_compat: + description: | + A dictionary of metadata key-and-value pairs, which is maintained for backward + compatibility. + in: body + required: true + type: object +metadata_items: + description: | + The number of allowed metadata items for each server. + in: body + required: true + type: integer +metadata_items_quota_details: + description: | + The object of detailed key metadata items quota, including in_use, + limit and reserved number of metadata items. + in: body + required: true + type: object +metadata_items_quota_optional: + description: | + The number of allowed metadata items for each server. + in: body + required: false + type: integer +metadata_object: + description: | + Metadata key and value pairs. The maximum size for each metadata key and value + pair is 255 bytes. + in: body + required: true + type: object +migrate: + description: | + The action. + in: body + required: true + type: string +migrate_dest_compute: + description: | + The target compute for a migration. + in: body + required: true + type: string +migrate_dest_host: + description: | + The target host for a migration. + in: body + required: true + type: string +migrate_dest_node: + description: | + The target node for a migration. + in: body + required: true + type: string +migrate_disk_processed_bytes: + description: | + The amount of disk, in bytes, that has been processed during the migration. + in: body + required: true + type: integer +migrate_disk_remaining_bytes: + description: | + The amount of disk, in bytes, that still needs to be migrated. + in: body + required: true + type: integer +migrate_disk_total_bytes: + description: | + The total amount of disk, in bytes, that needs to be migrated. + in: body + required: true + type: integer +migrate_memory_processed_bytes: + description: | + The amount of memory, in bytes, that has been processed during the migration. + in: body + required: true + type: integer +migrate_memory_remaining_bytes: + description: | + The amount of memory, in bytes, that still needs to be migrated. + in: body + required: true + type: integer +migrate_memory_total_bytes: + description: | + The total amount of memory, in bytes, that needs to be migrated. + in: body + required: true + type: integer +migrate_source_compute: + description: | + The source compute for a migration. + in: body + required: true + type: string +migrate_source_node: + description: | + The source node for a migration. + in: body + required: true + type: string +migrate_status: + description: | + The current status of the migration. + in: body + required: true + type: string +migration: + description: | + The server migration object. + in: body + required: true + type: object +migration_id: + description: | + The ID of the server migration. + in: body + required: true + type: integer +migration_links_2_23: + description: | + Links to the migration. + This parameter is returned if the migration type is ``live-migration`` and + the migration status is one of ``queued``, ``preparing``, ``running`` + and ``post-migrating``. See `API Guide / Links and References + `_ + for more info. + in: body + required: false + type: array + min_version: 2.23 +migration_new_flavor_id: + description: | + In ``resize`` case, the flavor ID for resizing the server. + In the other cases, this parameter is same as the flavor ID + of the server when the migration was started. + in: body + required: true + type: string +migration_old_flavor_id: + description: | + The flavor ID of the server when the migration was started. + in: body + required: true + type: string +migration_type_2_23: + description: | + The type of the server migration. This is one of ``live-migration``, + ``migration``, ``resize`` and ``evacuation``. + in: body + required: true + type: string + min_version: 2.23 +migrations: + description: | + The list of server migration objects. + in: body + required: true + type: array +minDisk_body: + description: | + The minimum amount of disk space an image requires to boot, in GiB. For example, ``100``. + in: body + required: true + type: integer +minRam_body: + description: | + The minimum amount of RAM an image requires to function, in MB. For example, ``512``. + in: body + required: true + type: integer +name: + description: | + The security group name. + in: body + required: true + type: string +name_server_group: + description: | + The name of the server group. + in: body + required: true + type: string +namespace: + description: | + A URL pointing to the namespace for this extension. + in: body + required: true + type: string +net_id: + description: | + The ID of the network for which you want to create a port interface. The ``net_id`` + and ``port_id`` parameters are mutually exclusive. If you do not specify the + ``net_id`` parameter, the OpenStack Networking API v2.0 uses the network information + cache that is associated with the instance. + in: body + required: false + type: string +net_id_resp: + description: | + The network ID. + in: body + required: true + type: string +net_id_resp_2_12: + description: | + The network ID. + in: body + required: true + type: string + min_version: 2.12 +network_label_body: + description: | + List of IP address and IP version pairs. + The ``network_label`` stands for the name of a network, + such as ``public`` or ``private``. + in: body + required: true + type: array +network_uuid: + description: | + To provision the server instance with a NIC for a network, specify the UUID of + the network in the ``uuid`` attribute in a ``networks`` object. Required if you + omit the ``port`` attribute. + + Starting with microversion 2.37, this value is strictly enforced to be in + UUID format. + in: body + required: false + type: string +networks: + description: | + A ``networks`` object. Required parameter when there are multiple networks defined + for the tenant. When you do not specify the networks parameter, the server attaches + to the only network created for the current tenant. Optionally, you can create + one or more NICs on the server. To provision the server instance with a NIC for + a network, specify the UUID of the network in the ``uuid`` attribute in a ``networks`` + object. To provision the server instance with a NIC for an already existing port, + specify the port-id in the ``port`` attribute in a ``networks`` object. + + If multiple networks are defined, the order in which they appear in the + guest operating system will not necessarily reflect the order in which they + are given in the server boot request. Guests should therefore not depend + on device order to deduce any information about their network devices. + Instead, device role tags should be used: introduced in 2.32, broken in + 2.37, and re-introduced and fixed in 2.42, the ``tag`` is an optional, + string attribute that can be used to assign a tag to a virtual network + interface. This tag is then exposed to the guest in the metadata API and + the config drive and is associated to hardware metadata for that network + interface, such as bus (ex: PCI), bus address (ex: 0000:00:02.0), and MAC + address. + + A bug has caused the ``tag`` attribute to no longer be accepted starting + with version 2.37. Therefore, network interfaces could only be tagged in + versions 2.32 to 2.36 inclusively. Version 2.42 has restored the ``tag`` + attribute. + + Starting with microversion 2.37, this field is required and the special + values *auto* and *none* can be specified for networks. *auto* tells the + Compute service to use a network that is available to the project, if one + exists. If one does not exist, the Compute service will attempt to + automatically allocate a network for the project (if possible). *none* + tells the Compute service to not allocate a network for the instance. The + *auto* and *none* values cannot be used with any other network values, + including other network uuids, ports, fixed IPs or device tags. These are + requested as strings for the networks value, not in a list. See the + associated example. + in: body + required: true + type: object +networks_quota_optional: &networks_quota_optional + description: | + The number of private networks that can be created per project. + in: body + required: false + type: integer + max_version: 2.49 +networks_quota_set_optional: + <<: *networks_quota_optional + max_version: 2.35 +new_file: + description: | + The name of the qcow2 file that Block Storage creates, which becomes the active + image for the VM. + in: body + required: true + type: string +nic_details_diagnostics: + description: | + The list of dictionaries with detailed information about VM NICs. + Following fields are presented in each dictionary: + + - ``mac_address`` - Mac address of the interface (String) + + - ``rx_octets`` - Received octets (Integer) + + - ``rx_errors`` - Received errors (Integer) + + - ``rx_drop`` - Received packets dropped (Integer) + + - ``rx_packets`` - Received packets (Integer) + + - ``rx_rate`` - Receive rate in bytes (Integer) + + - ``tx_octets`` - Transmitted Octets (Integer) + + - ``tx_errors`` - Transmit errors (Integer) + + - ``tx_drop`` - Transmit dropped packets (Integer) + + - ``tx_packets`` - Transmit packets (Integer) + + - ``tx_rate`` - Transmit rate in bytes (Integer) + in: body + required: true + type: array + min_version: 2.48 +num_cpus_diagnostics: + description: | + The number of vCPUs. + in: body + required: true + type: integer + min_version: 2.48 +num_disks_diagnostics: + description: | + The number of disks. + in: body + required: true + type: integer + min_version: 2.48 +num_nics_diagnostics: + description: | + The number of vNICs. + in: body + required: true + type: integer + min_version: 2.48 +on_shared_storage: + description: | + Server on shared storage. + + .. note:: + + Starting since version 2.14, Nova automatically detects + whether the server is on shared storage or not. + Therefore this parameter was removed. + in: body + required: true + type: boolean + max_version: 2.13 +os: + description: | + The name of the operating system. + in: body + required: true + type: string +os-availability-zone:availability_zone: + description: | + The availability zone from which to launch the server. When you provision resources, + you specify from which availability zone you want your instance to be built. Typically, + you use availability zones to arrange OpenStack compute hosts into logical groups. + An availability zone provides a form of physical isolation and redundancy from + other availability zones. For instance, if some racks in your data center are + on a separate power source, you can put servers in those racks in their own availability + zone. Availability zones can also help separate different classes of hardware. By + segregating resources into availability zones, you can ensure that your application + resources are spread across disparate machines to achieve high availability in + the event of hardware or other failure. + in: body + required: false + type: string +OS-DCF:diskConfig: + description: | + Controls how the API partitions the disk when you create, rebuild, or resize servers. + A server inherits the ``OS-DCF:diskConfig`` value from the image from which it + was created, and an image inherits the ``OS-DCF:diskConfig`` value from the server + from which it was created. To override the inherited setting, you can include + this attribute in the request body of a server create, rebuild, or resize request. If + the ``OS-DCF:diskConfig`` value for an image is ``MANUAL``, you cannot create + a server from that image and set its ``OS-DCF:diskConfig`` value to ``AUTO``. + A valid value is: + + - ``AUTO``. The API builds the server with a single partition the size of the + target flavor disk. The API automatically adjusts the file system to fit the + entire partition. + + - ``MANUAL``. The API builds the server by using whatever partition scheme and + file system is in the source image. If the target flavor disk is larger, the API + does not partition the remaining disk space. + in: body + required: false + type: string +OS-EXT-AZ:availability_zone: + description: | + The availability zone name. + in: body + required: true + type: string +OS-EXT-AZ:availability_zone_optional: + description: | + The availability zone name. + in: body + required: false + type: string +OS-EXT-SRV-ATTR:host: + description: | + The name of the compute host on which this instance is running. + Appears in the response for administrative users only. + in: body + required: true + type: string +OS-EXT-SRV-ATTR:hypervisor_hostname: + description: | + The hypervisor host name provided by the Nova virt driver. For the Ironic driver, + it is the Ironic node uuid. Appears in the response for administrative users only. + in: body + required: true + type: string +OS-EXT-SRV-ATTR:instance_name: + description: | + The instance name. The Compute API generates the instance name from the instance + name template. Appears in the response for administrative users only. + in: body + required: true + type: string +OS-EXT-STS:power_state: + description: | + The power state of the instance. This is an enum value that is mapped as:: + + 0: NOSTATE + 1: RUNNING + 3: PAUSED + 4: SHUTDOWN + 6: CRASHED + 7: SUSPENDED + in: body + required: true + type: integer +OS-EXT-STS:task_state: + description: | + The task state of the instance. + in: body + required: true + type: string +OS-EXT-STS:vm_state: + description: | + The VM state. + in: body + required: true + type: string +os-extended-volumes:volumes_attached: + description: | + The attached volumes, if any. + in: body + required: true + type: array +os-extended-volumes:volumes_attached.delete_on_termination: + description: | + A flag indicating if the attached volume will be deleted + when the server is deleted. By default this is False and + can only be set when creating a volume while creating a + server, which is commonly referred to as boot from volume. + in: body + required: true + type: boolean + min_version: 2.3 +os-extended-volumes:volumes_attached.id: + description: | + The attached volume ID. + in: body + required: true + type: string +os-getConsoleOutput: + description: | + The action to get console output of the server. + in: body + required: true + type: object +os-getRDPConsole: + description: | + The action. + in: body + required: true + type: object +os-getRDPConsole-type: + description: | + The type of RDP console. The only valid value is ``rdp-html5``. + in: body + required: true + type: string +os-getRDPConsole-url: + description: | + The URL used to connect to the RDP console. + in: body + required: true + type: string +os-getSerialConsole: + description: | + The action. + in: body + required: true + type: object +os-getSerialConsole-type: + description: | + The type of serial console. The only valid value is ``serial``. + in: body + required: true + type: string +os-getSerialConsole-url: + description: | + The URL used to connect to the Serial console. + in: body + required: true + type: string +os-getSPICEConsole: + description: | + The action. + in: body + required: true + type: object +os-getSPICEConsole-type: + description: | + The type of SPICE console. The only valid value is ``spice-html5``. + in: body + required: true + type: string +os-getSPICEConsole-url: + description: | + The URL used to connect to the SPICE console. + in: body + required: true + type: string +os-getVNCConsole: + description: | + The action. + in: body + required: true + type: object +os-getVNCConsole-type: + description: | + The type of VNC console. The valid values are ``novnc`` and ``xvpvnc``. + in: body + required: true + type: string +os-getVNCConsole-url: + description: | + The URL used to connect to the VNC console. + in: body + required: true + type: string +os-migrateLive: + description: | + The action. + in: body + required: true + type: object +os-resetState: + description: | + The action. + in: body + required: true + type: object +os-resetState_state: + description: | + The state of the server to be set, ``active`` or ``error`` are valid. + in: body + required: true + type: string +OS-SRV-USG:launched_at: + description: | + The date and time when the server was launched. + + The date and time stamp format is `ISO 8601 `_: + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + For example, ``2015-08-27T09:49:58-05:00``. + + The ``hh±:mm`` value, if included, is the time zone as an offset from UTC. + If the ``deleted_at`` date and time stamp is not set, its value is ``null``. + in: body + required: true + type: string +OS-SRV-USG:terminated_at: + description: | + The date and time when the server was deleted. + + The date and time stamp format is `ISO 8601 `_: + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + For example, ``2015-08-27T09:49:58-05:00``. + The ``±hh:mm`` value, if included, is the time zone as an offset from UTC. + If the ``deleted_at`` date and time stamp is not set, its value is ``null``. + in: body + required: true + type: string +os-start: + description: | + The action to start a stopped server. + in: body + required: true + type: none +os-stop: + description: | + The action to stop a running server. + in: body + required: true + type: none +os:scheduler_hints: + description: | + The dictionary of data to send to the scheduler. Alternatively, you can specify + ``OS-SCH-HNT:scheduler_hints`` as the string. + in: body + required: false + type: object +overall_status: + description: | + The overall status of instance audit tasks. + + :: + + M of N hosts done. K errors. + + The ``M`` value is the number of hosts whose instance audit tasks have + been done in the period. The ``N`` value is the number of all hosts. + The ``K`` value is the number of hosts whose instance audit tasks + cause errors. If instance audit tasks have been done at all hosts + in the period, the overall status is as follows: + + :: + + ALL hosts done. K errors. + + in: body + required: true + type: string +para: + description: | + The parameter object. + in: body + required: true + type: object +parent_group_id: + description: | + Security group ID. + in: body + required: true + type: string +password: + description: | + The password returned from metadata server. + in: body + required: false + type: string +path: + description: | + The path field in the personality object. + in: body + required: true + type: string +pause: + description: | + The action to pause a server. + in: body + required: true + type: none +period_beginning: + description: | + The beginning time of the instance usage audit period. + For example, ``2016-05-01 00:00:00``. + in: body + required: true + type: string +period_ending: + description: | + The ending time of the instance usage audit period. + For example, ``2016-06-01 00:00:00``. + in: body + required: true + type: string +personality: + description: | + The file path and contents, text only, to inject into the server at launch. The + maximum size of the file path data is 255 bytes. The maximum limit is the number + of allowed bytes in the decoded, rather than encoded, data. + in: body + required: false + type: array +policies: + description: | + A list of exactly one policy name to associate with the server group. The + current valid policy names are: + + - ``anti-affinity`` - servers in this group must be scheduled to + different hosts. + - ``affinity`` - servers in this group must be scheduled to the same host. + - ``soft-anti-affinity`` - servers in this group should be scheduled to + different hosts if possible, but if not possible then they should still + be scheduled instead of resulting in a build failure. This policy was + added in microversion 2.15. + - ``soft-affinity`` - servers in this group should be scheduled to the same + host if possible, but if not possible then they should still be scheduled + instead of resulting in a build failure. This policy was added in + microversion 2.15. + in: body + required: true + type: array +pool: + description: | + Pool from which to allocate the IP address. If you omit this parameter, the call + allocates the floating IP address from the public pool. If no floating IP addresses + are available, the call returns the ``400`` response code with an informational + message. Policy defaults enable only users with the administrative role or the + owner of the server to perform this operation. Cloud providers can change these + permissions through the ``policy.json`` file. + in: body + required: false + type: string +port: + description: | + To provision the server instance with a NIC for an already existing port, + specify the port-id in the ``port`` attribute in a ``networks`` object. + The port status must be ``DOWN``. Required if you omit the ``uuid`` + attribute. Requested security groups are not applied to pre-existing ports. + in: body + required: false + type: string +port_id: + description: | + The ID of the port for which you want to create an interface. The ``net_id`` + and ``port_id`` parameters are mutually exclusive. If you do not specify the + ``port_id`` parameter, the OpenStack Networking API v2.0 allocates a port and + creates an interface for it on the network. + in: body + required: false + type: string +port_id_resp: + description: | + The port ID. + in: body + required: true + type: string +port_number: + description: | + The port number. + in: body + required: true + type: integer +port_state: + description: | + The port state. + in: body + required: true + type: string +preserve_ephemeral: + description: | + Indicates whether the server is rebuilt with the preservation of the ephemeral + partition (``true``). + + .. note:: + + This only works with baremetal servers provided by + Ironic. Passing it to any other server instance results in a + fault and will prevent the rebuild from happening. + in: body + required: false + type: boolean +previous: + description: | + Moves to the previous metadata item. + format: uri + in: body + required: false + type: string +private_key: + description: | + The secret key. + in: body + required: true + type: string +progress: + description: | + A percentage value of the build progress. + in: body + required: true + type: integer +project_id: + description: | + The UUID of the project. If omitted, the project ID defaults to the + calling tenant. + in: body + required: false + type: string +project_id_instance_action: + description: | + The UUID of the project that this server belongs to. + in: body + required: true + type: string +project_id_query_server: + description: | + Filter the list of servers by the given project ID. + + .. note:: + + 'tenant_id' can also be requested which is alias of 'project_id' + but that is not recommended to use as that will be removed in future. + in: body + required: false + type: string +project_id_server_group: + description: | + The project ID who owns the server group. + min_version: 2.13 + in: body + required: true + type: string +project_id_value: + description: | + The project id under which the bulk ip addresses are created + in: body + required: true + type: string +quota_class_id_body: + <<: *quota_class_id + in: body +quota_class_set: + description: | + A ``quota_class_set`` object. + in: body + required: true + type: object +quota_set: + description: | + A ``quota_set`` object. + in: body + required: true + type: object +quota_tenant_or_user_id_body: + description: | + The UUID of the tenant/user the quotas listed for. + in: body + required: true + type: string +ram: &ram + description: | + The amount of allowed server RAM, in MB, for each tenant. + in: body + required: true + type: integer +ram_quota_class: &ram_quota_class + <<: *ram + description: | + The amount of allowed instance RAM, in MB, for the quota class. +ram_quota_class_optional: + <<: *ram_quota_class + required: false +ram_quota_details: + description: | + The object of detailed key ram quota, including in_use, + limit and reserved number of ram. + in: body + required: true + type: object +ram_quota_optional: + description: | + The amount of allowed server RAM, in MB, for each tenant. + in: body + required: false + type: integer +reboot: + description: | + The action to reboot a server. + in: body + required: true + type: object +reboot_type: + description: | + The type of the reboot action. The valid values are ``HARD`` and ``SOFT``. + A ``SOFT`` reboot attempts a graceful shutdown and restart of the server. + A ``HARD`` reboot attempts a forced shutdown and restart of the server. + The ``HARD`` reboot corresponds to the power cycles of the server. + in: body + required: true + type: string +rebuild: + description: | + The action to rebuild a server. + in: body + required: true + type: object +remote_console: + description: | + The remote console object. + in: body + required: true + type: object +remote_console_protocol: + description: | + The protocol of remote console. The valid values are ``vnc``, ``spice``, + ``rdp``, ``serial`` and ``mks``. The protocol ``mks`` is added since + Microversion ``2.8``. + in: body + required: true + type: string +remote_console_type: + description: | + The type of remote console. The valid values are ``novnc``, ``xvpvnc``, + ``rdp-html5``, ``spice-html5``, ``serial``, and ``webmks``. The type + ``webmks`` is added since Microversion ``2.8``. + in: body + required: true + type: string +remote_console_url: + description: | + The URL is used to connect the console. + in: body + required: true + type: string +removeFixedIp: + description: | + The action to remove a fixed ip address from a server. + in: body + required: true + type: object +removeFloatingIp: + description: | + The action to remove or disassociate a floating IP address + from the server. + in: body + required: true + type: object +removeSecurityGroup: + description: | + The action to remove a security group from the server. + in: body + required: true + type: object +removeTenantAccess: + description: | + The action. + in: body + required: true + type: string +request_id_body: + description: | + The request id generated when execute the API of this action. + in: body + required: true + type: string +rescue: + description: | + The action to rescue a server. + in: body + required: true + type: object +rescue_image_ref: + description: | + The image reference to use to rescue your server instance. Specify the image reference + by ID or full URL. If you omit an image reference, default is the base image + reference. + in: body + required: false + type: string +reservation_id: + description: | + The reservation id for the server. This is an id that can + be useful in tracking groups of servers created with multiple + create, that will all have the same reservation_id. + in: body + required: true + type: string +reserved: + description: | + The reserved quota value. + in: body + required: true + type: integer +reserved_fixedip: + description: | + True if the fixed ip is reserved, otherwise False. + in: body + required: true + type: boolean + min_version: 2.4 +resetNetwork: + description: | + The action. + in: body + required: true + type: none +resize: + description: | + The action to resize a server. + in: body + required: true + type: object +restore: + description: | + The action. + in: body + required: true + type: none +resume: + description: | + The action to resume a suspended server. + in: body + required: true + type: none +return_reservation_id: + description: | + Set to ``True`` to request that the response return a reservation ID instead of + instance information. Default is ``False``. + in: body + required: false + type: boolean +revertResize: + description: | + The action to revert a resize operation. + in: body + required: true + type: none +rules: + description: | + The list of security group rules. + in: body + required: true + type: array +running_vms: + description: | + The number of running vms on this hypervisor. + in: body + required: true + type: integer +running_vms_total: + description: | + The total number of running vms on all hypervisors. + in: body + required: true + type: integer +secgroup_default_rule_id: + description: | + The security group default rule ID. + in: body + required: true + type: string +secgroup_rule_cidr: + description: | + The CIDR for address range. + in: body + required: false + type: string +secgroup_rule_id: + description: | + The security group rule ID. + in: body + required: true + type: string +secgroup_rule_ip_range: + description: | + An IP range object. Includes the security group rule ``cidr``. + in: body + required: true + type: object +secgroup_tenant_id_body: + description: | + The UUID of the tenant that owns this security group. + in: body + required: true + type: string +security_group: + description: | + Specify the ``security_group`` action in the request body. + in: body + required: true + type: string +security_group_default_rule: + description: | + A ``security_group_default_rule`` object. + in: body + required: true + type: object +security_group_default_rules: + description: | + A list of the ``security_group_default_rule`` object. + in: body + required: true + type: array +security_group_id_body: + description: | + The ID of the security group. + in: body + required: true + type: string +security_group_rule: + description: | + A ``security_group_rule`` object. + in: body + required: true + type: object +security_group_rules: + description: | + The number of allowed rules for each security group. + in: body + required: false + type: integer + max_version: 2.35 +security_group_rules_quota: + description: | + The number of allowed rules for each security group. + in: body + required: true + type: integer + max_version: 2.35 +security_group_rules_quota_class: &security_group_rules_quota_class + description: | + The number of allowed rules for each security group. + in: body + required: true + type: integer + max_version: 2.49 +security_group_rules_quota_class_optional: + <<: *security_group_rules_quota_class + required: false +security_group_rules_quota_details: + description: | + The object of detailed security group rules quota, including in_use, + limit and reserved number of security group rules. + in: body + required: true + type: object + max_version: 2.35 +security_groups: + description: | + One or more security groups. Specify the name of the security group in the + ``name`` attribute. If you omit this attribute, the API creates the server + in the ``default`` security group. Requested security groups are not + applied to pre-existing ports. + in: body + required: false + type: array +security_groups_obj: + description: | + One or more security groups objects. + in: body + required: true + type: array +security_groups_quota: + description: | + The number of allowed security groups for each tenant. + in: body + required: true + type: integer + max_version: 2.35 +security_groups_quota_class: &security_groups_quota_class + description: | + The number of allowed security groups for the quota class. + in: body + required: true + type: integer + max_version: 2.49 +security_groups_quota_class_optional: + <<: *security_groups_quota_class + required: false +security_groups_quota_details: + description: | + The object of detailed security groups, including in_use, + limit and reserved number of security groups. + in: body + required: true + type: object + max_version: 2.35 +security_groups_quota_optional: + description: | + The number of allowed security groups for each tenant. + in: body + required: false + type: integer + max_version: 2.35 +server: + description: | + A ``server`` object. + in: body + required: true + type: object +server_description: + type: string + in: body + required: false + min_version: 2.19 + description: | + A free form description of the server. Limited to 255 characters + in length. Before microversion 2.19 this was set to the server + name. +server_description_resp: + description: | + The description of the server. + Before microversion 2.19 this was set to the server name. + in: body + required: true + type: string + min_version: 2.19 +server_group: + description: | + The server group obejct. + in: body + required: true + type: object +server_group_id_body: + description: | + The UUID of the server group. + in: body + required: true + type: string +server_group_members: &server_group_members + description: | + The number of allowed members for each server group. + in: body + required: true + type: integer +server_group_members_quota_class: + <<: *server_group_members + min_version: 2.50 +server_group_members_quota_details: + description: | + The object of detailed server group members, including in_use, + limit and reserved number of server group members. + in: body + required: true + type: object +server_group_members_quota_optional: + description: | + The number of allowed members for each server group. + in: body + required: false + type: integer +server_groups: &server_groups + description: | + The number of allowed server groups for each tenant. + in: body + required: true + type: integer +server_groups_list: + description: | + The list of existing server groups. + in: body + required: true + type: array +server_groups_quota_class: + <<: *server_groups + description: | + The number of allowed server groups for the quota class. + min_version: 2.50 +server_groups_quota_class_optional: + <<: *server_groups + description: | + The number of allowed server groups for the quota class. + required: false +server_groups_quota_details: + description: | + The object of detailed server groups, including in_use, + limit and reserved number of server groups. + in: body + required: true + type: object +server_groups_quota_optional: + description: | + The number of allowed server groups for each tenant. + in: body + required: false + type: integer +server_hostname: + in: body + required: true + type: string + description: | + The hostname set on the instance when it is booted. + min_version: 2.3 +server_id: + description: | + The UUID of the server. + in: body + required: true + type: string +server_id_optional: + description: | + The UUID of the server. + in: body + required: false + type: string +server_kernel_id: + in: body + required: true + type: string + description: | + The UUID of the kernel image when using an AMI. Will be null if not. + min_version: 2.3 +server_launch_index: + in: body + required: true + type: integer + description: | + When servers are launched via multiple create, this is the + sequence in which the servers were launched. + min_version: 2.3 +server_links: + description: | + Links pertaining to the server. See `API Guide / Links and + References + `_ + for more info. + in: body + type: array + required: true +server_name: + description: | + The server name. + in: body + required: true + type: string +server_name_optional: + description: | + The server name. + in: body + required: false + type: string +server_ramdisk_id: + in: body + required: true + type: string + description: | + The UUID of the ramdisk image when using an AMI. Will be null if not. + min_version: 2.3 +server_reservation_id: + in: body + required: true + type: string + description: | + The reservation id for the server. This is an id that can + be useful in tracking groups of servers created with multiple + create, that will all have the same reservation_id. + min_version: 2.3 +server_root_device_name: + in: body + required: true + type: string + description: | + The root device name for the instance + min_version: 2.3 +server_status: + description: | + The server status. + in: body + required: true + type: string +server_tags_create: + description: | + A list of tags. Tags have the following restrictions: + + - Tag is a Unicode bytestring no longer than 60 characters. + + - Tag is a non-empty string. + + - '/' is not allowed to be in a tag name + + - Comma is not allowed to be in a tag name in order to simplify + requests that specify lists of tags + + - All other characters are allowed to be in a tag name + + - Each server can have up to 50 tags. + in: body + required: false + type: array + min_version: 2.52 +server_usages: + description: | + A list of the server usage objects. + in: body + required: true + type: array +server_usages_optional: + description: | + A list of the server usage objects. + in: body + required: false + type: array +server_user_data: + in: body + required: true + type: string + description: | + The user_data the instance was created with. + min_version: 2.3 +server_uuid: + description: | + The UUID of the server instance to which the API dispatches the event. You must + assign this instance to a host. Otherwise, this call does not dispatch the event + to the instance. + in: body + required: true + type: string +servers: + description: | + A list of ``server`` objects. + in: body + required: true + type: array +servers_max_count: + in: body + required: false + type: string + description: | + The max number of servers to be created. Defaults to the value of ``min_count``. +servers_min_count: + in: body + required: false + type: string + description: | + The min number of servers to be created. Defaults to 1. +servers_multiple_create_name: + in: body + required: true + type: string + description: | + A base name for creating unique names during multiple create. A + unique string will be appended to the end of this base name for + every server created. +service: + description: | + Object representing a compute service. + in: body + required: true + type: object +service_disable_reason: + description: | + The disable reason of the service, ``null`` if the service is enabled or + disabled without reason provided. + in: body + required: true + type: string +service_id_body: + description: | + The id of the service. + in: body + required: true + type: integer +service_id_body_2_52: + description: | + The id of the service. + in: body + required: true + type: integer + max_version: 2.52 +service_id_body_2_53: + description: | + The id of the service as a uuid. + in: body + required: true + type: string + min_version: 2.53 +service_id_body_2_53_no_version: + description: | + The id of the service as a uuid. + in: body + required: true + type: string +service_state: + description: | + The state of the service. One of ``up`` or ``down``. + in: body + required: true + type: string +service_status: + description: | + The status of the service. One of ``enabled`` or ``disabled``. + in: body + required: true + type: string +# This is an optional input parameter to PUT /os-services/{service_id} added +# in microversion 2.53. +service_status_2_53_in: + description: | + The status of the service. One of ``enabled`` or ``disabled``. + in: body + required: false + type: string +services: + description: | + A list of service objects. + in: body + required: true + type: array +set_metadata: + description: | + The set_metadata object used to set metadata for host aggregate. + in: body + required: true + type: object +shelve: + description: | + The action. + in: body + required: true + type: none +shelveOffload: + description: | + The action. + in: body + required: true + type: none +size: + description: | + The size of the volume, in gibibytes (GiB). + in: body + required: true + type: integer +snapshot: + description: | + A partial representation of a snapshot that is used to create a snapshot. + in: body + required: true + type: object +snapshot_description: + description: | + The snapshot description. + in: body + required: true + type: string +snapshot_description_optional: + description: | + The snapshot description. + in: body + required: false + type: string +snapshot_id: + description: | + The UUID for a snapshot. + in: body + required: true + type: string +snapshot_id_optional: + description: | + The UUID for a snapshot. + in: body + required: false + type: string +snapshot_id_resp_2_45: + description: | + The UUID for the resulting image snapshot. + in: body + required: true + type: string + min_version: 2.45 +snapshot_name: + description: | + The snapshot name. + in: body + required: true + type: string +snapshot_name_optional: + description: | + The snapshot name. + in: body + required: false + type: string +snapshot_status: + description: | + The status of the snapshot. Valid status values are: + + - ``available`` + - ``creating`` + - ``deleting`` + - ``error`` + - ``error_deleting`` + in: body + required: true + type: string +snapshots: + description: | + A list of snapshot objects. + in: body + required: true + type: array +source_type: + description: | + The source type of the volume. A valid value is ``blank``, ``snapshot``, ``volume``, + or ``image``. + in: body + required: true + type: string +start_simple_tenant_usage_body: + description: | + The beginning time to calculate usage statistics on compute and + storage resources. The date and time stamp format is as follows: + + :: + + CCYY-MM-DDThh:mm:ss.NNNNNN + + For example, ``2015-08-27T09:49:58.123456``. + in: body + required: true + type: string +start_time: + description: | + The date and time when the action was started. The date and time + stamp format is `ISO 8601 `_ + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + For example, ``2015-08-27T09:49:58-05:00``. The ``±hh:mm`` + value, if included, is the time zone as an offset from UTC. In + the previous example, the offset value is ``-05:00``. + in: body + required: true + type: string +started_at: + description: | + The date and time when the server was launched. + + The date and time stamp format is as follows: + + :: + + CCYY-MM-DDThh:mm:ss.NNNNNN + + For example, ``2015-08-27T09:49:58.123456``. + in: body + required: true + type: string +started_at_optional: + description: | + The date and time when the server was launched. + + The date and time stamp format is as follows: + + :: + + CCYY-MM-DDThh:mm:ss.NNNNNN + + For example, ``2015-08-27T09:49:58.123456``. + in: body + required: false + type: string +stop_simple_tenant_usage: + description: | + The ending time to calculate usage statistics on compute and + storage resources. The date and time stamp format is as follows: + + :: + + CCYY-MM-DDThh:mm:ss.NNNNNN + + For example, ``2015-08-27T09:49:58.123456``. + in: body + required: true + type: string +subnet_id: + description: | + The UUID of the subnet. + in: body + required: true + type: string +suspend: + description: | + The action to suspend a server. + in: body + required: true + type: none +tags: + description: | + A list of tags. The maximum count of tags in this list is 50. + in: body + required: true + type: array + min_version: 2.26 +tenant_id_body: + description: | + The UUID of the tenant in a multi-tenancy cloud. + in: body + required: true + type: string +tenant_id_optional: + description: | + The UUID of the tenant in a multi-tenancy cloud. + in: body + required: false + type: string +tenant_usage: + description: | + The tenant usage object. + in: body + required: true + type: object +tenant_usages: + description: | + A list of the tenant usage objects. + in: body + required: true + type: array +to_port: + description: | + The port at end of range. + in: body + required: true + type: integer +total_cores_used: + description: | + The number of used server cores in each tenant. + If ``reserved`` query parameter is specified and it is not 0, + the number of reserved server cores are also included. + in: body + required: true + type: integer +total_errors: + description: | + The total number of instance audit task errors. + in: body + required: true + type: integer +total_floatingips_used: + description: | + The number of used floating IP addresses in each tenant. + If ``reserved`` query parameter is specified and it is not 0, + the number of reserved floating IP addresses are also included. + in: body + required: true + type: integer + max_version: 2.35 +total_hours: + description: | + The total duration that servers exist (in hours). + in: body + required: true + type: float +total_instances: + description: | + The total number of VM instances in the period. + in: body + required: true + type: integer +total_instances_used: + description: | + The number of servers in each tenant. + If ``reserved`` query parameter is specified and it is not 0, + the number of reserved servers are also included. + in: body + required: true + type: integer +total_local_gb_usage: + description: | + Multiplying the server disk size (in GiB) by hours the server exists, + and then adding that all together for each server. + in: body + required: true + type: float +total_memory_mb_usage: + description: | + Multiplying the server memory size (in MB) by hours the server exists, + and then adding that all together for each server. + in: body + required: true + type: float +total_ram_used: + description: | + The amount of used server RAM in each tenant. + If ``reserved`` query parameter is specified and it is not 0, + the amount of reserved server RAM is also included. + in: body + required: true + type: integer +total_security_groups_used: + description: | + The number of used security groups in each tenant. + If ``reserved`` query parameter is specified and it is not 0, + the number of reserved security groups are also included. + in: body + required: true + type: integer + max_version: 2.35 +total_server_groups_used: + description: | + The number of used server groups in each tenant. + If ``reserved`` query parameter is specified and it is not 0, + the number of reserved server groups are also included. + in: body + required: true + type: integer +total_vcpus_usage: + description: | + Multiplying the number of virtual CPUs of the server by hours the server exists, + and then adding that all together for each server. + in: body + required: true + type: float +trigger_crash_dump: + in: body + required: true + type: none + description: | + Specifies the trigger crash dump action should be run +type-os-assisted-volume-snapshot: + description: | + The snapshot type. A valid value is ``qcow2``. + in: body + required: true + type: string +unlock: + description: | + The action to unlock a locked server. + in: body + required: true + type: none +unpause: + description: | + The action to unpause a paused server. + in: body + required: true + type: none +unrescue: + description: | + The action to unrescue a server in rescue mode. + in: body + required: true + type: none +unshelve: + description: | + The action. + in: body + required: true + type: none +updated: + description: | + The date and time when the resource was updated. The date and time + stamp format is `ISO 8601 `_ + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + For example, ``2015-08-27T09:49:58-05:00``. The ``±hh:mm`` + value, if included, is the time zone as an offset from UTC. In + the previous example, the offset value is ``-05:00``. + in: body + required: true + type: string +updated_consider_null: + description: | + The date and time when the resource was updated, if the resource has + not been updated, this field will show as ``null``. The date and time + stamp format is `ISO 8601 `_ + + :: + + CCYY-MM-DDThh:mm:ss±hh:mm + + For example, ``2015-08-27T09:49:58-05:00``. The ``±hh:mm`` + value, if included, is the time zone as an offset from UTC. In + the previous example, the offset value is ``-05:00``. + in: body + required: true + type: string +updated_version: + description: | + This is a fixed string. It is ``2011-01-21T11:33:21Z`` in version 2.0, + ``2013-07-23T11:33:21Z`` in version 2.1. + + .. note:: + It is vestigial and provides no useful information. + It will be deprecated and removed in the future. + in: body + required: true + type: string +uptime: + description: | + The total uptime of the hypervisor and information about average + load. + in: body + required: true + type: string +uptime_diagnostics: + description: | + The amount of time in seconds that the VM has been running. + in: body + required: true + type: integer + min_version: 2.48 +uptime_simple_tenant_usage: + description: | + The uptime of the server. + in: body + required: true + type: integer +uptime_simple_tenant_usage_optional: + description: | + The uptime of the server. + in: body + required: false + type: integer +url: + description: | + The URL associated with the agent. + in: body + required: true + type: string +usage_links: + description: | + Links pertaining to usage. See `API Guide / Links and + References + `_ + for more info. + in: body + type: array + required: false + min_version: 2.40 +user_data: + description: | + Configuration information or scripts to use upon launch. Must be Base64 encoded. + + .. note:: + + The ``null`` value allowed in Nova legacy v2 API, but due to the strict + input validation, it isn't allowed in Nova v2.1 API. + in: body + required: false + type: string +user_id: + description: | + The user ID of the user who owns the server. + in: body + required: true + type: string +user_id_server_group: + description: | + The user ID who owns the server group. + min_version: 2.13 + in: body + required: true + type: string +vcpus: + description: | + The number of virtual CPUs that the server uses. + in: body + required: true + type: integer +vcpus_optional: + description: | + The number of virtual CPUs that the server uses. + in: body + required: false + type: integer +version: + description: | + The version. + in: body + required: true + type: string +version_id: + type: string + in: body + required: true + description: > + A common name for the version in question. Informative only, it + has no real semantic meaning. +version_ip: + description: | + The IP version of the address associated with server. + in: body + required: true + type: integer +version_max: + type: string + in: body + required: true + description: > + If this version of the API supports microversions, the maximum + microversion that is supported. This will be the empty string if + microversions are not supported. +version_min: + type: string + in: body + required: true + description: > + If this version of the API supports microversions, the minimum + microversion that is supported. This will be the empty string if + microversions are not supported. +version_status: + type: string + in: body + required: true + description: | + The status of this API version. This can be one of: + + - ``CURRENT``: this is the preferred version of the API to use + - ``SUPPORTED``: this is an older, but still supported version of the API + - ``DEPRECATED``: a deprecated version of the API that is slated for removal +versions: + type: array + in: body + required: true + description: > + A list of version objects that describe the API versions available. +virtual_interface: + description: | + Virtual interface for the floating ip address. + in: body + required: true + type: string +virtual_interface_id: + description: | + The UUID of the virtual interface. + in: body + required: true + type: string +virtual_interface_id_optional: + description: | + Virtual interface for the floating ip address + in: body + required: false + type: string +virtual_interfaces: + description: | + An array of virtual interfaces. + in: body + required: true + type: array +vm_state_diagnostics: + description: | + A string enum denoting the current state of the VM. Possible values are: + + - ``pending`` + - ``running`` + - ``paused`` + - ``shutdown`` + - ``crashed`` + - ``suspended`` + in: body + required: true + type: string + min_version: 2.48 +vm_state_optional: + description: | + The VM state. + in: body + required: false + type: string +volume: + description: | + The ``volume`` object. + in: body + required: true + type: object +volume_id: + description: | + The source volume ID. + in: body + required: true + type: string +volume_id_resp: + description: | + The UUID of the volume. + in: body + required: true + type: string +volume_status: + description: | + The status of the volume. + in: body + required: true + type: string +volume_type: + description: | + The name or unique identifier for a volume type. + in: body + required: true + type: string +volume_type_optional: + description: | + The unique identifier for a volume type. + in: body + required: false + type: string +# This is the volumeAttachment in a response body. +volumeAttachment: + description: | + A dictionary representation of a volume attachment containing the fields + ``device``, ``id``, ``serverId`` and ``volumeId``. + in: body + required: true + type: object +# This is the volumeAttachment in a POST (attach volume) request body. +volumeAttachment_post: + description: | + A dictionary representation of a volume attachment containing the fields + ``device`` and ``volumeId``. + in: body + required: true + type: object +# This is the volumeAttachment in a PUT (swap volume) request body. +volumeAttachment_put: + description: | + A dictionary representation of a volume attachment containing the field + ``volumeId``. + in: body + required: true + type: object +volumeAttachments: + description: | + The list of volume attachments. + in: body + required: true + type: array +volumeId: + description: | + The UUID of the volume to attach. + in: body + required: true + type: string +volumeId_resp: + description: | + The UUID of the attached volume. + in: body + required: true + type: string +volumeId_swap: + description: | + The UUID of the volume to attach instead of the attached volume. + in: body + required: true + type: string +volumes: + description: | + The list of ``volume`` objects. + in: body + required: true + type: array +vpn_public_ip: + description: | + The VPN IP address. + in: body + required: true + type: string +vpn_public_ip_resp: + description: | + The VPN public IP address. + in: body + required: false + type: string +vpn_public_port: + description: | + The VPN port. + in: body + required: true + type: string +vpn_public_port_resp: + description: | + The VPN public port. + in: body + required: false + type: string +vpn_state: + description: | + The VPN state. + in: body + required: false + type: string diff --git a/api-ref/source/request-ids.inc b/api-ref/source/request-ids.inc new file mode 100644 index 00000000000..76c0efafedb --- /dev/null +++ b/api-ref/source/request-ids.inc @@ -0,0 +1,24 @@ +.. -*- rst -*- + +=========== +Request IDs +=========== + +Users can specify the global request ID in the request header. +Users can receive the local request ID in the response header. + +For more details about Request IDs, please reference: `Faults +`_ + +**Request** + +.. rest_parameters:: parameters.yaml + + - X-Openstack-Request-Id: x-openstack-request-id_req + +**Response** + +.. rest_parameters:: parameters.yaml + + - X-Compute-Request-Id: x-compute-request-id_resp + - X-Openstack-Request-Id: x-openstack-request-id_resp diff --git a/api-ref/source/server-migrations.inc b/api-ref/source/server-migrations.inc new file mode 100644 index 00000000000..476bf7687a1 --- /dev/null +++ b/api-ref/source/server-migrations.inc @@ -0,0 +1,231 @@ +.. -*- rst -*- + +========================================= + Server migrations (servers, migrations) +========================================= + +List, show, perform actions on and delete server migrations. + +List Migrations +=============== + +.. rest_method:: GET /servers/{server_id}/migrations + +Lists in-progress live migrations for a given server. + +.. note:: Microversion 2.23 or greater is required for this API. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through the +``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - migrations: migrations + - created_at: created + - dest_compute: migrate_dest_compute + - dest_host: migrate_dest_host + - dest_node: migrate_dest_node + - disk_processed_bytes: migrate_disk_processed_bytes + - disk_remaining_bytes: migrate_disk_remaining_bytes + - disk_total_bytes: migrate_disk_total_bytes + - id: migration_id + - memory_processed_bytes: migrate_memory_processed_bytes + - memory_remaining_bytes: migrate_memory_remaining_bytes + - memory_total_bytes: migrate_memory_total_bytes + - server_uuid: server_id + - source_compute: migrate_source_compute + - source_node: migrate_source_node + - status: migrate_status + - updated_at: updated + +**Example List Migrations** + +.. literalinclude:: ../../doc/api_samples/server-migrations/v2.23/migrations-index.json + :language: javascript + +Show Migration Details +====================== + +.. rest_method:: GET /servers/{server_id}/migrations/{migration_id} + +Show details for an in-progress live migration for a given server. + +.. note:: Microversion 2.23 or greater is required for this API. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through the +``policy.json`` file. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - migration_id: migration_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - migration: migration + - created_at: created + - dest_compute: migrate_dest_compute + - dest_host: migrate_dest_host + - dest_node: migrate_dest_node + - disk_processed_bytes: migrate_disk_processed_bytes + - disk_remaining_bytes: migrate_disk_remaining_bytes + - disk_total_bytes: migrate_disk_total_bytes + - id: migration_id + - memory_processed_bytes: migrate_memory_processed_bytes + - memory_remaining_bytes: migrate_memory_remaining_bytes + - memory_total_bytes: migrate_memory_total_bytes + - server_uuid: server_id + - source_compute: migrate_source_compute + - source_node: migrate_source_node + - status: migrate_status + - updated_at: updated + +**Example Show Migration Details** + +.. literalinclude:: ../../doc/api_samples/server-migrations/v2.23/migrations-get.json + :language: javascript + +Force Migration Complete Action (force_complete Action) +======================================================= + +.. rest_method:: POST /servers/{server_id}/migrations/{migration_id}/action + +Force an in-progress live migration for a given server to complete. + +Specify the ``force_complete`` action in the request body. + +.. note:: Microversion 2.22 or greater is required for this API. + +.. note:: Not all compute back ends support forcefully completing an + in-progress live migration. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through the +``policy.json`` file. + +**Preconditions** + +The server OS-EXT-STS:vm_state value must be ``active`` and the server +OS-EXT-STS:task_state value must be ``migrating``. + +If the server is locked, you must have administrator privileges to force the +completion of the server migration. + +The migration status must be ``running``. + +**Asynchronous Postconditions** + +After you make this request, you typically must keep polling the server status +to determine whether the request succeeded. + +**Troubleshooting** + +If the server status remains ``ACTIVE`` for an inordinate amount of time, the +request may have failed. Ensure you meet the preconditions and run the request +again. If the request fails again, investigate the compute back end. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - migration_id: migration_id_path + - force_complete: force_migration_complete + +**Example Force Migration Complete (force_complete Action)** + +.. literalinclude:: ../../doc/api_samples/server-migrations/force_complete.json + :language: javascript + +Response +-------- + +There is no body content for the response of a successful POST operation. + +Delete (Abort) Migration +======================== + +.. rest_method:: DELETE /servers/{server_id}/migrations/{migration_id} + +Abort an in-progress live migration. + +.. note:: Microversion 2.24 or greater is required for this API. + +.. note:: Not all compute back ends support aborting an in-progress live + migration. + +Policy defaults enable only users with the administrative role to perform +this operation. Cloud providers can change these permissions through the +``policy.json`` file. + +**Preconditions** + +The server OS-EXT-STS:task_state value must be ``migrating``. + +If the server is locked, you must have administrator privileges to force the +completion of the server migration. + +The migration status must be ``running``. + +**Asynchronous Postconditions** + +After you make this request, you typically must keep polling the server status +to determine whether the request succeeded. You may also monitor the migration +using:: + + GET /servers/{server_id}/migrations/{migration_id} + +**Troubleshooting** + +If the server task_state remains ``migrating`` for an inordinate amount of +time, the request may have failed. Ensure you meet the preconditions and run +the request again. If the request fails again, investigate the compute back +end. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - migration_id: migration_id_path + +Response +-------- + +There is no body content for the response of a successful DELETE operation. diff --git a/api-ref/source/server-security-groups.inc b/api-ref/source/server-security-groups.inc new file mode 100644 index 00000000000..1d0c22403cb --- /dev/null +++ b/api-ref/source/server-security-groups.inc @@ -0,0 +1,43 @@ +.. -*- rst -*- + +====================================================== + Servers Security Groups (servers, os-security-groups) +====================================================== + +Lists Security Groups for a server. + +List Security Groups By Server +============================== + +.. rest_method:: GET /servers/{server_id}/os-security-groups + +Lists security groups for a server. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - security_groups: security_groups_obj + - description: description + - id: security_group_id_body + - name: name + - rules: rules + - tenant_id: tenant_id_body + + +**Example List security groups by server** + +.. literalinclude:: ../../doc/api_samples/os-security-groups/server-security-groups-list-resp.json + :language: javascript diff --git a/api-ref/source/servers-action-console-output.inc b/api-ref/source/servers-action-console-output.inc new file mode 100644 index 00000000000..cb96edb34f1 --- /dev/null +++ b/api-ref/source/servers-action-console-output.inc @@ -0,0 +1,50 @@ +.. -*- rst -*- + +Show Console Output (os-getConsoleOutput Action) +================================================ + +.. rest_method:: POST /servers/{server_id}/action + +Shows console output for a server. + +This API returns the text of the console since boot. +The content returned may be large. Limit the lines of console +text, beginning at the tail of the content, by setting +the optional ``length`` parameter in the request body. + +The server to get console log from should set +``export LC_ALL=en_US.UTF-8`` in order to avoid incorrect unicode error. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), +notFound(404), conflict(409), methodNotImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - os-getConsoleOutput: os-getConsoleOutput + - length: length + +**Example Show Console Output (os-getConsoleOutput Action)** + +This example requests the last 50 lines of console content +from the specified server. + +.. literalinclude:: ../../doc/api_samples/os-console-output/console-output-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - output: console_output + +**Example Show Console Output (os-getConsoleOutput Action)** + +.. literalinclude:: ../../doc/api_samples/os-console-output/console-output-post-resp.json + :language: javascript diff --git a/api-ref/source/servers-action-crash-dump.inc b/api-ref/source/servers-action-crash-dump.inc new file mode 100644 index 00000000000..80694066da2 --- /dev/null +++ b/api-ref/source/servers-action-crash-dump.inc @@ -0,0 +1,51 @@ +.. -*- rst -*- + +Trigger Crash Dump In Server +============================ + +.. rest_method:: POST /servers/{server_id}/action + +.. versionadded:: 2.17 + +Trigger a crash dump in a server. + +When a server starts behaving oddly at a fundamental level, it maybe +be useful to get a kernel level crash dump to debug further. The crash +dump action forces a crash dump followed by a system reboot of the +server. Once the server comes back online, you can find a Kernel Crash +Dump file in a certain location of the filesystem. For example, for +Ubuntu you can find it in the ``/var/crash`` directory. + +.. warning:: + + This action can cause data loss. Also, network connectivity can be + lost both during and after this operation. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), +forbidden(403), itemNotFound(404), conflict(409) + +* 400 is returned if the server does not support a crash dump (either + by configuration or because the backend does not support it) +* 409 is returned if the server is not in a state where a crash dump + action is allowed. + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - trigger_crash_dump: trigger_crash_dump + +**Example Trigger crash dump: JSON request** + +.. literalinclude:: ../../doc/api_samples/servers/v2.17/server-action-trigger-crash-dump.json + :language: javascript + +Response +-------- + +No body is returned on a successful submission. diff --git a/api-ref/source/servers-action-deferred-delete.inc b/api-ref/source/servers-action-deferred-delete.inc new file mode 100644 index 00000000000..dd454551636 --- /dev/null +++ b/api-ref/source/servers-action-deferred-delete.inc @@ -0,0 +1,76 @@ +.. -*- rst -*- + +Force-Delete Server (forceDelete Action) +======================================== + +.. rest_method:: POST /servers/{server_id}/action + +Force-deletes a server before deferred cleanup. + +Specify the ``forceDelete`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - forceDelete: forceDelete + +**Example Force-Delete Server (forceDelete Action): JSON request** + +.. literalinclude:: ../../doc/api_samples/os-deferred-delete/force-delete-post-req.json + :language: javascript + +Response +-------- + +No body is returned on a successful submission. + +Restore Soft-Deleted Instance (restore Action) +============================================== + +.. rest_method:: POST /servers/{server_id}/action + +Restores a previously soft-deleted server instance. You cannot use +this method to restore deleted instances. + +Specify the ``restore`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - restore: restore + +**Example Restore Soft-Deleted Instance (restore Action): JSON request** + +.. literalinclude:: ../../doc/api_samples/os-deferred-delete/restore-post-req.json + :language: javascript + +Response +-------- + +No body is returned on a successful submission. diff --git a/api-ref/source/servers-action-evacuate.inc b/api-ref/source/servers-action-evacuate.inc new file mode 100644 index 00000000000..35fe67eccbb --- /dev/null +++ b/api-ref/source/servers-action-evacuate.inc @@ -0,0 +1,52 @@ +.. -*- rst -*- + +Evacuate Server (evacuate Action) +================================= + +.. rest_method:: POST /servers/{server_id}/action + +Evacuates a server from a failed host to a new host. + +- Specify the ``evacuate`` action in the request body. +- In the request body, if ``onSharedStorage`` is set, then do not set ``adminPass``. +- The target host should not be the same as the instance host. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - evacuate: evacuate + - host: host + - adminPass: adminPass_evacuate_request + - onSharedStorage: on_shared_storage + - force: force_evacuate + +| + +**Example Evacuate Server (evacuate Action)** + +.. literalinclude:: ../../doc/api_samples/os-evacuate/server-evacuate-req.json + :language: javascript + + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - adminPass: adminPass_evacuate + +.. note:: API does not return any Response for Microversion 2.14 or greater. + + +**Example Evacuate Server (evacuate Action)** + +.. literalinclude:: ../../doc/api_samples/os-evacuate/server-evacuate-resp.json + :language: javascript diff --git a/api-ref/source/servers-action-fixed-ip.inc b/api-ref/source/servers-action-fixed-ip.inc new file mode 100644 index 00000000000..71f06490f50 --- /dev/null +++ b/api-ref/source/servers-action-fixed-ip.inc @@ -0,0 +1,87 @@ +.. -*- rst -*- + +Add (Associate) Fixed Ip (addFixedIp Action) (DEPRECATED) +========================================================== + +.. warning:: This API is deprecated and will fail with a 404 starting + from microversion 2.44. This is replaced with using the + Neutron networking service API. + +.. rest_method:: POST /servers/{server_id}/action + +Adds a fixed IP address to a server instance, which associates that +address with the server. The fixed IP address is retrieved from the +network that you specify in the request. + +Specify the ``addFixedIp`` action and the network ID in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - addFixedIp: addFixedIp + - networkId: net_id_resp + +**Example Add (Associate) Fixed Ip (addFixedIp Action)** + +.. literalinclude:: ../../doc/api_samples/os-multinic/multinic-add-fixed-ip-req.json + :language: javascript + +Response +-------- + +No response body is returned after a successful addFixedIp action. + + +Remove (Disassociate) Fixed Ip (removeFixedIp Action) (DEPRECATED) +=================================================================== + +.. warning:: This API is deprecated and will fail with a 404 starting + from microversion 2.44. This is replaced with using the + Neutron networking service API. + +.. rest_method:: POST /servers/{server_id}/action + +Removes, or disassociates, a fixed IP address from a server. + +Specify the ``removeFixedIp`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - removeFixedIp: removeFixedIp + - address: ip_address + +**Example Remove (Disassociate) Fixed Ip (removeFixedIp Action)** + +.. literalinclude:: ../../doc/api_samples/os-multinic/multinic-remove-fixed-ip-req.json + :language: javascript + +Response +-------- + +No response body is returned after a successful removeFixedIp action. diff --git a/api-ref/source/servers-action-remote-consoles.inc b/api-ref/source/servers-action-remote-consoles.inc new file mode 100644 index 00000000000..105a00dd10a --- /dev/null +++ b/api-ref/source/servers-action-remote-consoles.inc @@ -0,0 +1,213 @@ +.. -*- rst -*- + +Get RDP Console (os-getRDPConsole Action) (DEPRECATED) +====================================================== + +.. rest_method:: POST /servers/{server_id}/action + max_version: 2.5 + +Gets an `RDP `__ console for a server. + +.. warning:: + + This action is deprecated in microversion 2.5 and superseded + by the API `Server Remote Consoles`_ in microversion 2.6. + The new API offers a unified API for different console types. + +The only supported connect type is ``rdp-html5``. The ``type`` parameter should +be set as ``rdp-html5``. + +Specify the ``os-getRDPConsole`` action in the request body. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - os-getRDPConsole: os-getRDPConsole + - type: os-getRDPConsole-type + +**Example Get RDP Console (os-getRDPConsole Action)** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/get-rdp-console-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - console: remote_console + - type: os-getRDPConsole-type + - url: os-getRDPConsole-url + +**Example Get RDP Console (os-getRDPConsole Action)** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/get-rdp-console-post-resp.json + :language: javascript + +Get Serial Console (os-getSerialConsole Action) (DEPRECATED) +============================================================ + +.. rest_method:: POST /servers/{server_id}/action + max_version: 2.5 + +Gets a serial console for a server. + +.. warning:: + + This action is deprecated in microversion 2.5 and superseded + by the API `Server Remote Consoles`_ in microversion 2.6. + The new API offers a unified API for different console types. + +Specify the ``os-getSerialConsole`` action in the request body. + +The only supported connection type is ``serial``. The ``type`` parameter +should be set as ``serial``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - os-getSerialConsole: os-getSerialConsole + - type: os-getSerialConsole-type + +**Example Get Serial Console (os-getSerialConsole Action)** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/get-serial-console-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - console: remote_console + - type: os-getSerialConsole-type + - url: os-getSerialConsole-url + +**Example Get Serial Console (os-getSerialConsole Action)** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/get-serial-console-post-resp.json + :language: javascript + +Get SPICE Console (os-getSPICEConsole Action) (DEPRECATED) +========================================================== + +.. rest_method:: POST /servers/{server_id}/action + max_version: 2.5 + +Gets a SPICE console for a server. + +.. warning:: + + This action is deprecated in microversion 2.5 and superseded + by the API `Server Remote Consoles`_ in microversion 2.6. + The new API offers a unified API for different console types. + +Specify the ``os-getSPICEConsole`` action in the request body. + +The only supported connection type is ``spice-html5``. The ``type`` parameter +should be set to ``spice-html5``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - os-getSPICEConsole: os-getSPICEConsole + - type: os-getSPICEConsole-type + +**Example Get Spice Console (os-getSPICEConsole Action)** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/get-spice-console-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - console: remote_console + - type: os-getSPICEConsole-type + - url: os-getSPICEConsole-url + +**Example Get SPICE Console (os-getSPICEConsole Action)** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/get-spice-console-post-resp.json + :language: javascript + +Get VNC Console (os-getVNCConsole Action) (DEPRECATED) +====================================================== + +.. rest_method:: POST /servers/{server_id}/action + max_version: 2.5 + +Gets a VNC console for a server. + +.. warning:: + + This action is deprecated in microversion 2.5 and superseded + by the API `Server Remote Consoles`_ in microversion 2.6. + The new API offers a unified API for different console types. + +Specify the ``os-getVNCConsole`` action in the request body. + +The supported connection types are ``novnc``, ``xvpvnc``. Such as connect +with ``novnc``, set ``type`` parameter to ``novnc``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - os-getVNCConsole: os-getVNCConsole + - type: os-getVNCConsole-type + +**Example Get Vnc Console (os-getVNCConsole Action)** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/get-vnc-console-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - console: remote_console + - type: os-getVNCConsole-type + - url: os-getVNCConsole-url + +**Example Get VNC Console (os-getVNCConsole Action)** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/get-vnc-console-post-resp.json + :language: javascript diff --git a/api-ref/source/servers-action-shelve.inc b/api-ref/source/servers-action-shelve.inc new file mode 100644 index 00000000000..b024031cdfc --- /dev/null +++ b/api-ref/source/servers-action-shelve.inc @@ -0,0 +1,162 @@ +.. -*- rst -*- + +Shelve Server (shelve Action) +============================= + +.. rest_method:: POST /servers/{server_id}/action + +Shelves a server. + +Specify the ``shelve`` action in the request body. + +All associated data and resources are kept but anything still in memory is not retained. To restore a shelved instance, use the ``unshelve`` action. To remove a shelved instance, use the ``shelveOffload`` action. + +Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. + +**Preconditions** + +The server status must be ``ACTIVE``, ``SHUTOFF``, ``PAUSED``, or ``SUSPENDED``. + +If the server is locked, you must have administrator privileges to shelve the server. + +**Asynchronous Postconditions** + +After you successfully shelve a server, its status changes to ``SHELVED`` and the image status is ``ACTIVE``. The server instance data appears on the compute node that the Compute service manages. + +If you boot the server from volumes or set the ``shelved_offload_time`` option to 0, the Compute service automatically deletes the instance on compute nodes and changes the server status to ``SHELVED_OFFLOADED``. + +**Troubleshooting** + +If the server status does not change to ``SHELVED`` or ``SHELVED_OFFLOADED``, the shelve operation failed. Ensure that you meet the preconditions and run the request again. If the request fails again, investigate whether another operation is running that causes a race condition. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - shelve: shelve + +| + +**Example Shelve server (shelve Action)** + +.. literalinclude:: ../../doc/api_samples/os-shelve/os-shelve.json + :language: javascript + + +Response +-------- + +If successful, this method does not return content in the response body. + + +Shelf-Offload (Remove) Server (shelveOffload Action) +==================================================== + +.. rest_method:: POST /servers/{server_id}/action + +Shelf-offloads, or removes, a shelved server. + +Specify the ``shelveOffload`` action in the request body. + +Data and resource associations are deleted. If an instance is no longer needed, you can remove that instance from the hypervisor to minimize resource usage. + +Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. + +**Preconditions** + +The server status must be ``SHELVED``. + +If the server is locked, you must have administrator privileges to shelve-offload the server. + +**Asynchronous Postconditions** + +After you successfully shelve-offload a server, its status changes to ``SHELVED_OFFLOADED``. The server instance data appears on the compute node. + +**Troubleshooting** + +If the server status does not change to ``SHELVED_OFFLOADED``, the shelve-offload operation failed. Ensure that you meet the preconditions and run the request again. If the request fails again, investigate whether another operation is running that causes a race condition. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - shelveOffload: shelveOffload + +| + +**Example Shelf-Offload server (shelveOffload Action)** + +.. literalinclude:: ../../doc/api_samples/os-shelve/os-shelve-offload.json + :language: javascript + + +Response +-------- + +If successful, this method does not return content in the response body. + + +Unshelve (Restore) Shelved Server (unshelve Action) +=================================================== + +.. rest_method:: POST /servers/{server_id}/action + +Unshelves, or restores, a shelved server. + +Specify the ``unshelve`` action in the request body. + +Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. + +**Preconditions** + +The server status must be ``SHELVED`` or ``SHELVED_OFFLOADED``. + +If the server is locked, you must have administrator privileges to unshelve the server. + +**Asynchronous Postconditions** + +After you successfully unshelve a server, its status changes to ``ACTIVE``. +The server appears on the compute node. + +The shelved image is deleted from the list of images returned by an API call. + +**Troubleshooting** + +If the server status does not change to ``ACTIVE``, the unshelve operation failed. Ensure that you meet the preconditions and run the request again. If the request fails again, investigate whether another operation is running that causes a race condition. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - unshelve: unshelve + +| + +**Example Unshelve server (unshelve Action)** + +.. literalinclude:: ../../doc/api_samples/os-shelve/os-unshelve.json + :language: javascript + + +Response +-------- + +If successful, this method does not return content in the response body. diff --git a/api-ref/source/servers-actions.inc b/api-ref/source/servers-actions.inc new file mode 100644 index 00000000000..19657043da7 --- /dev/null +++ b/api-ref/source/servers-actions.inc @@ -0,0 +1,1060 @@ +.. -*- rst -*- +.. needs:body_verification + +=========================================== + Servers - run an action (servers, action) +=========================================== + +Enables all users to perform an action on a server. Specify the action +in the request body. + +You can associate a fixed or floating IP address with a server, +or disassociate a fixed or floating IP address from a server. +You can attach a volume to a server. + +You can create an image from a server, create a backup of a server, +evacuate a server from a failed host to a new host, and force-delete a +server before deferred cleanup. +You can lock, pause, reboot, rebuild, rescue, resize, resume, confirm +the resize of, revert a pending resize for, shelve, shelf-offload, +unshelve, start, stop, unlock, unpause, and unrescue a server. You can +also change the password of the server and add a security group to or +remove a security group from a server. You can also trigger a crash dump +into a server since Mitaka release. + +You can get an RDP, serial, SPICE, or VNC console for a server. + +Add (Associate) Floating Ip (addFloatingIp Action) (DEPRECATED) +================================================================ + +.. warning:: This API is deprecated and will fail with a 404 starting + from microversion 2.44. This is replaced with using the + Neutron networking service API. + +.. rest_method:: POST /servers/{server_id}/action + +Adds a floating IP address to a server, which associates +that address with the server. + +A pool of floating IP addresses, configured by the cloud administrator, +is available in OpenStack Compute. The project quota defines the maximum +number of floating IP addresses that you can allocate to the project. +After you `create (allocate) a floating IPaddress +`__ +for a project, you can associate that address with the server. Specify +the ``addFloatingIp`` action in the request body. + +If an instance is connected to multiple networks, you can associate a +floating IP address with a specific fixed IP address by using the +optional ``fixed_address`` parameter. + +**Preconditions** + +The server must exist. + +You can only add a floating IP address to the server when its status is ``ACTIVE`` or ``STOPPED`` + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - addFloatingIp: addFloatingIp + - address: address + - fixed_address: fixed_address + +**Example Add (Associate) Floating Ip (addFloatingIp Action)** + +.. literalinclude:: ../../doc/api_samples/servers/server-action-addfloatingip-req.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Add Security Group To A Server (addSecurityGroup Action) +======================================================== + +.. rest_method:: POST /servers/{server_id}/action + +Adds a security group to a server. + +Specify the ``addSecurityGroup`` action in the request body. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - addSecurityGroup: addSecurityGroup + - name: name + +**Example Add Security Group To A Server (addSecurityGroup Action)** + +.. literalinclude:: ../../doc/api_samples/os-security-groups/security-group-add-post-req.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Change Administrative Password (changePassword Action) +====================================================== + +.. rest_method:: POST /servers/{server_id}/action + +Changes the administrative password for a server. + +Specify the ``changePassword`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers can +change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - changePassword: changePassword + - adminPass: adminPass_change_password + +**Example Change Administrative Password (changePassword Action)** + +.. literalinclude:: ../../doc/api_samples/os-admin-password/admin-password-change-password.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Confirm Resized Server (confirmResize Action) +============================================= + +.. rest_method:: POST /servers/{server_id}/action + +Confirms a pending resize action for a server. + +Specify the ``confirmResize`` action in the request body. + +After you make this request, you typically must keep polling the server +status to determine whether the request succeeded. A successfully +confirming resize operation shows a status of ``ACTIVE`` or ``SHUTOFF`` +and a migration_status of ``confirmed``. You can also see the resized +server in the compute node that OpenStack Compute manages. + +**Preconditions** + +You can only confirm the resized server where the status is +``VERIFY_RESIZED``, the vm_status is ``RESIZED``, and the +migration_status is ``finished`` or ``confirming``. + +If the server is locked, you must have administrator privileges +to confirm the server. + +**Troubleshooting** + +If the server status remains ``RESIZED``, the request failed. Ensure you +meet the preconditions and run the request again. If the request fails +again, investigate the compute back end or ask your cloud provider. + +Normal response codes: 204 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - confirmResize: confirmResize + +**Example Confirm Resized Server (confirmResize Action)** + +.. literalinclude:: ../../doc/api_samples/servers/server-action-confirm-resize.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Create Server Back Up (createBackup Action) +=========================================== + +.. rest_method:: POST /servers/{server_id}/action + +Creates a back up of a server. + +.. note:: This API is not supported for volume-backed instances. + +Specify the ``createBackup`` action in the request body. + +Policy defaults enable only users with the administrative role or the +owner of the server to perform this operation. Cloud providers can +change these permissions through the ``policy.json`` file. + +.. note:: + Starting from version 2.39 the image quota enforcement with Nova `metadata` + is removed and quota checks should be performed using Glance API directly. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - createBackup: createBackup + - name: backup_name + - backup_type: backup_type + - rotation: backup_rotation + - metadata: metadata + +**Example Create Server Back Up (createBackup Action)** + +.. literalinclude:: ../../doc/api_samples/os-create-backup/create-backup-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - Location: image_location + - image_id: snapshot_id_resp_2_45 + +**Example Create Server Back Up (v2.45)** + +.. literalinclude:: ../../doc/api_samples/os-create-backup/v2.45/create-backup-resp.json + :language: javascript + + +Create Image (createImage Action) +================================= + +.. rest_method:: POST /servers/{server_id}/action + +Creates an image from a server. + +Specify the ``createImage`` action in the request body. + +After you make this request, you typically must keep polling the status of +the created image to determine whether the request succeeded. + +If the operation succeeds, the created image has a status of ``active`` and +the server status returns to the original status. You can also see the new +image in the image back end that OpenStack Image service manages. + +.. note:: + Starting from version 2.39 the image quota enforcement with Nova `metadata` + is removed and quota checks should be performed using Glance API directly. + +**Preconditions** + +The server must exist. + +You can only create a new image from the server when its status is ``ACTIVE``, +``SHUTOFF``, ``PAUSED``, or ``SUSPENDED``. + +The connection to the Image service is valid. + +**Troubleshooting** + +If the image status remains uploading or shows another error status, +the request failed. Ensure you meet the preconditions and run the request +again. If the request fails again, investigate the image back end. + +If the server status does not go back to an original server's status, +the request failed. Ensure you meet the preconditions, or check if +there is another operation that causes race conditions for the server, +then run the request again. If the request fails again, investigate the +compute back end or ask your cloud provider. + +If the request fails due to an error on OpenStack Compute service, the image +is purged from the image store that OpenStack Image service manages. Ensure +you meet the preconditions and run the request again. If the request fails +again, investigate OpenStack Compute service or ask your cloud provider. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - createImage: createImage + - name: image_name + - metadata: image_metadata + +**Example Create Image (createImage Action)** + +.. literalinclude:: ../../doc/api_samples/servers/server-action-create-image.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - Location: image_location + - image_id: snapshot_id_resp_2_45 + +**Example Create Image (v2.45)** + +.. literalinclude:: ../../doc/api_samples/servers/v2.45/server-action-create-image-resp.json + :language: javascript + + +Lock Server (lock Action) +========================= + +.. rest_method:: POST /servers/{server_id}/action + +Locks a server. + +Specify the ``lock`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - lock: lock + +**Example Lock Server (lock Action)** + +.. literalinclude:: ../../doc/api_samples/os-lock-server/lock-server.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Pause Server (pause Action) +=========================== + +.. rest_method:: POST /servers/{server_id}/action + +Pauses a server. Changes its status to ``PAUSED``. + +Specify the ``pause`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - pause: pause + +**Example Pause Server (pause Action)** + +.. literalinclude:: ../../doc/api_samples/os-pause-server/pause-server.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Reboot Server (reboot Action) +============================= + +.. rest_method:: POST /servers/{server_id}/action + +Reboots a server. + +Specify the ``reboot`` action in the request body. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - reboot: reboot + - type: reboot_type + +**Example Reboot Server (reboot Action)** + +.. literalinclude:: ../../doc/api_samples/servers/server-action-reboot.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Rebuild Server (rebuild Action) +=============================== + +.. rest_method:: POST /servers/{server_id}/action + +Rebuilds a server. + +Specify the ``rebuild`` action in the request body. + +To rebuild the server with preservation of the ephemeral partition, +set the ``preserve_ephemeral`` parameter to ``true``. + +Normal response codes: 202 + +Error response codes: badRequest(400),unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - rebuild: rebuild + - imageRef: imageRef_rebuild + - accessIPv4: accessIPv4_in + - accessIPv6: accessIPv6_in + - adminPass: adminPass_request + - metadata: metadata + - name: server_name_optional + - OS-DCF:diskConfig: OS-DCF:diskConfig + - personality: personality + - personality.path: path + - personality.contents: contents + - preserve_ephemeral: preserve_ephemeral + - description: server_description + +**Example Rebuild Server (rebuild Action) (v2.47)** + +.. literalinclude:: ../../doc/api_samples/servers/v2.47/server-action-rebuild.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - Location: server_location + - server: server + - accessIPv4: accessIPv4 + - accessIPv6: accessIPv6 + - addresses: addresses_obj + - adminPass: adminPass_response + - created: created + - flavor: flavor_server + - flavor.id: flavor_id_body_2_46 + - flavor.links: flavor_links_2_46 + - flavor.vcpus: flavor_cpus_2_47 + - flavor.ram: flavor_ram_2_47 + - flavor.disk: flavor_disk_2_47 + - flavor.ephemeral: flavor_ephem_disk_2_47 + - flavor.swap: flavor_swap_2_47 + - flavor.original_name: flavor_original_name + - flavor.extra_specs: extra_specs_2_47 + - flavor.extra_specs.key: flavor_extra_spec_key_2_47 + - flavor.extra_specs.value: flavor_extra_spec_value_2_47 + - hostId: hostId + - id: server_id + - image: image + - image.id: image_id_body + - image.links: links + - links: server_links + - metadata: metadata_object + - name: server_name + - OS-DCF:diskConfig: disk_config + - progress: progress + - status: server_status + - tenant_id: tenant_id_body + - updated: updated + - user_id: user_id + - locked: locked + - description: server_description_resp + - tags: tags + +**Example Rebuild Server (rebuild Action) (v2.47)** + +.. literalinclude:: ../../doc/api_samples/servers/v2.47/server-action-rebuild-resp.json + :language: javascript + +Remove (Disassociate) Floating Ip (removeFloatingIp Action) (DEPRECATED) +========================================================================= + +.. warning:: This API is deprecated and will fail with a 404 starting + from microversion 2.44. This is replaced with using the + Neutron networking service API. + +.. rest_method:: POST /servers/{server_id}/action + +Removes, or disassociates, a floating IP address from a server. + +The IP address is returned to the pool of IP addresses that is available +for all projects. When you remove a floating IP address and that IP address +is still associated with a running instance, it is automatically +disassociated from that instance. + +Specify the ``removeFloatingIp`` action in the request body. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - removeFloatingIp: removeFloatingIp + - address: address + +**Example Remove (Disassociate) Floating Ip (removeFloatingIp Action)** + +.. literalinclude:: ../../doc/api_samples/servers/server-action-removefloatingip-req.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Remove Security Group From A Server (removeSecurityGroup Action) +================================================================ + +.. rest_method:: POST /servers/{server_id}/action + +Removes a security group from a server. + +Specify the ``removeSecurityGroup`` action in the request body. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - removeSecurityGroup: removeSecurityGroup + - name: name + +**Example Remove Security Group From A Server (removeSecurityGroup Action)** + +.. literalinclude:: ../../doc/api_samples/os-security-groups/security-group-remove-post-req.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Rescue Server (rescue Action) +============================= + +.. rest_method:: POST /servers/{server_id}/action + +Puts a server in rescue mode and changes its status to ``RESCUE``. + +Specify the ``rescue`` action in the request body. + +If you specify the ``rescue_image_ref`` extended attribute, +the image is used to rescue the instance. If you omit an image +reference, the base image reference is used by default. + +**Asynchronous Postconditions** + +After you successfully rescue a server and make a ``GET +/servers/​{server_id}​`` request, its status changes to ``RESCUE``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - rescue: rescue + - adminPass: adminPass_rescue_request + - rescue_image_ref: rescue_image_ref + +**Example Rescue server (rescue Action)** + +.. literalinclude:: ../../doc/api_samples/os-rescue/server-rescue-req-with-image-ref.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - adminPass: adminPass_response + +**Example Rescue server (rescue Action)** + +.. literalinclude:: ../../doc/api_samples/os-rescue/server-rescue.json + :language: javascript + +Resize Server (resize Action) +============================= + +.. rest_method:: POST /servers/{server_id}/action + +Resizes a server. + +Specify the ``resize`` action in the request body. + +A successfully resized server shows a ``VERIFY_RESIZE`` status, +``RESIZED`` VM status, and ``finished`` migration status. If you set the +``resize_confirm_window`` option of the Compute service to an integer value, +the Compute service automatically confirms the resize operation after +the set interval in seconds. + +**Preconditions** + +You can only resize a server when its status is ``ACTIVE`` or ``SHUTOFF``. + +If the server is locked, you must have administrator privileges +to resize the server. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - resize: resize + - flavorRef: flavorRef_resize + - OS-DCF:diskConfig: OS-DCF:diskConfig + +**Example Resize Server (Resize Action)** + +.. literalinclude:: ../../doc/api_samples/servers/server-action-resize.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Resume Suspended Server (resume Action) +======================================= + +.. rest_method:: POST /servers/{server_id}/action + +Resumes a suspended server and changes its status to ``ACTIVE``. + +Specify the ``resume`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - resume: resume + +**Example Resume Suspended Server (Resume Action)** + +.. literalinclude:: ../../doc/api_samples/os-suspend-server/server-resume.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Revert Resized Server (revertResize Action) +=========================================== + +.. rest_method:: POST /servers/{server_id}/action + +Cancels and reverts a pending resize action for a server. + +Specify the ``revertResize`` action in the request body. + +**Preconditions** + +You can only revert the resized server where the status is +``VERIFY_RESIZE`` and the OS-EXT-STS:vm_state is ``resized``. + +If the server is locked, you must have administrator privileges to revert +the resizing. + +**Asynchronous Postconditions** + +After you make this request, you typically must keep polling the server status +to determine whether the request succeeded. A reverting resize operation shows +a status of ``REVERT_RESIZE`` and a task_state of ``resize_reverting``. If +successful, the status will return to ``ACTIVE`` or ``SHUTOFF``. You can also +see the reverted server in the compute node that OpenStack Compute manages. + +**Troubleshooting** + +If the server status remains ``VERIFY_RESIZE``, the request failed. Ensure you +meet the preconditions and run the request again. If the request fails again, +investigate the compute back end. + +The server is not reverted in the compute node that OpenStack Compute manages. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - revertResize: revertResize + +**Example Revert Resized Server (revertResize Action)** + +.. literalinclude:: ../../doc/api_samples/servers/server-action-revert-resize.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Start Server (os-start Action) +============================== + +.. rest_method:: POST /servers/{server_id}/action + +Starts a stopped server and changes its status to ``ACTIVE``. + +Specify the ``os-start`` action in the request body. + +**Preconditions** + +The server status must be ``SHUTOFF``. + +If the server is locked, you must have administrator privileges +to start the server. + +**Asynchronous Postconditions** + +After you successfully start a server, its status changes to ``ACTIVE``. + +**Troubleshooting** + +If the server status does not change to ``ACTIVE``, the start operation failed. +Ensure that you meet the preconditions and run the request again. +If the request fails again, investigate whether another operation is running +that causes a race condition. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - os-start: os-start + +**Example Start server** + +.. literalinclude:: ../../doc/api_samples/servers/server-action-start.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Stop Server (os-stop Action) +============================ + +.. rest_method:: POST /servers/{server_id}/action + +Stops a running server and changes its status to ``SHUTOFF``. + +Specify the ``os-stop`` action in the request body. + +**Preconditions** + +The server status must be ``ACTIVE`` or ``ERROR``. + +If the server is locked, you must have administrator privileges +to stop the server. + +**Asynchronous Postconditions** + +After you successfully stop a server, its status changes to ``SHUTOFF``. +This API operation does not delete the server instance data and the data +will be available again after ``os-start`` action. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - os-stop: os-stop + +**Example Stop server** + +.. literalinclude:: ../../doc/api_samples/servers/server-action-stop.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Suspend Server (suspend Action) +=============================== + +.. rest_method:: POST /servers/{server_id}/action + +Suspends a server and changes its status to ``SUSPENDED``. + +Specify the ``suspend`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - suspend: suspend + +**Example Suspend Server (suspend Action)** + +.. literalinclude:: ../../doc/api_samples/os-suspend-server/server-suspend.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Unlock Server (unlock Action) +============================= + +.. rest_method:: POST /servers/{server_id}/action + +Unlocks a locked server. + +Specify the ``unlock`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - unlock: unlock + +**Example Unlock Server (unlock Action)** + +.. literalinclude:: ../../doc/api_samples/os-lock-server/unlock-server.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Unpause Server (unpause Action) +=============================== + +.. rest_method:: POST /servers/{server_id}/action + +Unpauses a paused server and changes its status to ``ACTIVE``. + +Specify the ``unpause`` action in the request body. + +Policy defaults enable only users with the administrative role or +the owner of the server to perform this operation. Cloud providers +can change these permissions through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - unpause: unpause + +**Example Unpause Server (unpause Action)** + +.. literalinclude:: ../../doc/api_samples/os-pause-server/unpause-server.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Unrescue Server (unrescue Action) +================================= + +.. rest_method:: POST /servers/{server_id}/action + +Unrescues a server. Changes status to ``ACTIVE``. + +Specify the ``unrescue`` action in the request body. + +**Preconditions** + +The server must exist. + +You can only unrescue a server when its status is ``RESCUE``. + +**Asynchronous Postconditions** + +After you successfully unrescue a server and make a +``GET /servers/​{server_id}​`` +request, its status changes to ``ACTIVE``. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - unrescue: unrescue + +**Example Unrescue server** + +.. literalinclude:: ../../doc/api_samples/os-rescue/server-unrescue-req.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. diff --git a/api-ref/source/servers-admin-action.inc b/api-ref/source/servers-admin-action.inc new file mode 100644 index 00000000000..b7227d5c283 --- /dev/null +++ b/api-ref/source/servers-admin-action.inc @@ -0,0 +1,224 @@ +.. -*- rst -*- + +========================================================== + Servers - run an administrative action (servers, action) +========================================================== + +Enables administrators to perform an action on a server. Specify the +action in the request body. + +You can inject network information into, migrate, live-migrate, +reset networking on, and reset the state of a server. + + +Inject Network Information (injectNetworkInfo Action) +===================================================== + +.. rest_method:: POST /servers/{server_id}/action + +Injects network information into a server. + +Specify the ``injectNetworkInfo`` action in the request body. + +Policy defaults enable only users with the administrative role to +perform this operation. Cloud providers can change these permissions +through the ``policy.json`` file. + +.. warning:: There is very limited support on this API, For more information, + see `nova virt support matrix + `__ + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - injectNetworkInfo: injectNetworkInfo + +**Example Inject Network Information (injectNetworkInfo Action)** + +.. literalinclude:: ../../doc/api_samples/os-admin-actions/admin-actions-inject-network-info.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Migrate Server (migrate Action) +=============================== + +.. rest_method:: POST /servers/{server_id}/action + +Migrates a server to a host. The scheduler chooses the host. + +Specify the ``migrate`` action in the request body. + +Policy defaults enable only users with the administrative role to +perform this operation. Cloud providers can change these permissions +through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - migrate: migrate + +**Example Migrate Server (migrate Action)** + +.. literalinclude:: ../../doc/api_samples/os-migrate-server/migrate-server.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Live-Migrate Server (os-migrateLive Action) +=========================================== + +.. rest_method:: POST /servers/{server_id}/action + +Live-migrates a server to a new host without rebooting. + +Specify the ``os-migrateLive`` action in the request body. + +Use the ``host`` parameter to specify the destination host. If +this param is ``null``, the scheduler chooses a host. If a scheduled host +is not suitable to do migration, the scheduler tries up to +``migrate_max_retries`` rescheduling attempts. + +Starting from API version 2.25, the ``block_migration`` parameter could be +to ``auto`` so that nova can decide value of block_migration during live +migration. + +Policy defaults enable only users with the administrative role to +perform this operation. Cloud providers can change these permissions +through the ``policy.json`` file. + +Starting from REST API version 2.34 pre-live-migration checks are done +asynchronously, results of these checks are available in ``instance-actions``. +Nova responds immediately, and no pre-live-migration checks are returned. +The instance will not immediately change state to ``ERROR``, if a failure of +the live-migration checks occurs. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403) +itemNotFound(404), conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - os-migrateLive: os-migrateLive + - host: host_migration + - block_migration: block_migration + - block_migration: block_migration_2_25 + - disk_over_commit: disk_over_commit + - force: force_live_migrate + +**Example Live-Migrate Server (os-migrateLive Action)** + +.. literalinclude:: ../../doc/api_samples/os-migrate-server/v2.30/live-migrate-server.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Reset Networking On A Server (resetNetwork Action) +================================================== + +.. rest_method:: POST /servers/{server_id}/action + +Resets networking on a server. + +.. note:: + + Only the XenServer driver implements this feature and only if the guest + has the XenAPI agent in the targeted server. + +Specify the ``resetNetwork`` action in the request body. + +Policy defaults enable only users with the administrative role to +perform this operation. Cloud providers can change these permissions +through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - resetNetwork: resetNetwork + +**Example Reset Networking On A Server (resetNetwork Action)** + +.. literalinclude:: ../../doc/api_samples/os-admin-actions/admin-actions-reset-network.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. + + +Reset Server State (os-resetState Action) +========================================= + +.. rest_method:: POST /servers/{server_id}/action + +Resets the state of a server. + +Specify the ``os-resetState`` action and the ``state`` in the request body. + +Policy defaults enable only users with the administrative role to +perform this operation. Cloud providers can change these permissions +through the ``policy.json`` file. + +Normal response codes: 202 + +Error response codes: unauthorized(401), forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - os-resetState: os-resetState + - os-resetState.state: os-resetState_state + +**Example Reset Server State (os-resetState Action)** + +.. literalinclude:: ../../doc/api_samples/os-admin-actions/admin-actions-reset-server-state.json + :language: javascript + +Response +-------- + +If successful, this method does not return content in the response body. diff --git a/api-ref/source/servers-remote-consoles.inc b/api-ref/source/servers-remote-consoles.inc new file mode 100644 index 00000000000..7bcf96b17c0 --- /dev/null +++ b/api-ref/source/servers-remote-consoles.inc @@ -0,0 +1,58 @@ +.. -*- rst -*- + +====================== +Server Remote Consoles +====================== + +Create server remote console. + +Create Remote Console +===================== + +.. rest_method:: POST /servers/{server_id}/remote-consoles + +.. note:: Microversion 2.6 or greater is required for this API. + +The API provides a unified request for creating a remote console. The user can +get a URL to connect the console from this API. The URL includes the token +which is used to get permission to access the console. Servers may support +different console protocols. To return a remote console using a specific +protocol, such as RDP, set the ``protocol`` parameter to ``rdp``. For the same +protocol, there may be different connection types such as ``vnc protocol and +novnc type`` or ``vnc protocol and xvpvnc type``. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), +conflict(409), notImplemented(501) + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server_id: server_id_path + - remote_console: remote_console + - protocol: remote_console_protocol + - type: remote_console_type + +**Example Get Remote VNC Console** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/v2.6/create-vnc-console-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - remote_console: remote_console + - protocol: remote_console_protocol + - type: remote_console_type + - url: remote_console_url + +**Example Get Remote VNC Console** + +.. literalinclude:: ../../doc/api_samples/os-remote-consoles/v2.6/create-vnc-console-resp.json + :language: javascript diff --git a/api-ref/source/servers.inc b/api-ref/source/servers.inc new file mode 100644 index 00000000000..3247d6f8ce2 --- /dev/null +++ b/api-ref/source/servers.inc @@ -0,0 +1,832 @@ +.. -*- rst -*- +.. TODO(sdague) parameter microversions need to be gone through in the + response (request side should be good) +.. needs:example_verification +.. needs:body_verification + +=================== + Servers (servers) +=================== + +Lists, creates, shows details for, updates, and deletes servers. + +**Passwords** + +When you create a server, you can specify a password through the +optional adminPass attribute. The password must meet the complexity +requirements set by your OpenStack Compute provider. The server might +enter an ``ERROR`` state if the complexity requirements are not met. In +this case, a client might issue a change password action to reset the +server password. + +If you do not specify a password, the API generates and assigns a random +password that it returns in the response object. This password meets the +security requirements set by the compute provider. For security reasons, +subsequent GET calls do not require this password. + +**Server metadata** + +You can specify custom server metadata at server launch time. The +maximum size for each metadata key-value pair is 255 bytes. The compute +provider determines the maximum number of key-value pairs for each +server. You can query this value through the ``maxServerMeta`` absolute +limit. + +**Server networks** + +You can specify one or more networks to which the server connects at +launch time. Users can also specify a specific port on the network or +the fixed IP address to assign to the server interface. + +.. note:: You can use both IPv4 and IPv6 addresses as access addresses, + and you can assign both addresses simultaneously. You can update + access addresses after you create a server. + +**Server personality** + +To customize the personality of a server instance, you can inject data +into its file system. For example, you might insert ssh keys, set +configuration files, or store data that you want to retrieve from inside +the instance. This customization method provides minimal launch-time +personalization. If you require significant customization, create a +custom image. + +Follow these guidelines when you inject files: + +- The maximum size of the file path data is 255 bytes. + +- Encode the file contents as a Base64 string. The compute provider + determines the maximum size of the file contents. The image that you + use to create the server determines this value. + + .. note:: + + The maximum limit refers to the number of bytes in the decoded + data and not to the number of characters in the encoded data. + +- The ``maxPersonality`` absolute limit defines the maximum number of + file path and content pairs that you can supply. The compute provider + determines this value. + +- The ``maxPersonalitySize`` absolute limit is a byte limit that + applies to all images in the deployment. Providers can set additional + per-image personality limits. + +The file injection might not occur until after the server builds and +boots. + +After file injection, only system administrators can access personality +files. For example, on Linux, all files have root as the owner and the +root group as the group owner, and allow only user and group read access +(``chmod 440``). + +**Server access addresses** + +In a hybrid environment, the underlying implementation might not control +the IP address of a server. Instead, the access IP address might be part +of the dedicated hardware; for example, a router/NAT device. In this +case, you cannot use the addresses that the implementation provides to +access the server from outside the local LAN. Instead, the API might +assign a separate access address at creation time to provide access to +the server. This address might not be directly bound to a network +interface on the server and might not necessarily appear when you query +the server addresses. However, clients should use an access address to +access the server directly. + +List Servers +============ + +.. rest_method:: GET /servers + +Lists IDs, names, and links for all servers. + + +Servers contain a status attribute that indicates the current server +state. You can filter on the server status when you complete a list +servers request. The server status is returned in the response +body. The possible server status values are: + +- ``ACTIVE``. The server is active. +- ``BUILDING``. The server has not finished the original build process. +- ``DELETED``. The server is permanently deleted. +- ``ERROR``. The server is in error. +- ``HARD_REBOOT``. The server is hard rebooting. This is equivalent to + pulling the power plug on a physical server, plugging it back in, + and rebooting it. +- ``MIGRATING``. The server is being migrated to a new host. +- ``PASSWORD``. The password is being reset on the server. +- ``PAUSED``. In a paused state, the state of the server is stored in + RAM. A paused server continues to run in frozen state. +- ``REBOOT``. The server is in a soft reboot state. A reboot command + was passed to the operating system. +- ``REBUILD``. The server is currently being rebuilt from an image. +- ``RESCUED``. The server is in rescue mode. A rescue image is running + with the original server image attached. +- ``RESIZED``. Server is performing the differential copy of data that + changed during its initial copy. Server is down for this stage. +- ``REVERT_RESIZE``. The resize or migration of a server failed for + some reason. The destination server is being cleaned up and the + original source server is restarting. +- ``SOFT_DELETED``. The server is marked as deleted but the disk + images are still available to restore. +- ``STOPPED``. The server is powered off and the disk image still + persists. +- ``SUSPENDED``. The server is suspended, either by request or + necessity. This status appears for only the XenServer/XCP, KVM, and + ESXi hypervisors. Administrative users can suspend an instance if it + is infrequently used or to perform system maintenance. When you + suspend an instance, its VM state is stored on disk, all memory is + written to disk, and the virtual machine is stopped. Suspending an + instance is similar to placing a device in hibernation; memory and + vCPUs become available to create other instances. +- ``UNKNOWN``. The state of the server is unknown. Contact your cloud + provider. +- ``VERIFY_RESIZE``. System is awaiting confirmation that the server + is operational after a move or resize. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), +forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - access_ip_v4: access_ip_v4_query_server + - access_ip_v6: access_ip_v6_query_server + - all_tenants: all_tenants_query + - auto_disk_config: disk_config_query_server + - availability_zone: availability_zone_query_server + - config_drive: config_drive_query_server + - changes-since: changes_since_server + - created_at: created_at_query_server + - deleted: deleted_query + - description: description_query_server + - flavor: flavor_query + - host: host_query_server + - hostname: hostname_query_server + - image: image_query + - ip: ip_query + - ip6: ip6_query + - kernel_id: kernel_id_query_server + - key_name: key_name_query_server + - launch_index: launch_index_query_server + - launched_at: launched_at_query_server + - limit: limit + - locked_by: locked_by_query_server + - marker: marker + - name: server_name_query + - node: node_query_server + - not-tags: not_tags_query + - not-tags-any: not_tags_any_query + - power_state: power_state_query_server + - progress: progress_query_server + - project_id: project_id_query_server + - ramdisk_id: ramdisk_id_query_server + - reservation_id: reservation_id_query + - root_device_name: server_root_device_name_query + - sort_dir: sort_dir_server + - sort_key: sort_key_server + - status: server_status_query + - tags: tags_query + - tags-any: tags_any_query + - task_state: task_state_query_server + - terminated_at: terminated_at_query_server + - user_id: user_id_query_server + - uuid: server_uuid_query + - vm_state: vm_state_query_server + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - servers: servers + - id: server_id + - links: links + - name: server_name + +**Example List Servers** + +.. literalinclude:: ../../doc/api_samples/servers/servers-list-resp.json + :language: javascript + + +Create Server +============= + +.. rest_method:: POST /servers + +Creates a server. + +The progress of this operation depends on the location of the +requested image, network I/O, host load, selected flavor, and other +factors. + +To check the progress of the request, make a ``GET /servers/{id}`` +request. This call returns a progress attribute, which is a percentage +value from 0 to 100. + +The ``Location`` header returns the full URL to the newly created +server and is available as a ``self`` and ``bookmark`` link in the +server representation. + +When you create a server, the response shows only the server ID, its +links, and the admin password. You can get additional attributes +through subsequent ``GET`` requests on the server. + +Include the ``block-device-mapping-v2`` parameter in the create +request body to boot a server from a volume. + +Include the ``key_name`` parameter in the create request body to add a +keypair to the server when you create it. To create a keypair, make a +`create keypair +`__ +request. + +.. note:: Starting with microversion 2.37 the ``networks`` field is required. + +**Preconditions** + +- The user must have sufficient server quota to create the number of + servers requested. +- The connection to the Image service is valid. + +**Asynchronous postconditions** + +- With correct permissions, you can see the server status as + ``ACTIVE`` through API calls. +- With correct access, you can see the created server in the compute + node that OpenStack Compute manages. + +**Troubleshooting** + +- If the server status remains ``BUILDING`` or shows another error + status, the request failed. Ensure you meet the preconditions then + investigate the compute node. + +- The server is not created in the compute node that OpenStack Compute + manages. + +- The compute node needs enough free resource to match the resource of + the server creation request. + +- Ensure that the scheduler selection filter can fulfill the request + with the available compute nodes that match the selection criteria + of the filter. + +Normal response codes: 202 + +Error response codes: badRequest(400), unauthorized(401), +forbidden(403), itemNotFound(404), conflict(409) + +.. + TODO(sdague): leave these notes for later when fixing the body + language. They are commented out so they won't render, but are + useful to not have to look this up again later. + + A conflict(409) is returned in the event of trying to allocated already + allocated resources (such as networks) to the server in question. + + entityTooLarge(413) is returned if the ``user_data`` exceeds what is + allowed by the backend. + + All other failure conditions map to 400, and will need to be + disambiguated by the error string returned. + +Request +------- + +.. rest_parameters:: parameters.yaml + + + - server: server + - name: server_name + - flavorRef: flavorRef + - imageRef: imageRef + - security_groups: security_groups + - metadata: metadata + - accessIPv4: accessIPv4_in + - accessIPv6: accessIPv6_in + - adminPass: adminPass_request + - user_data: user_data + - availability_zone: os-availability-zone:availability_zone + - networks: networks + - networks.uuid: network_uuid + - networks.port: port + - networks.fixed_ip: fixed_ip + - networks.tag: device_tag_nic + - personality: personality + - block_device_mapping_v2: block_device_mapping_v2 + - block_device_mapping_v2.device_name: device_name + - block_device_mapping_v2.source_type: source_type + - block_device_mapping_v2.destination_type: destination_type + - block_device_mapping_v2.delete_on_termination: delete_on_termination + - block_device_mapping_v2.guest_format: guest_format + - block_device_mapping_v2.boot_index: boot_index + - block_device_mapping_v2.uuid: block_device_uuid + - block_device_mapping_v2.tag: device_tag_bdm + - config_drive: config_drive + - key_name: key_name + - os:scheduler_hints: os:scheduler_hints + - OS-DCF:diskConfig: OS-DCF:diskConfig + - description: server_description + - tags: server_tags_create + +**Example Create Server** + +.. literalinclude:: ../../doc/api_samples/servers/server-create-req.json + :language: javascript + +**Example Create Server With Automatic Networking (v2.37)** + +.. literalinclude:: ../../doc/api_samples/servers/v2.37/server-create-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - server: server + - id: server_id + - links: links + - OS-DCF:diskConfig: disk_config + - security_groups: security_groups_obj + - security_groups.name: name + - adminPass: adminPass_response + +**Example Create Server** + +.. literalinclude:: ../../doc/api_samples/servers/server-create-resp.json + :language: javascript + +Create Multiple Servers +======================= + +.. rest_method:: POST /servers + +There is a second kind of create call which can build multiple servers +at once. This supports all the same parameters as create with a few +additional attributes specific to multiple create. + +Error handling for multiple create is not as consistent as for single +server create, and there is no guarantee that all the servers will be +built. This call should generally be avoided in favor of clients doing +direct individual server creates. + +Request (Additional Parameters) +------------------------------- + +These are the parameters beyond single create that are supported. + +.. rest_parameters:: parameters.yaml + + - name: servers_multiple_create_name + - min_count: servers_min_count + - max_count: servers_max_count + - return_reservation_id: return_reservation_id + +**Example Multiple Create with reservation ID** + +.. literalinclude:: ../../doc/api_samples/os-multiple-create/multiple-create-post-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - reservation_id: reservation_id + +If ``return_reservation_id`` is set to ``true`` only the +``reservation_id`` will be returned. This can be used as a filter with +list servers detailed to see the status of all the servers being +built. + +**Example Create multiple servers with reservation ID** + +.. literalinclude:: ../../doc/api_samples/os-multiple-create/multiple-create-post-resp.json + :language: javascript + +If ``return_reservation_id`` is set to ``false`` a representation of +the ``first`` server will be returned. + +**Example Create multiple servers without reservation ID** + +.. literalinclude:: ../../doc/api_samples/os-multiple-create/multiple-create-no-resv-post-resp.json + :language: javascript + + +List Servers Detailed +===================== + +.. rest_method:: GET /servers/detail + +For each server, shows server details including configuration drive, +extended status, and server usage information. + +The extended status information appears in the OS-EXT-STS:vm_state, +OS-EXT-STS:power_state, and OS-EXT-STS:task_state attributes. + +The server usage information appears in the OS-SRV-USG:launched_at and +OS-SRV-USG:terminated_at attributes. + +To hide addresses information for instances in a certain state, set +the osapi_hide_server_address_states configuration option. Set this +option to a valid VM state in the nova.conf configuration file. + +HostId is unique per account and is not globally unique. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), +forbidden(403) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - access_ip_v4: access_ip_v4_query_server + - access_ip_v6: access_ip_v6_query_server + - all_tenants: all_tenants_query + - auto_disk_config: disk_config_query_server + - availability_zone: availability_zone_query_server + - config_drive: config_drive_query_server + - changes-since: changes_since_server + - created_at: created_at_query_server + - deleted: deleted_query + - description: description_query_server + - flavor: flavor_query + - host: host_query_server + - hostname: hostname_query_server + - image: image_query + - ip: ip_query + - ip6: ip6_query + - kernel_id: kernel_id_query_server + - key_name: key_name_query_server + - launch_index: launch_index_query_server + - launched_at: launched_at_query_server + - limit: limit + - locked_by: locked_by_query_server + - marker: marker + - name: server_name_query + - node: node_query_server + - not-tags: not_tags_query + - not-tags-any: not_tags_any_query + - power_state: power_state_query_server + - progress: progress_query_server + - project_id: project_id_query_server + - ramdisk_id: ramdisk_id_query_server + - reservation_id: reservation_id_query + - root_device_name: server_root_device_name_query + - sort_dir: sort_dir_server + - sort_key: sort_key_server + - status: server_status_query + - tags: tags_query + - tags-any: tags_any_query + - task_state: task_state_query_server + - terminated_at: terminated_at_query_server + - user_id: user_id_query_server + - uuid: server_uuid_query + - vm_state: vm_state_query_server + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - server: server + - addresses: addresses + - created: created + - flavor: flavor_server + - flavor.id: flavor_id_body_2_46 + - flavor.links: flavor_links_2_46 + - flavor.vcpus: flavor_cpus_2_47 + - flavor.ram: flavor_ram_2_47 + - flavor.disk: flavor_disk_2_47 + - flavor.ephemeral: flavor_ephem_disk_2_47 + - flavor.swap: flavor_swap_2_47 + - flavor.original_name: flavor_original_name + - flavor.extra_specs: extra_specs_2_47 + - flavor.extra_specs.key: flavor_extra_spec_key_2_47 + - flavor.extra_specs.value: flavor_extra_spec_value_2_47 + - hostId: hostId + - id: server_id + - image: image + - key_name: key_name_resp + - links: links + - metadata: metadata_compat + - name: server_name + - accessIPv4: accessIPv4 + - accessIPv6: accessIPv6 + - config_drive: config_drive_resp + - OS-DCF:diskConfig: disk_config + - OS-EXT-AZ:availability_zone: OS-EXT-AZ:availability_zone + - OS-EXT-SRV-ATTR:host: OS-EXT-SRV-ATTR:host + - OS-EXT-SRV-ATTR:hypervisor_hostname: OS-EXT-SRV-ATTR:hypervisor_hostname + - OS-EXT-SRV-ATTR:instance_name: OS-EXT-SRV-ATTR:instance_name + - OS-EXT-STS:power_state: OS-EXT-STS:power_state + - OS-EXT-STS:task_state: OS-EXT-STS:task_state + - OS-EXT-STS:vm_state: OS-EXT-STS:vm_state + - os-extended-volumes:volumes_attached: os-extended-volumes:volumes_attached + - os-extended-volumes:volumes_attached.id: os-extended-volumes:volumes_attached.id + - os-extended-volumes:volumes_attached.delete_on_termination: os-extended-volumes:volumes_attached.delete_on_termination + - OS-SRV-USG:launched_at: OS-SRV-USG:launched_at + - OS-SRV-USG:terminated_at: OS-SRV-USG:terminated_at + - progress: progress + - security_groups: security_groups_obj + - security_group.name: name + - status: server_status + - host_status: host_status + - tenant_id: tenant_id_body + - updated: updated + - user_id: user_id + - OS-EXT-SRV-ATTR:hostname: server_hostname + - OS-EXT-SRV-ATTR:reservation_id: server_reservation_id + - OS-EXT-SRV-ATTR:launch_index: server_launch_index + - OS-EXT-SRV-ATTR:kernel_id: server_kernel_id + - OS-EXT-SRV-ATTR:ramdisk_id: server_ramdisk_id + - OS-EXT-SRV-ATTR:root_device_name: server_root_device_name + - OS-EXT-SRV-ATTR:user_data: server_user_data + - locked: locked + +**Example List Servers Detailed (2.47)** + +.. literalinclude:: /../../doc/api_samples/servers/v2.47/servers-details-resp.json + :language: javascript + + +Show Server Details +=================== + +.. rest_method:: GET /servers/{server_id} + +Shows details for a server. + +Includes server details including configuration drive, extended status, and server usage information. + +The extended status information appears in the ``OS-EXT-STS:vm_state``, ``OS-EXT-STS:power_state``, and ``OS-EXT-STS:task_state`` attributes. + +The server usage information appears in the ``OS-SRV-USG:launched_at`` and ``OS-SRV-USG:terminated_at`` attributes. + +To hide ``addresses`` information for instances in a certain state, set the ``osapi_hide_server_address_states`` configuration option. Set this option to a valid VM state in the ``nova.conf`` configuration file. + +HostId is unique per account and is not globally unique. + +**Preconditions** + +The server must exist. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403), +itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - server: server + - addresses: addresses + - created: created + - flavor: flavor_server + - flavor.id: flavor_id_body_2_46 + - flavor.links: flavor_links_2_46 + - flavor.vcpus: flavor_cpus_2_47 + - flavor.ram: flavor_ram_2_47 + - flavor.disk: flavor_disk_2_47 + - flavor.ephemeral: flavor_ephem_disk_2_47 + - flavor.swap: flavor_swap_2_47 + - flavor.original_name: flavor_original_name + - flavor.extra_specs: extra_specs_2_47 + - flavor.extra_specs.key: flavor_extra_spec_key_2_47 + - flavor.extra_specs.value: flavor_extra_spec_value_2_47 + - hostId: hostId + - id: server_id + - image: image + - key_name: key_name_resp + - links: links + - metadata: metadata_compat + - name: server_name + - accessIPv4: accessIPv4 + - accessIPv6: accessIPv6 + - config_drive: config_drive_resp + - OS-DCF:diskConfig: disk_config + - OS-EXT-AZ:availability_zone: OS-EXT-AZ:availability_zone + - OS-EXT-SRV-ATTR:host: OS-EXT-SRV-ATTR:host + - OS-EXT-SRV-ATTR:hypervisor_hostname: OS-EXT-SRV-ATTR:hypervisor_hostname + - OS-EXT-SRV-ATTR:instance_name: OS-EXT-SRV-ATTR:instance_name + - OS-EXT-STS:power_state: OS-EXT-STS:power_state + - OS-EXT-STS:task_state: OS-EXT-STS:task_state + - OS-EXT-STS:vm_state: OS-EXT-STS:vm_state + - os-extended-volumes:volumes_attached: os-extended-volumes:volumes_attached + - os-extended-volumes:volumes_attached.id: os-extended-volumes:volumes_attached.id + - os-extended-volumes:volumes_attached.delete_on_termination: os-extended-volumes:volumes_attached.delete_on_termination + - OS-SRV-USG:launched_at: OS-SRV-USG:launched_at + - OS-SRV-USG:terminated_at: OS-SRV-USG:terminated_at + - progress: progress + - security_groups: security_groups_obj + - security_group.name: name + - status: server_status + - host_status: host_status + - tenant_id: tenant_id_body + - updated: updated + - user_id: user_id + - fault: fault + - OS-EXT-SRV-ATTR:hostname: server_hostname + - OS-EXT-SRV-ATTR:reservation_id: server_reservation_id + - OS-EXT-SRV-ATTR:launch_index: server_launch_index + - OS-EXT-SRV-ATTR:kernel_id: server_kernel_id + - OS-EXT-SRV-ATTR:ramdisk_id: server_ramdisk_id + - OS-EXT-SRV-ATTR:root_device_name: server_root_device_name + - OS-EXT-SRV-ATTR:user_data: server_user_data + - locked: locked + +**Example Show Server Details (2.47)** + +.. literalinclude:: ../../doc/api_samples/servers/v2.47/server-get-resp.json + :language: javascript + +Update Server +============= + +.. rest_method:: PUT /servers/{server_id} + +Updates the editable attributes of an existing server. + +Normal response codes: 200 + +Error response codes: badRequest(400), unauthorized(401), +forbidden(403), itemNotFound(404) + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + - accessIPv4: accessIPv4_in + - accessIPv6: accessIPv6_in + - name: server_name_optional + - OS-DCF:diskConfig: OS-DCF:diskConfig + - description: server_description + +**Example Update server name (2.47)** + +.. literalinclude:: ../../doc/api_samples/servers/v2.47/server-update-req.json + :language: javascript + +.. + TODO(sdague): split up the update examples to show them being used + separately. + + **Example Update server IP addresses: JSON request** + + .. literalinclude:: ../../doc/api_samples/servers/server-update-address-req.json + :language: javascript + + **Example Update server OS-DCF:diskConfig parameter: JSON request** + + .. literalinclude:: ../../doc/api_samples/servers/server-update-diskconfig-req.json + :language: javascript + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - server: server + - addresses: addresses + - created: created + - flavor: flavor_server + - flavor.id: flavor_id_body_2_46 + - flavor.links: flavor_links_2_46 + - flavor.vcpus: flavor_cpus_2_47 + - flavor.ram: flavor_ram_2_47 + - flavor.disk: flavor_disk_2_47 + - flavor.ephemeral: flavor_ephem_disk_2_47 + - flavor.swap: flavor_swap_2_47 + - flavor.original_name: flavor_original_name + - flavor.extra_specs: extra_specs_2_47 + - flavor.extra_specs.key: flavor_extra_spec_key_2_47 + - flavor.extra_specs.value: flavor_extra_spec_value_2_47 + - hostId: hostId + - id: server_id + - image: image + - key_name: key_name + - links: links + - metadata: metadata + - name: server_name + - accessIPv4: accessIPv4 + - accessIPv6: accessIPv6 + - OS-DCF:diskConfig: OS-DCF:diskConfig + - OS-EXT-AZ:availability_zone: OS-EXT-AZ:availability_zone + - OS-EXT-SRV-ATTR:host: OS-EXT-SRV-ATTR:host + - OS-EXT-SRV-ATTR:hypervisor_hostname: OS-EXT-SRV-ATTR:hypervisor_hostname + - OS-EXT-SRV-ATTR:instance_name: OS-EXT-SRV-ATTR:instance_name + - OS-EXT-STS:power_state: OS-EXT-STS:power_state + - OS-EXT-STS:task_state: OS-EXT-STS:task_state + - OS-EXT-STS:vm_state: OS-EXT-STS:vm_state + - OS-SRV-USG:launched_at: OS-SRV-USG:launched_at + - OS-SRV-USG:terminated_at: OS-SRV-USG:terminated_at + - progress: progress + - security_groups: security_groups_obj + - security_group.name: name + - status: server_status + - host_status: host_status + - tenant_id: tenant_id_body + - updated: updated + - user_id: user_id + - OS-EXT-SRV-ATTR:hostname: server_hostname + - OS-EXT-SRV-ATTR:reservation_id: server_reservation_id + - OS-EXT-SRV-ATTR:launch_index: server_launch_index + - OS-EXT-SRV-ATTR:kernel_id: server_kernel_id + - OS-EXT-SRV-ATTR:ramdisk_id: server_ramdisk_id + - OS-EXT-SRV-ATTR:root_device_name: server_root_device_name + - OS-EXT-SRV-ATTR:user_data: server_user_data + - locked: locked + +**Example Update server name (2.47)** + +.. literalinclude:: ../../doc/api_samples/servers/v2.47/server-update-resp.json + :language: javascript + +Delete Server +============= + +.. rest_method:: DELETE /servers/{server_id} + +Deletes a server. + +By default, the instance is going to be (hard) deleted immediately from +the system, but you can set ``reclaim_instance_interval`` > 0 to make +the API soft delete the instance, so that the instance's vm won't be +deleted until the ``reclaim_instance_interval`` has expired since the +instance was soft deleted. The instance marked as ``SOFT_DELETED`` can +be recovered via ``restore`` action before it's really deleted from the +system. + +**Preconditions** + +- The server must exist. +- Anyone can delete a server when the status of the server is not + locked and when the policy allows. +- If the server is locked, you must have administrator privileges to + delete the server. + +**Asynchronous postconditions** + +- With correct permissions, you can see the server status as ``deleting``. +- The ports attached to the server, which Nova created during the server + create process or when attaching interfaces later, are deleted. +- The server does not appear in the list servers response. +- If hard delete, the server managed by OpenStack Compute is deleted + on the compute node. + +**Troubleshooting** + +- If server status remains in ``deleting`` status or another error + status, the request failed. Ensure that you meet the + preconditions. Then, investigate the compute back end. +- The request returns the HTTP 409 response code when the server is + locked even if you have correct permissions. Ensure that you meet the + preconditions then investigate the server status. +- The server managed by OpenStack Compute is not deleted from the + compute node. + +Normal response codes: 204 + +Error response codes: unauthorized(401), forbidden(403), +itemNotFound(404), conflict(409) + +.. + TODO(sdague): for later phase of updating body. + + conflict is returned under 2 conditions. When the instance is + locked, so can't be deleted, or if the instance is in some other + state which makes it not possible to delete. + + +Request +------- + +.. rest_parameters:: parameters.yaml + + - server_id: server_id_path + +Response +-------- + +There is no body content for the response of a successful DELETE query diff --git a/api-ref/source/urls.inc b/api-ref/source/urls.inc new file mode 100644 index 00000000000..72f34fcc05a --- /dev/null +++ b/api-ref/source/urls.inc @@ -0,0 +1,26 @@ +.. -*- rst -*- + +============== + Service URLs +============== + +All API calls described throughout the rest of this document require +authentication with the OpenStack Identity service. After authentication, +a base ``service url`` can be extracted from the Identity token of type +``compute``. This ``service url`` will be the root url that every API call +uses to build a full path. + +For instance, if the ``service url`` is +``http://mycompute.pvt/compute/v2.1`` then the full API call for +``/servers`` is ``http://mycompute.pvt/compute/v2.1/servers``. + +Depending on the deployment, the Compute ``service url`` might be http or +https, a custom port, a custom path, and include your tenant id. The +only way to know the urls for your deployment is by using the service +catalog. The Compute URL should never be hard coded in applications, +even if they are only expected to work at a single site. It should +always be discovered from the Identity token. + +As such, for the rest of this document we will be using short hand +where ``GET /servers`` really means ``GET +{your_compute_service_url}/servers``. diff --git a/api-ref/source/versions.inc b/api-ref/source/versions.inc new file mode 100644 index 00000000000..2d07eb3c2f5 --- /dev/null +++ b/api-ref/source/versions.inc @@ -0,0 +1,108 @@ +.. -*- rst -*- + +============== + API Versions +============== + +In order to bring new features to users over time, the Nova API +supports versioning. There are two kinds of versions in Nova. + +- ''major versions'', which have dedicated urls +- ''microversions'', which can be requested through the use of the + ``X-OpenStack-Nova-API-Version`` header, or since microversion 2.27 + the ``OpenStack-API-Version`` header may also be used. + +For more details about Microversions, please reference: +`Microversions +`_ + +.. note:: The maximum microversion supported by each release varies. + Please reference: + `API Microversion History + `__ + for API microversion history details. + +The Version APIs work differently from other APIs as they *do not* +require authentication. + +List All Major Versions +======================= + +.. rest_method:: GET / + +This fetches all the information about all known major API versions in +the deployment. Links to more specific information will be provided +for each API version, as well as information about supported min and +max microversions. + +Normal Response Codes: 200 + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - versions: versions + - id: version_id + - links: links + - min_version: version_min + - status: version_status + - updated: updated_version + - version: version_max + + +Response Example +---------------- + +This demonstrates the expected response from a bleeding edge server +that supports up to the current microversion. When querying OpenStack +environments you will typically find the current microversion on the +v2.1 API is lower than listed below. + +.. literalinclude:: /../../doc/api_samples/versions/versions-get-resp.json + :language: javascript + + +Show Details of Specific API Version +==================================== + +.. rest_method:: GET /{api_version} + +This gets the details of a specific API at its root. Nearly all this +information exists at the API root, so this is mostly a redundant +operation. + +.. TODO(sdague) we should probably deprecate this call as everything + that's needed is really in the root now + +Normal Response Codes: 200 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - api_version: api_version + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - version: version + - id: version_id + - links: links + - media-types: media_types + - min_version: version_min + - status: version_status + - updated: updated_version + - version: version_max + + +Response Example +---------------- + +This is an example of a ``GET /v2.1`` on a relatively current server. + +.. literalinclude:: /../../doc/api_samples/versions/v21-version-get-resp.json + :language: javascript diff --git a/bandit.yaml b/bandit.yaml deleted file mode 100644 index 69e2cea845d..00000000000 --- a/bandit.yaml +++ /dev/null @@ -1,129 +0,0 @@ -# optional: after how many files to update progress -#show_progress_every: 100 - -# optional: plugins directory name -#plugins_dir: 'plugins' - -# optional: plugins discovery name pattern -plugin_name_pattern: '*.py' - -# optional: terminal escape sequences to display colors -#output_colors: -# DEFAULT: '\033[0m' -# HEADER: '\033[95m' -# LOW: '\033[94m' -# MEDIUM: '\033[93m' -# HIGH: '\033[91m' - -# optional: log format string -#log_format: "[%(module)s]\t%(levelname)s\t%(message)s" - -# globs of files which should be analyzed -include: - - '*.py' - -# a list of strings, which if found in the path will cause files to be excluded -# for example /tests/ - to remove all all files in tests directory -exclude_dirs: - - '/tests/' - -profiles: - XSS: - include: - - jinja2_autoescape_false - - use_of_mako_templates - - ShellInjection: - include: - - subprocess_popen_with_shell_equals_true - - subprocess_without_shell_equals_true - - any_other_function_with_shell_equals_true - - start_process_with_a_shell - - start_process_with_no_shell - exclude: - - SqlInjection: - include: - - hardcoded_sql_expressions - -blacklist_calls: - bad_name_sets: - - pickle: - qualnames: [pickle.loads, pickle.load, pickle.Unpickler, - cPickle.loads, cPickle.load, cPickle.Unpickler] - message: "Pickle library appears to be in use, possible security issue." - - marshal: - qualnames: [marshal.load, marshal.loads] - message: "Deserialization with the marshal module is possibly dangerous." - - md5: - qualnames: [hashlib.md5] - message: "Use of insecure MD5 hash function." - - mktemp_q: - qualnames: [tempfile.mktemp] - message: "Use of insecure and deprecated function (mktemp)." - - eval: - qualnames: [eval] - message: "Use of possibly insecure function - consider using safer ast.literal_eval." - - mark_safe: - names: [mark_safe] - message: "Use of mark_safe() may expose cross-site scripting vulnerabilities and should be reviewed." - - httpsconnection: - qualnames: [httplib.HTTPSConnection] - message: "Use of HTTPSConnection does not provide security, see https://wiki.openstack.org/wiki/OSSN/OSSN-0033" - - yaml_load: - qualnames: [yaml.load] - message: "Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load()." - - urllib_urlopen: - qualnames: [urllib.urlopen, urllib.urlretrieve, urllib.URLopener, urllib.FancyURLopener, urllib2.urlopen, urllib2.Request] - message: "Audit url open for permitted schemes. Allowing use of file:/ or custom schemes is often unexpected." - - paramiko_injection: - qualnames: [paramiko.exec_command, paramiko.invoke_shell] - message: "Paramiko exec_command() and invoke_shell() usage may expose command injection vulnerabilities and should be reviewed." - -shell_injection: - # Start a process using the subprocess module, or one of its wrappers. - subprocess: [subprocess.Popen, subprocess.call, subprocess.check_call, - subprocess.check_output, utils.execute, utils.execute_with_timeout] - # Start a process with a function vulnerable to shell injection. - shell: [os.system, os.popen, os.popen2, os.popen3, os.popen4, - popen2.popen2, popen2.popen3, popen2.popen4, popen2.Popen3, - popen2.Popen4, commands.getoutput, commands.getstatusoutput] - # Start a process with a function that is not vulnerable to shell injection. - no_shell: [os.execl, os.execle, os.execlp, os.execlpe, os.execv,os.execve, - os.execvp, os.execvpe, os.spawnl, os.spawnle, os.spawnlp, - os.spawnlpe, os.spawnv, os.spawnve, os.spawnvp, os.spawnvpe, - os.startfile] - -blacklist_imports: - bad_import_sets: - - telnet: - imports: [telnetlib] - level: HIGH - message: "Telnet is considered insecure. Use SSH or some other encrypted protocol." - - info_libs: - imports: [pickle, cPickle, subprocess, Crypto] - level: LOW - message: "Consider possible security implications associated with {module} module." - -hardcoded_password: - word_list: "wordlist/default-passwords" - -ssl_with_bad_version: - bad_protocol_versions: - - 'PROTOCOL_SSLv2' - - 'SSLv2_METHOD' - - 'SSLv23_METHOD' - - 'PROTOCOL_SSLv3' # strict option - - 'PROTOCOL_TLSv1' # strict option - - 'SSLv3_METHOD' # strict option - - 'TLSv1_METHOD' # strict option - -password_config_option_not_marked_secret: - function_names: - - oslo.config.cfg.StrOpt - - oslo_config.cfg.StrOpt - -execute_with_run_as_root_equals_true: - function_names: - - nova.utils.execute - - nova.utils.trycmd diff --git a/bindep.txt b/bindep.txt new file mode 100644 index 00000000000..49c6c732e04 --- /dev/null +++ b/bindep.txt @@ -0,0 +1,37 @@ +# This is a cross-platform list tracking distribution packages needed for install and tests; +# see http://docs.openstack.org/infra/bindep/ for additional information. + +build-essential [platform:dpkg test] +gcc [platform:rpm test] +# gettext and graphviz are needed by doc builds only. For transition, +# have them in both doc and test. +# TODO(jaegerandi): Remove test once infra scripts are updated. +gettext [doc test] +graphviz [doc test] +language-pack-en [platform:ubuntu] +libffi-dev [platform:dpkg test] +libffi-devel [platform:rpm test] +libmysqlclient-dev [platform:dpkg] +libpq-dev [platform:dpkg test] +libsqlite3-dev [platform:dpkg test] +libxml2-dev [platform:dpkg test] +libxslt-devel [platform:rpm test] +libxslt1-dev [platform:dpkg test] +locales [platform:debian] +mysql [platform:rpm] +mysql-client [platform:dpkg] +mysql-devel [platform:rpm test] +mysql-server +pkg-config [platform:dpkg test] +pkgconfig [platform:rpm test] +postgresql +postgresql-client [platform:dpkg] +postgresql-devel [platform:rpm test] +postgresql-server [platform:rpm] +python-dev [platform:dpkg test] +python-devel [platform:rpm test] +python3-all [platform:dpkg !platform:ubuntu-precise] +python3-all-dev [platform:dpkg !platform:ubuntu-precise] +python3-devel [platform:fedora] +python34-devel [platform:centos] +sqlite-devel [platform:rpm test] diff --git a/devstack/tempest-dsvm-cells-rc b/devstack/tempest-dsvm-cells-rc index 2eef8239fc9..4894ed195c0 100644 --- a/devstack/tempest-dsvm-cells-rc +++ b/devstack/tempest-dsvm-cells-rc @@ -25,6 +25,9 @@ # should be escaped. There is no need to specify .* at the end of a # pattern, as it's handled by the final match. +# Test idempotent ids are used for specific tests because +# these are unchanged if the test name changes. + # Construct a regex to use when limiting scope of tempest # to avoid features unsupported by Nova Cells. r="^(?!.*" @@ -32,9 +35,6 @@ r="^(?!.*" # skip security group tests r="$r(?:tempest\.api\.compute\.security_groups)" -# skip test that requires security groups -r="$r|(?:tempest\.thirdparty\.boto\.test_ec2_instance_run\.InstanceRunTest\.test_compute_with_volumes)" - # skip aggregates tests r="$r|(?:tempest\.api\.compute\.admin\.test_aggregates)" r="$r|(?:tempest\.scenario\.test_aggregates_basic_ops)" @@ -48,11 +48,16 @@ r="$r|(?:tempest\.api\.compute\.admin\.test_fixed_ips)" # skip floating-ip tests r="$r|(?:tempest\.api\.compute\.floating_ips)" # https://bugs.launchpad.net/tempest/+bug/1513983 - The follow scenario tests rely on Neutron but use floating IPs -r="$r|(?:tempest\.scenario\.test_network_advanced_server_ops\.TestNetworkAdvancedServerOps\.test_server_connectivity_pause_unpause)" -r="$r|(?:tempest\.scenario\.test_network_basic_ops\.TestNetworkBasicOps\.test_network_basic_ops)" -r="$r|(?:tempest\.scenario\.test_network_basic_ops\.TestNetworkBasicOps\.test_update_router_admin_state)" -r="$r|(?:tempest\.scenario\.test_network_v6\.TestGettingAddress\.test_slaac_from_os)" -r="$r|(?:tempest\.scenario\.test_security_groups_basic_ops\.TestSecurityGroupsBasicOps\.test_cross_tenant_traffic)" +# tempest.scenario.test_network_advanced_server_ops.TestNetworkAdvancedServerOps.test_server_connectivity_pause_unpause +r="$r|(?:.*id\-2b2642db\-6568\-4b35\-b812\-eceed3fa20ce.*)" +# tempest.scenario.test_network_basic_ops.TestNetworkBasicOps.test_network_basic_ops +r="$r|(?:.*id\-f323b3ba\-82f8\-4db7\-8ea6\-6a895869ec49.*)" +# tempest.scenario.test_network_basic_ops.TestNetworkBasicOps.test_update_router_admin_state +r="$r|(?:.*id\-04b9fe4e\-85e8\-4aea\-b937\-ea93885ac59f.*)" +# tempest.scenario.test_network_v6.TestGettingAddress.test_slaac_from_os +r="$r|(?:.*id\-2c92df61\-29f0\-4eaa\-bee3\-7c65bef62a43.*)" +# tempest.scenario.test_security_groups_basic_ops.TestSecurityGroupsBasicOps.test_cross_tenant_traffic +r="$r|(?:.*id\-e79f879e\-debb\-440c\-a7e4\-efeda05b6848.*)" # exclude the slow tag r="$r|(?:.*\[.*\bslow\b.*\])" @@ -62,30 +67,52 @@ r="$r|(?:.*\[.*\bslow\b.*\])" # NOTE(mriedem): Resize tests are skipped in devstack until custom flavors # in devstack used in Tempest runs are synced to the cells database. -r="$r|(?:tempest\.api\.compute\.admin\.test_networks\.NetworksTest\.test_get_network)" -r="$r|(?:tempest\.api\.compute\.admin\.test_networks\.NetworksTest\.test_list_all_networks)" -r="$r|(?:tempest\.api\.compute\.servers\.test_server_rescue\.ServerRescueTestJSON)" -r="$r|(?:tempest\.api\.compute\.servers\.test_create_server\.ServersTestJSON\.test_create_server_with_scheduler_hint_group)" -r="$r|(?:tempest\.api\.compute\.servers\.test_create_server\.ServersTestManualDisk\.test_create_server_with_scheduler_hint_group)" -r="$r|(?:tempest\.api\.compute\.servers\.test_virtual_interfaces\.VirtualInterfacesTestJSON\.test_list_virtual_interfaces)" -r="$r|(?:tempest\.api\.compute\.test_networks\.ComputeNetworksTest\.test_list_networks)" -r="$r|(?:tempest\.scenario\.test_minimum_basic\.TestMinimumBasicScenario\.test_minimum_basic_scenario)" -r="$r|(?:tempest\.api\.compute\.servers\.test_server_rescue_negative\.ServerRescueNegativeTestJSON)" -r="$r|(?:tempest\.scenario\.test_encrypted_cinder_volumes\.TestEncryptedCinderVolumes\.test_encrypted_cinder_volumes_cryptsetup)" -r="$r|(?:tempest\.scenario\.test_encrypted_cinder_volumes\.TestEncryptedCinderVolumes\.test_encrypted_cinder_volumes_luks)" -r="$r|(?:tempest\.thirdparty\.boto\.test_ec2_network\.EC2NetworkTest\.test_disassociate_not_associated_floating_ip)" -r="$r|(?:tempest\.scenario\.test_server_basic_ops\.TestServerBasicOps\.(test_server_basicops|test_server_basic_ops))" -r="$r|(?:tempest\.scenario\.test_snapshot_pattern\.TestSnapshotPattern\.test_snapshot_pattern)" -r="$r|(?:tempest\.api\.compute\.admin\.test_hosts\.HostsAdminTestJSON\.test_show_host_detail)" -r="$r|(?:tempest\.api\.compute\.test_tenant_networks\.ComputeTenantNetworksTest\.test_list_show_tenant_networks)" +# NOTE(mriedem): Rescue tests are skipped in devstack. They rely on floating +# IPs and security groups, and rescue might not work with cells v1 anyway due +# to synchronization issues. + +# tempest.api.compute.admin.test_networks.NetworksTest.test_get_network)" +r="$r|(?:.*id\-d206d211\-8912\-486f\-86e2\-a9d090d1f416.*)" +# tempest.api.compute.admin.test_networks.NetworksTest.test_list_all_networks)" +r="$r|(?:.*id\-df3d1046\-6fa5\-4b2c\-ad0c\-cfa46a351cb9.*)" +# tempest.api.compute.servers.test_create_server.ServersTestJSON.test_create_server_with_scheduler_hint_group +r="$r|(?:.*id\-ed20d3fb\-9d1f\-4329\-b160\-543fbd5d9811.*)" +# tempest.api.compute.servers.test_virtual_interfaces.VirtualInterfacesTestJSON.test_list_virtual_interfaces +r="$r|(?:.*id\-96c4e2ef\-5e4d\-4d7f\-87f5\-fed6dca18016.*)" +# tempest.api.compute.test_networks.ComputeNetworksTest.test_list_networks +r="$r|(?:.*id\-3fe07175\-312e\-49a5\-a623\-5f52eeada4c2.*)" +# tempest.scenario.test_minimum_basic.TestMinimumBasicScenario.test_minimum_basic_scenario +r="$r|(?:.*id\-bdbb5441\-9204\-419d\-a225\-b4fdbfb1a1a8.*)" +# tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_cryptsetup +r="$r|(?:.*id\-cbc752ed\-b716\-4717\-910f\-956cce965722.*)" +# tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_luks +r="$r|(?:.*id\-79165fb4\-5534\-4b9d\-8429\-97ccffb8f86e.*)" +# tempest.scenario.test_server_basic_ops.TestServerBasicOps.test_server_basicops +r="$r|(?:.*id\-7fff3fb3\-91d8\-4fd0\-bd7d\-0204f1f180ba.*)" +# tempest.scenario.test_snapshot_pattern.TestSnapshotPattern.test_snapshot_pattern +r="$r|(?:.*id\-608e604b\-1d63\-4a82\-8e3e\-91bc665c90b4.*)" +# tempest.api.compute.admin.test_hosts.HostsAdminTestJSON.test_show_host_detail +r="$r|(?:.*id\-38adbb12\-aee2\-4498\-8aec\-329c72423aa4.*)" +# tempest.api.compute.test_tenant_networks.ComputeTenantNetworksTest.test_list_show_tenant_networks +r="$r|(?:.*id\-edfea98e\-bbe3\-4c7a\-9739\-87b986baff26.*)" # https://bugs.launchpad.net/nova/+bug/1489581 r="$r|(?:tempest\.scenario\.test_volume_boot_pattern\.)" -# https://bugs.launchpad.net/nova/+bug/1445628 -r="$r|(?:tempest\.thirdparty\.boto\.test_ec2_instance_run\.InstanceRunTest\.test_run_idempotent_instances)" # https://bugs.launchpad.net/nova/+bug/1466696 - Cells: Race between instance 'unlock' and 'stop' can cause 'stop' to fail -r="$r|(?:tempest\.api\.compute\.servers\.test_server_actions\.ServerActionsTestJSON\.test_lock_unlock_server)" +# tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_lock_unlock_server +r="$r|(?:.*id\-80a8094c\-211e\-440a\-ab88\-9e59d556c7ee.*)" # scheduler hints apparently don't work in devstack cells -r="$r|(?:tempest\.scenario\.test_server_multinode\.TestServerMultinode\.test_schedule_to_all_nodes)" +# tempest.scenario.test_server_multinode.TestServerMultinode.test_schedule_to_all_nodes +r="$r|(?:.*id\-9cecbe35\-b9d4\-48da\-a37e\-7ce70aa43d30.*)" +# test_stamp_pattern uses security groups which aren't supported in cells v1 +# tempest.scenario.test_stamp_pattern.TestStampPattern.test_stamp_pattern +r="$r|(?:.*id\-10fd234a\-515c\-41e5\-b092\-8323060598c5.*)" +# Bug 1709985: rebuild randomly times out, probably due to sync issues +# tempest.api.compute.admin.test_servers.ServersAdminTestJSON.test_rebuild_server_in_error_state +r="$r|(?:.*id\-682cb127\-e5bb\-4f53\-87ce\-cb9003604442.*)" r="$r).*$" export DEVSTACK_GATE_TEMPEST_REGEX="$r" + +# Don't run the cells v1 job with ssh validation since it uses floating IPs +# by default which cells v1 doesn't support. +export DEVSTACK_LOCAL_CONFIG="TEMPEST_RUN_VALIDATION=False" diff --git a/devstack/tempest-dsvm-lvm-rc b/devstack/tempest-dsvm-lvm-rc new file mode 100644 index 00000000000..ba6aef9f670 --- /dev/null +++ b/devstack/tempest-dsvm-lvm-rc @@ -0,0 +1,55 @@ +# Copyright 2016 IBM Corp. +# +# 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. + +# +# This script is executed in the OpenStack CI *tempest-dsvm-lvm job. +# It's used to configure which tempest tests actually get run. You can find +# the CI job configuration here: +# +# http://git.openstack.org/cgit/openstack-infra/project-config/tree/jenkins/jobs/lvm.yaml +# + +# Construct a regex to use when limiting scope of tempest +# to avoid features unsupported by Nova's LVM support. + +# Note that several tests are disabled by the use of tempest +# feature toggles in devstack/lib/tempest for an lvm config, +# so this regex is not entirely representative of what's excluded. + +# When adding entries to the regex, add a comment explaining why +# since this list should not grow. + +r="^(?!.*" +r="$r(?:.*\[.*\bslow\b.*\])" + +# NOTE(mriedem): resize of non-volume-backed lvm instances does not yet work +# tempest.api.compute.admin.test_migrations.MigrationsAdminTest.test_list_migrations_in_flavor_resize_situation +r="$r|(?:.*id\-1b512062\-8093\-438e\-b47a\-37d2f597cd64.*)" +# tempest.api.compute.admin.test_migrations.MigrationsAdminTest.test_resize_server_revert_deleted_flavor +r="$r|(?:.*id\-33f1fec3\-ba18\-4470\-8e4e\-1d888e7c3593.*)" +# tempest.api.compute.servers.test_delete_server.DeleteServersTestJSON.test_delete_server_while_in_verify_resize_state +r="$r|(?:.*id\-ab0c38b4\-cdd8\-49d3\-9b92\-0cb898723c01.*)" +# tempest.api.compute.servers.test_disk_config.ServerDiskConfigTestJSON.test_resize_server_from_auto_to_manual +r="$r|(?:.*id\-693d16f3\-556c\-489a\-8bac\-3d0ca2490bad.*)" +# tempest.api.compute.servers.test_disk_config.ServerDiskConfigTestJSON.test_resize_server_from_manual_to_auto +r="$r|(?:.*id\-414e7e93\-45b5\-44bc\-8e03\-55159c6bfc97.*)" +# tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_confirm +r="$r|(?:.*id\-1499262a\-9328\-4eda\-9068\-db1ac57498d2.*)" +# tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_confirm_from_stopped +r="$r|(?:.*id\-138b131d\-66df\-48c9\-a171\-64f45eb92962.*)" +# tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_revert +r="$r|(?:.*id\-c03aab19\-adb1\-44f5\-917d\-c419577e9e68.*)" +r="$r).*$" + +export DEVSTACK_GATE_TEMPEST_REGEX="$r" diff --git a/devstack/tempest-dsvm-tempest-xen-rc b/devstack/tempest-dsvm-tempest-xen-rc new file mode 100644 index 00000000000..669c7054674 --- /dev/null +++ b/devstack/tempest-dsvm-tempest-xen-rc @@ -0,0 +1,40 @@ +# 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. + +# +# This script is executed in the Xen Project OpenStack CI dsvm-tempest-xen job. +# It's used to configure which tempest tests actually get run. You can find +# the CI job configuration here: +# +# https://xenbits.xen.org/gitweb/?p=openstack/ci-loop-config.git;a=blob;f=jenkins/jobs/jobs.yaml;hb=HEAD +# + +# When adding entries to the regex, add a comment explaining why +# since this list should not grow. + +# exclude the slow tag +r="\[.*\bslow\b.*\]" + +# volume_swap fail +# https://bugs.launchpad.net/nova/+bug/1676499 +r="$r|tempest\.api\.compute\.admin\.test_volume_swap\.TestVolumeSwap\.test_volume_swap" + +# Because paused guest can not be snapshot +# https://bugs.launchpad.net/nova/+bug/1675787 +r="$r|tempest\.api\.compute\.images\.test_images\.ImagesTestJSON\.test_create_image_from_paused_server" + +# Cannot boot from encrypted volume +# https://bugs.launchpad.net/nova/+bug/1702897 +r="$r|tempest\.scenario\.test_volume_boot_pattern\.TestVolumeBootPattern\.test_boot_server_from_encrypted_volume_luks" + +r="^(?!.*(?:$r))(?:^tempest\.(?:api|scenario|thirdparty))" +export DEVSTACK_GATE_TEMPEST_REGEX="$r" diff --git a/doc/api_samples/all_extensions/extensions-list-resp-v2.json b/doc/api_samples/all_extensions/extensions-list-resp-v2.json deleted file mode 100644 index ec9a4547d46..00000000000 --- a/doc/api_samples/all_extensions/extensions-list-resp-v2.json +++ /dev/null @@ -1,724 +0,0 @@ -{ - "extensions": [ - { - "alias": "NMN", - "description": "Multiple network support.", - "links": [], - "name": "Multinic", - "namespace": "http://docs.openstack.org/compute/ext/multinic/api/v1.1", - "updated": "2011-06-09T00:00:00Z" - }, - { - "alias": "OS-DCF", - "description": "Disk Management Extension.", - "links": [], - "name": "DiskConfig", - "namespace": "http://docs.openstack.org/compute/ext/disk_config/api/v1.1", - "updated": "2011-09-27T00:00:00Z" - }, - { - "alias": "OS-EXT-AZ", - "description": "Extended Availability Zone support.", - "links": [], - "name": "ExtendedAvailabilityZone", - "namespace": "http://docs.openstack.org/compute/ext/extended_availability_zone/api/v2", - "updated": "2013-01-30T00:00:00Z" - }, - { - "alias": "OS-EXT-IMG-SIZE", - "description": "Adds image size to image listings.", - "links": [], - "name": "ImageSize", - "namespace": "http://docs.openstack.org/compute/ext/image_size/api/v1.1", - "updated": "2013-02-19T00:00:00Z" - }, - { - "alias": "OS-EXT-IPS", - "description": "Adds type parameter to the ip list.", - "links": [], - "name": "ExtendedIps", - "namespace": "http://docs.openstack.org/compute/ext/extended_ips/api/v1.1", - "updated": "2013-01-06T00:00:00Z" - }, - { - "alias": "OS-EXT-IPS-MAC", - "description": "Adds mac address parameter to the ip list.", - "links": [], - "name": "ExtendedIpsMac", - "namespace": "http://docs.openstack.org/compute/ext/extended_ips_mac/api/v1.1", - "updated": "2013-03-07T00:00:00Z" - }, - { - "alias": "OS-EXT-SRV-ATTR", - "description": "Extended Server Attributes support.", - "links": [], - "name": "ExtendedServerAttributes", - "namespace": "http://docs.openstack.org/compute/ext/extended_status/api/v1.1", - "updated": "2011-11-03T00:00:00Z" - }, - { - "alias": "OS-EXT-STS", - "description": "Extended Status support.", - "links": [], - "name": "ExtendedStatus", - "namespace": "http://docs.openstack.org/compute/ext/extended_status/api/v1.1", - "updated": "2011-11-03T00:00:00Z" - }, - { - "alias": "OS-EXT-VIF-NET", - "description": "Adds network id parameter to the virtual interface list.", - "links": [], - "name": "ExtendedVIFNet", - "namespace": "http://docs.openstack.org/compute/ext/extended-virtual-interfaces-net/api/v1.1", - "updated": "2013-03-07T00:00:00Z" - }, - { - "alias": "OS-FLV-DISABLED", - "description": "Support to show the disabled status of a flavor.", - "links": [], - "name": "FlavorDisabled", - "namespace": "http://docs.openstack.org/compute/ext/flavor_disabled/api/v1.1", - "updated": "2012-08-29T00:00:00Z" - }, - { - "alias": "OS-FLV-EXT-DATA", - "description": "Provide additional data for flavors.", - "links": [], - "name": "FlavorExtraData", - "namespace": "http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1", - "updated": "2011-09-14T00:00:00Z" - }, - { - "alias": "OS-SCH-HNT", - "description": "Pass arbitrary key/value pairs to the scheduler.", - "links": [], - "name": "SchedulerHints", - "namespace": "http://docs.openstack.org/compute/ext/scheduler-hints/api/v2", - "updated": "2011-07-19T00:00:00Z" - }, - { - "alias": "OS-SRV-USG", - "description": "Adds launched_at and terminated_at on Servers.", - "links": [], - "name": "ServerUsage", - "namespace": "http://docs.openstack.org/compute/ext/server_usage/api/v1.1", - "updated": "2013-04-29T00:00:00Z" - }, - { - "alias": "os-admin-actions", - "description": "Enable admin-only server actions\n\n Actions include: pause, unpause, suspend, resume, migrate,\n resetNetwork, injectNetworkInfo, lock, unlock, createBackup\n ", - "links": [], - "name": "AdminActions", - "namespace": "http://docs.openstack.org/compute/ext/admin-actions/api/v1.1", - "updated": "2011-09-20T00:00:00Z" - }, - { - "alias": "os-agents", - "description": "Agents support.", - "links": [], - "name": "Agents", - "namespace": "http://docs.openstack.org/compute/ext/agents/api/v2", - "updated": "2012-10-28T00:00:00Z" - }, - { - "alias": "os-aggregates", - "description": "Admin-only aggregate administration.", - "links": [], - "name": "Aggregates", - "namespace": "http://docs.openstack.org/compute/ext/aggregates/api/v1.1", - "updated": "2012-01-12T00:00:00Z" - }, - { - "alias": "os-assisted-volume-snapshots", - "description": "Assisted volume snapshots.", - "links": [], - "name": "AssistedVolumeSnapshots", - "namespace": "http://docs.openstack.org/compute/ext/assisted-volume-snapshots/api/v2", - "updated": "2013-08-29T00:00:00Z" - }, - { - "alias": "os-attach-interfaces", - "description": "Attach interface support.", - "links": [], - "name": "AttachInterfaces", - "namespace": "http://docs.openstack.org/compute/ext/interfaces/api/v1.1", - "updated": "2012-07-22T00:00:00Z" - }, - { - "alias": "os-availability-zone", - "description": "1. Add availability_zone to the Create Server v1.1 API.\n 2. Add availability zones describing.\n ", - "links": [], - "name": "AvailabilityZone", - "namespace": "http://docs.openstack.org/compute/ext/availabilityzone/api/v1.1", - "updated": "2012-12-21T00:00:00Z" - }, - { - "alias": "os-baremetal-ext-status", - "description": "Add extended status in Baremetal Nodes v2 API.", - "links": [], - "name": "BareMetalExtStatus", - "namespace": "http://docs.openstack.org/compute/ext/baremetal_ext_status/api/v2", - "updated": "2013-08-27T00:00:00Z" - }, - { - "alias": "os-baremetal-nodes", - "description": "Admin-only bare-metal node administration.", - "links": [], - "name": "BareMetalNodes", - "namespace": "http://docs.openstack.org/compute/ext/baremetal_nodes/api/v2", - "updated": "2013-01-04T00:00:00Z" - }, - { - "alias": "os-block-device-mapping-v2-boot", - "description": "Allow boot with the new BDM data format.", - "links": [], - "name": "BlockDeviceMappingV2Boot", - "namespace": "http://docs.openstack.org/compute/ext/block_device_mapping_v2_boot/api/v2", - "updated": "2013-07-08T00:00:00Z" - }, - { - "alias": "os-cell-capacities", - "description": "Adding functionality to get cell capacities.", - "links": [], - "name": "CellCapacities", - "namespace": "http://docs.openstack.org/compute/ext/cell_capacities/api/v1.1", - "updated": "2013-05-27T00:00:00Z" - }, - { - "alias": "os-cells", - "description": "Enables cells-related functionality such as adding neighbor cells,\n listing neighbor cells, and getting the capabilities of the local cell.\n ", - "links": [], - "name": "Cells", - "namespace": "http://docs.openstack.org/compute/ext/cells/api/v1.1", - "updated": "2013-05-14T00:00:00Z" - }, - { - "alias": "os-certificates", - "description": "Certificates support.", - "links": [], - "name": "Certificates", - "namespace": "http://docs.openstack.org/compute/ext/certificates/api/v1.1", - "updated": "2012-01-19T00:00:00Z" - }, - { - "alias": "os-cloudpipe", - "description": "Adds actions to create cloudpipe instances.\n\n When running with the Vlan network mode, you need a mechanism to route\n from the public Internet to your vlans. This mechanism is known as a\n cloudpipe.\n\n At the time of creating this class, only OpenVPN is supported. Support for\n a SSH Bastion host is forthcoming.\n ", - "links": [], - "name": "Cloudpipe", - "namespace": "http://docs.openstack.org/compute/ext/cloudpipe/api/v1.1", - "updated": "2011-12-16T00:00:00Z" - }, - { - "alias": "os-cloudpipe-update", - "description": "Adds the ability to set the vpn ip/port for cloudpipe instances.", - "links": [], - "name": "CloudpipeUpdate", - "namespace": "http://docs.openstack.org/compute/ext/cloudpipe-update/api/v2", - "updated": "2012-11-14T00:00:00Z" - }, - { - "alias": "os-config-drive", - "description": "Config Drive Extension.", - "links": [], - "name": "ConfigDrive", - "namespace": "http://docs.openstack.org/compute/ext/config_drive/api/v1.1", - "updated": "2012-07-16T00:00:00Z" - }, - { - "alias": "os-console-auth-tokens", - "description": "Console token authentication support.", - "links": [], - "name": "ConsoleAuthTokens", - "namespace": "http://docs.openstack.org/compute/ext/consoles-auth-tokens/api/v2", - "updated": "2013-08-13T00:00:00Z" - }, - { - "alias": "os-console-output", - "description": "Console log output support, with tailing ability.", - "links": [], - "name": "ConsoleOutput", - "namespace": "http://docs.openstack.org/compute/ext/os-console-output/api/v2", - "updated": "2011-12-08T00:00:00Z" - }, - { - "alias": "os-consoles", - "description": "Interactive Console support.", - "links": [], - "name": "Consoles", - "namespace": "http://docs.openstack.org/compute/ext/os-consoles/api/v2", - "updated": "2011-12-23T00:00:00Z" - }, - { - "alias": "os-create-server-ext", - "description": "Extended support to the Create Server v1.1 API.", - "links": [], - "name": "Createserverext", - "namespace": "http://docs.openstack.org/compute/ext/createserverext/api/v1.1", - "updated": "2011-07-19T00:00:00Z" - }, - { - "alias": "os-deferred-delete", - "description": "Instance deferred delete.", - "links": [], - "name": "DeferredDelete", - "namespace": "http://docs.openstack.org/compute/ext/deferred-delete/api/v1.1", - "updated": "2011-09-01T00:00:00Z" - }, - { - "alias": "os-evacuate", - "description": "Enables server evacuation.", - "links": [], - "name": "Evacuate", - "namespace": "http://docs.openstack.org/compute/ext/evacuate/api/v2", - "updated": "2013-01-06T00:00:00Z" - }, - { - "alias": "os-extended-evacuate-find-host", - "description": "Enables server evacuation without target host. Scheduler will select\n one to target.\n ", - "links": [], - "name": "ExtendedEvacuateFindHost", - "namespace": "http://docs.openstack.org/compute/ext/extended_evacuate_find_host/api/v2", - "updated": "2014-02-12T00:00:00Z" - }, - { - "alias": "os-extended-floating-ips", - "description": "Adds optional fixed_address to the add floating IP command.", - "links": [], - "name": "ExtendedFloatingIps", - "namespace": "http://docs.openstack.org/compute/ext/extended_floating_ips/api/v2", - "updated": "2013-04-19T00:00:00Z" - }, - { - "alias": "os-extended-hypervisors", - "description": "Extended hypervisors support.", - "links": [], - "name": "ExtendedHypervisors", - "namespace": "http://docs.openstack.org/compute/ext/extended_hypervisors/api/v1.1", - "updated": "2014-01-04T00:00:00Z" - }, - { - "alias": "os-extended-networks", - "description": "Adds additional fields to networks", - "links": [], - "name": "ExtendedNetworks", - "namespace": "http://docs.openstack.org/compute/ext/extended_networks/api/v2", - "updated": "2014-05-09T00:00:00Z" - }, - { - "alias": "os-extended-quotas", - "description": "Adds ability for admins to delete quota\n and optionally force the update Quota command.\n ", - "links": [], - "name": "ExtendedQuotas", - "namespace": "http://docs.openstack.org/compute/ext/extended_quotas/api/v1.1", - "updated": "2013-06-09T00:00:00Z" - }, - { - "alias": "os-extended-rescue-with-image", - "description": "Allow the user to specify the image to use for rescue.", - "links": [], - "name": "ExtendedRescueWithImage", - "namespace": "http://docs.openstack.org/compute/ext/extended_rescue_with_image/api/v2", - "updated": "2014-01-04T00:00:00Z" - }, - { - "alias": "os-hypervisor-status", - "description": "Show hypervisor status.", - "links": [], - "name": "HypervisorStatus", - "namespace": "http://docs.openstack.org/compute/ext/hypervisor_status/api/v1.1", - "updated": "2014-04-17T00:00:00Z" - }, - { - "alias": "os-extended-services", - "description": "Extended services support.", - "links": [], - "name": "ExtendedServices", - "namespace": "http://docs.openstack.org/compute/ext/extended_services/api/v2", - "updated": "2013-05-17T00:00:00Z" - }, - { - "alias": "os-extended-services-delete", - "description": "Extended services deletion support.", - "links": [], - "name": "ExtendedServicesDelete", - "namespace": "http://docs.openstack.org/compute/ext/extended_services_delete/api/v2", - "updated": "2013-12-10T00:00:00Z" - }, - { - "alias": "os-extended-volumes", - "description": "Extended Volumes support.", - "links": [], - "name": "ExtendedVolumes", - "namespace": "http://docs.openstack.org/compute/ext/extended_volumes/api/v1.1", - "updated": "2013-06-07T00:00:00Z" - }, - { - "alias": "os-fixed-ips", - "description": "Fixed IPs support.", - "links": [], - "name": "FixedIPs", - "namespace": "http://docs.openstack.org/compute/ext/fixed_ips/api/v2", - "updated": "2012-10-18T19:25:27Z" - }, - { - "alias": "os-flavor-access", - "description": "Flavor access support.", - "links": [], - "name": "FlavorAccess", - "namespace": "http://docs.openstack.org/compute/ext/flavor_access/api/v2", - "updated": "2012-08-01T00:00:00Z" - }, - { - "alias": "os-flavor-extra-specs", - "description": "Instance type (flavor) extra specs.", - "links": [], - "name": "FlavorExtraSpecs", - "namespace": "http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1", - "updated": "2011-06-23T00:00:00Z" - }, - { - "alias": "os-flavor-manage", - "description": "Flavor create/delete API support.", - "links": [], - "name": "FlavorManage", - "namespace": "http://docs.openstack.org/compute/ext/flavor_manage/api/v1.1", - "updated": "2012-01-19T00:00:00Z" - }, - { - "alias": "os-flavor-rxtx", - "description": "Support to show the rxtx status of a flavor.", - "links": [], - "name": "FlavorRxtx", - "namespace": "http://docs.openstack.org/compute/ext/flavor_rxtx/api/v1.1", - "updated": "2012-08-29T00:00:00Z" - }, - { - "alias": "os-flavor-swap", - "description": "Support to show the swap status of a flavor.", - "links": [], - "name": "FlavorSwap", - "namespace": "http://docs.openstack.org/compute/ext/flavor_swap/api/v1.1", - "updated": "2012-08-29T00:00:00Z" - }, - { - "alias": "os-floating-ip-dns", - "description": "Floating IP DNS support.", - "links": [], - "name": "FloatingIpDns", - "namespace": "http://docs.openstack.org/ext/floating_ip_dns/api/v1.1", - "updated": "2011-12-23T00:00:00Z" - }, - { - "alias": "os-floating-ip-pools", - "description": "Floating IPs support.", - "links": [], - "name": "FloatingIpPools", - "namespace": "http://docs.openstack.org/compute/ext/floating_ip_pools/api/v1.1", - "updated": "2012-01-04T00:00:00Z" - }, - { - "alias": "os-floating-ips", - "description": "Floating IPs support.", - "links": [], - "name": "FloatingIps", - "namespace": "http://docs.openstack.org/compute/ext/floating_ips/api/v1.1", - "updated": "2011-06-16T00:00:00Z" - }, - { - "alias": "os-floating-ips-bulk", - "description": "Bulk handling of Floating IPs.", - "links": [], - "name": "FloatingIpsBulk", - "namespace": "http://docs.openstack.org/compute/ext/floating_ips_bulk/api/v2", - "updated": "2012-10-29T19:25:27Z" - }, - { - "alias": "os-fping", - "description": "Fping Management Extension.", - "links": [], - "name": "Fping", - "namespace": "http://docs.openstack.org/compute/ext/fping/api/v1.1", - "updated": "2012-07-06T00:00:00Z" - }, - { - "alias": "os-hide-server-addresses", - "description": "Support hiding server addresses in certain states.", - "links": [], - "name": "HideServerAddresses", - "namespace": "http://docs.openstack.org/compute/ext/hide_server_addresses/api/v1.1", - "updated": "2012-12-11T00:00:00Z" - }, - { - "alias": "os-hosts", - "description": "Admin-only host administration.", - "links": [], - "name": "Hosts", - "namespace": "http://docs.openstack.org/compute/ext/hosts/api/v1.1", - "updated": "2011-06-29T00:00:00Z" - }, - { - "alias": "os-hypervisors", - "description": "Admin-only hypervisor administration.", - "links": [], - "name": "Hypervisors", - "namespace": "http://docs.openstack.org/compute/ext/hypervisors/api/v1.1", - "updated": "2012-06-21T00:00:00Z" - }, - { - "alias": "os-instance-actions", - "description": "View a log of actions and events taken on an instance.", - "links": [], - "name": "InstanceActions", - "namespace": "http://docs.openstack.org/compute/ext/instance-actions/api/v1.1", - "updated": "2013-02-08T00:00:00Z" - }, - { - "alias": "os-instance_usage_audit_log", - "description": "Admin-only Task Log Monitoring.", - "links": [], - "name": "OSInstanceUsageAuditLog", - "namespace": "http://docs.openstack.org/ext/services/api/v1.1", - "updated": "2012-07-06T01:00:00Z" - }, - { - "alias": "os-keypairs", - "description": "Keypair Support.", - "links": [], - "name": "Keypairs", - "namespace": "http://docs.openstack.org/compute/ext/keypairs/api/v1.1", - "updated": "2011-08-08T00:00:00Z" - }, - { - "alias": "os-migrations", - "description": "Provide data on migrations.", - "links": [], - "name": "Migrations", - "namespace": "http://docs.openstack.org/compute/ext/migrations/api/v2.0", - "updated": "2013-05-30T00:00:00Z" - }, - { - "alias": "os-multiple-create", - "description": "Allow multiple create in the Create Server v1.1 API.", - "links": [], - "name": "MultipleCreate", - "namespace": "http://docs.openstack.org/compute/ext/multiplecreate/api/v1.1", - "updated": "2012-08-07T00:00:00Z" - }, - { - "alias": "os-networks", - "description": "Admin-only Network Management Extension.", - "links": [], - "name": "Networks", - "namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1", - "updated": "2011-12-23T00:00:00Z" - }, - { - "alias": "os-networks-associate", - "description": "Network association support.", - "links": [], - "name": "NetworkAssociationSupport", - "namespace": "http://docs.openstack.org/compute/ext/networks_associate/api/v2", - "updated": "2012-11-19T00:00:00Z" - }, - { - "alias": "os-preserve-ephemeral-rebuild", - "description": "Allow preservation of the ephemeral partition on rebuild.", - "links": [], - "name": "PreserveEphemeralOnRebuild", - "namespace": "http://docs.openstack.org/compute/ext/preserve_ephemeral_rebuild/api/v2", - "updated": "2013-12-17T00:00:00Z" - }, - { - "alias": "os-quota-class-sets", - "description": "Quota classes management support.", - "links": [], - "name": "QuotaClasses", - "namespace": "http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1", - "updated": "2012-03-12T00:00:00Z" - }, - { - "alias": "os-quota-sets", - "description": "Quotas management support.", - "links": [], - "name": "Quotas", - "namespace": "http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1", - "updated": "2011-08-08T00:00:00Z" - }, - { - "alias": "os-rescue", - "description": "Instance rescue mode.", - "links": [], - "name": "Rescue", - "namespace": "http://docs.openstack.org/compute/ext/rescue/api/v1.1", - "updated": "2011-08-18T00:00:00Z" - }, - { - "alias": "os-security-group-default-rules", - "description": "Default rules for security group support.", - "links": [], - "name": "SecurityGroupDefaultRules", - "namespace": "http://docs.openstack.org/compute/ext/securitygroupdefaultrules/api/v1.1", - "updated": "2013-02-05T00:00:00Z" - }, - { - "alias": "os-security-groups", - "description": "Security group support.", - "links": [], - "name": "SecurityGroups", - "namespace": "http://docs.openstack.org/compute/ext/securitygroups/api/v1.1", - "updated": "2013-05-28T00:00:00Z" - }, - { - "alias": "os-server-diagnostics", - "description": "Allow Admins to view server diagnostics through server action.", - "links": [], - "name": "ServerDiagnostics", - "namespace": "http://docs.openstack.org/compute/ext/server-diagnostics/api/v1.1", - "updated": "2011-12-21T00:00:00Z" - }, - { - "alias": "os-server-external-events", - "description": "Server External Event Triggers.", - "links": [], - "name": "ServerExternalEvents", - "namespace": "http://docs.openstack.org/compute/ext/server-external-events/api/v2", - "updated": "2014-02-18T00:00:00Z" - }, - { - "alias": "os-server-groups", - "description": "Server group support.", - "links": [], - "name": "ServerGroups", - "namespace": "http://docs.openstack.org/compute/ext/servergroups/api/v2", - "updated": "2013-06-20T00:00:00Z" - }, - { - "alias": "os-server-group-quotas", - "description": "Adds quota support to server groups.", - "links": [], - "name": "ServerGroupQuotas", - "namespace": "http://docs.openstack.org/compute/ext/server-group-quotas/api/v2", - "updated": "2014-07-25T00:00:00Z" - }, - { - "alias": "os-server-list-multi-status", - "description": "Allow to filter the servers by a set of status values.", - "links": [], - "name": "ServerListMultiStatus", - "namespace": "http://docs.openstack.org/compute/ext/os-server-list-multi-status/api/v2", - "updated": "2014-05-11T00:00:00Z" - }, - { - "alias": "os-server-password", - "description": "Server password support.", - "links": [], - "name": "ServerPassword", - "namespace": "http://docs.openstack.org/compute/ext/server-password/api/v2", - "updated": "2012-11-29T00:00:00Z" - }, - { - "alias": "os-server-start-stop", - "description": "Start/Stop instance compute API support.", - "links": [], - "name": "ServerStartStop", - "namespace": "http://docs.openstack.org/compute/ext/servers/api/v1.1", - "updated": "2012-01-23T00:00:00Z" - }, - { - "alias": "os-services", - "description": "Services support.", - "links": [], - "name": "Services", - "namespace": "http://docs.openstack.org/compute/ext/services/api/v2", - "updated": "2012-10-28T00:00:00Z" - }, - { - "alias": "os-shelve", - "description": "Instance shelve mode.", - "links": [], - "name": "Shelve", - "namespace": "http://docs.openstack.org/compute/ext/shelve/api/v1.1", - "updated": "2013-04-06T00:00:00Z" - }, - { - "alias": "os-simple-tenant-usage", - "description": "Simple tenant usage extension.", - "links": [], - "name": "SimpleTenantUsage", - "namespace": "http://docs.openstack.org/compute/ext/os-simple-tenant-usage/api/v1.1", - "updated": "2011-08-19T00:00:00Z" - }, - { - "alias": "os-tenant-networks", - "description": "Tenant-based Network Management Extension.", - "links": [], - "name": "OSTenantNetworks", - "namespace": "http://docs.openstack.org/compute/ext/os-tenant-networks/api/v2", - "updated": "2012-03-07T14:46:43Z" - }, - { - "alias": "os-used-limits", - "description": "Provide data on limited resources that are being used.", - "links": [], - "name": "UsedLimits", - "namespace": "http://docs.openstack.org/compute/ext/used_limits/api/v1.1", - "updated": "2012-07-13T00:00:00Z" - }, - { - "alias": "os-used-limits-for-admin", - "description": "Provide data to admin on limited resources used by other tenants.", - "links": [], - "name": "UsedLimitsForAdmin", - "namespace": "http://docs.openstack.org/compute/ext/used_limits_for_admin/api/v1.1", - "updated": "2013-05-02T00:00:00Z" - }, - { - "alias": "os-user-data", - "description": "Add user_data to the Create Server v1.1 API.", - "links": [], - "name": "UserData", - "namespace": "http://docs.openstack.org/compute/ext/userdata/api/v1.1", - "updated": "2012-08-07T00:00:00Z" - }, - { - "alias": "os-user-quotas", - "description": "Project user quota support.", - "links": [], - "name": "UserQuotas", - "namespace": "http://docs.openstack.org/compute/ext/user_quotas/api/v1.1", - "updated": "2013-07-18T00:00:00Z" - }, - { - "alias": "os-virtual-interfaces", - "description": "Virtual interface support.", - "links": [], - "name": "VirtualInterfaces", - "namespace": "http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1", - "updated": "2011-08-17T00:00:00Z" - }, - { - "alias": "os-volume-attachment-update", - "description": "Support for updating a volume attachment.", - "links": [], - "name": "VolumeAttachmentUpdate", - "namespace": "http://docs.openstack.org/compute/ext/os-volume-attachment-update/api/v2", - "updated": "2013-06-20T00:00:00Z" - }, - { - "alias": "os-volumes", - "description": "Volumes support.", - "links": [], - "name": "Volumes", - "namespace": "http://docs.openstack.org/compute/ext/volumes/api/v1.1", - "updated": "2011-03-25T00:00:00Z" - }, - { - "alias": "os-server-sort-keys", - "description": "Add sorting support in get Server v2 API.", - "links": [], - "name": "ServerSortKeys", - "namespace": "http://docs.openstack.org/compute/ext/server_sort_keys/api/v2", - "updated": "2014-05-22T00:00:00Z" - } - ] -} diff --git a/doc/api_samples/all_extensions/flavor-get-resp.json b/doc/api_samples/all_extensions/flavor-get-resp.json deleted file mode 100644 index ab159173370..00000000000 --- a/doc/api_samples/all_extensions/flavor-get-resp.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "flavor": { - "OS-FLV-DISABLED:disabled": false, - "OS-FLV-EXT-DATA:ephemeral": 0, - "disk": 1, - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/1", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ], - "name": "m1.tiny", - "os-flavor-access:is_public": true, - "ram": 512, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 1 - } -} diff --git a/doc/api_samples/all_extensions/flavors-detail-resp.json b/doc/api_samples/all_extensions/flavors-detail-resp.json deleted file mode 100644 index 9e8a4822a60..00000000000 --- a/doc/api_samples/all_extensions/flavors-detail-resp.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "flavors": [ - { - "OS-FLV-DISABLED:disabled": false, - "disk": 1, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/1", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ], - "name": "m1.tiny", - "ram": 512, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 1 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 20, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "2", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/2", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/2", - "rel": "bookmark" - } - ], - "name": "m1.small", - "ram": 2048, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 1 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 40, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "3", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/3", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/3", - "rel": "bookmark" - } - ], - "name": "m1.medium", - "ram": 4096, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 2 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 80, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "4", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/4", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/4", - "rel": "bookmark" - } - ], - "name": "m1.large", - "ram": 8192, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 4 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 160, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "5", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/5", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/5", - "rel": "bookmark" - } - ], - "name": "m1.xlarge", - "ram": 16384, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 8 - } - ] -} diff --git a/doc/api_samples/all_extensions/flavors-list-resp.json b/doc/api_samples/all_extensions/flavors-list-resp.json deleted file mode 100644 index c585f2bb524..00000000000 --- a/doc/api_samples/all_extensions/flavors-list-resp.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "flavors": [ - { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/1", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ], - "name": "m1.tiny" - }, - { - "id": "2", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/2", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/2", - "rel": "bookmark" - } - ], - "name": "m1.small" - }, - { - "id": "3", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/3", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/3", - "rel": "bookmark" - } - ], - "name": "m1.medium" - }, - { - "id": "4", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/4", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/4", - "rel": "bookmark" - } - ], - "name": "m1.large" - }, - { - "id": "5", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/5", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/5", - "rel": "bookmark" - } - ], - "name": "m1.xlarge" - } - ] -} \ No newline at end of file diff --git a/doc/api_samples/all_extensions/server-action-confirm-resize.json b/doc/api_samples/all_extensions/server-action-confirm-resize.json deleted file mode 100644 index 2940d2a3925..00000000000 --- a/doc/api_samples/all_extensions/server-action-confirm-resize.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "confirmResize" : null -} \ No newline at end of file diff --git a/doc/api_samples/all_extensions/server-action-reboot.json b/doc/api_samples/all_extensions/server-action-reboot.json deleted file mode 100644 index 3920ea7285e..00000000000 --- a/doc/api_samples/all_extensions/server-action-reboot.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "reboot" : { - "type" : "SOFT" - } -} \ No newline at end of file diff --git a/doc/api_samples/all_extensions/server-action-rebuild-resp.json b/doc/api_samples/all_extensions/server-action-rebuild-resp.json deleted file mode 100644 index e7d03272eef..00000000000 --- a/doc/api_samples/all_extensions/server-action-rebuild-resp.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "server": { - "OS-DCF:diskConfig": "AUTO", - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "version": 4 - } - ] - }, - "adminPass": "seekr3t", - "created": "2012-09-25T13:36:08Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "935dcd1019fd43814a1d2a6e9b320dcac352d3a02c69f8be7ba41002", - "id": "27568e59-cfb7-4283-a00e-4af933f2d539", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/27568e59-cfb7-4283-a00e-4af933f2d539", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/27568e59-cfb7-4283-a00e-4af933f2d539", - "rel": "bookmark" - } - ], - "metadata": { - "meta var": "meta val" - }, - "name": "foobar", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2012-09-25T13:36:09Z", - "user_id": "fake" - } -} \ No newline at end of file diff --git a/doc/api_samples/all_extensions/server-action-rebuild.json b/doc/api_samples/all_extensions/server-action-rebuild.json deleted file mode 100644 index f79d3b7f24f..00000000000 --- a/doc/api_samples/all_extensions/server-action-rebuild.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "rebuild" : { - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "name" : "foobar", - "adminPass" : "seekr3t", - "accessIPv4" : "1.2.3.4", - "accessIPv6" : "80fe::", - "metadata" : { - "meta var" : "meta val" - }, - "personality" : [ - { - "path" : "/etc/banner.txt", - "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" - } - ] - } -} diff --git a/doc/api_samples/all_extensions/server-action-resize.json b/doc/api_samples/all_extensions/server-action-resize.json deleted file mode 100644 index bdaa37a1763..00000000000 --- a/doc/api_samples/all_extensions/server-action-resize.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resize" : { - "flavorRef" : "2" - } -} \ No newline at end of file diff --git a/doc/api_samples/all_extensions/server-action-revert-resize.json b/doc/api_samples/all_extensions/server-action-revert-resize.json deleted file mode 100644 index 76c460242cc..00000000000 --- a/doc/api_samples/all_extensions/server-action-revert-resize.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "revertResize" : null -} \ No newline at end of file diff --git a/doc/api_samples/all_extensions/server-create-req.json b/doc/api_samples/all_extensions/server-create-req.json deleted file mode 100644 index 439f9b97acb..00000000000 --- a/doc/api_samples/all_extensions/server-create-req.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "server" : { - "name" : "new-server-test", - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef" : "1", - "metadata" : { - "My Server Name" : "Apache1" - } - } -} diff --git a/doc/api_samples/all_extensions/server-create-resp.json b/doc/api_samples/all_extensions/server-create-resp.json deleted file mode 100644 index 310507befd2..00000000000 --- a/doc/api_samples/all_extensions/server-create-resp.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "server": { - "OS-DCF:diskConfig": "AUTO", - "adminPass": "zPnp2GseTqG4", - "id": "8195065c-fea4-4d57-b93f-5c5c63fe90e8", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/8195065c-fea4-4d57-b93f-5c5c63fe90e8", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/8195065c-fea4-4d57-b93f-5c5c63fe90e8", - "rel": "bookmark" - } - ], - "security_groups": [ - { - "name": "default" - } - ] - } -} diff --git a/doc/api_samples/all_extensions/server-get-resp.json b/doc/api_samples/all_extensions/server-get-resp.json deleted file mode 100644 index 66c980b4952..00000000000 --- a/doc/api_samples/all_extensions/server-get-resp.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "server": { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-23T13:37:00Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "9cc36101a27c2a69c1a18241f6228454d9d7f466bd90c62db8e8b856", - "id": "f474386b-4fb6-4e1f-b1d5-d6bf4437f7d5", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f474386b-4fb6-4e1f-b1d5-d6bf4437f7d5", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f474386b-4fb6-4e1f-b1d5-d6bf4437f7d5", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "accessIPv4": "", - "accessIPv6": "", - "config_drive": "", - "OS-DCF:diskConfig": "AUTO", - "OS-EXT-AZ:availability_zone": "nova", - "OS-EXT-SRV-ATTR:host": "b8b357f7100d4391828f2177c922ef93", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", - "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", - "OS-EXT-STS:power_state": 1, - "OS-EXT-STS:task_state": null, - "OS-EXT-STS:vm_state": "active", - "os-extended-volumes:volumes_attached": [], - "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", - "OS-SRV-USG:terminated_at": null, - "progress": 0, - "security_groups": [ - { - "name": "default" - } - ], - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-10-31T07:31:30Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/all_extensions/servers-details-resp.json b/doc/api_samples/all_extensions/servers-details-resp.json deleted file mode 100644 index 9b87d0e5b85..00000000000 --- a/doc/api_samples/all_extensions/servers-details-resp.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "servers": [ - { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-23T13:53:12Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "f1e160ad2bf07084f3d3e0dfdd0795d80da18a60825322c15775c0dd", - "id": "9cbefc35-d372-40c5-88e2-9fda1b6ea12c", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/9cbefc35-d372-40c5-88e2-9fda1b6ea12c", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/9cbefc35-d372-40c5-88e2-9fda1b6ea12c", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "accessIPv4": "", - "accessIPv6": "", - "config_drive": "", - "OS-DCF:diskConfig": "AUTO", - "OS-EXT-AZ:availability_zone": "nova", - "OS-EXT-SRV-ATTR:host": "c3f14e9812ad496baf92ccfb3c61e15f", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", - "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", - "OS-EXT-STS:power_state": 1, - "OS-EXT-STS:task_state": null, - "OS-EXT-STS:vm_state": "active", - "os-extended-volumes:volumes_attached": [], - "OS-SRV-USG:launched_at": "2013-09-23T13:53:12.774549", - "OS-SRV-USG:terminated_at": null, - "progress": 0, - "security_groups": [ - { - "name": "default" - } - ], - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-10-31T06:32:32Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/all_extensions/servers-list-resp.json b/doc/api_samples/all_extensions/servers-list-resp.json deleted file mode 100644 index b6fa7e8d981..00000000000 --- a/doc/api_samples/all_extensions/servers-list-resp.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "servers": [ - { - "id": "a291599e-6de2-41a6-88df-c443ddcef70d", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/a291599e-6de2-41a6-88df-c443ddcef70d", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/a291599e-6de2-41a6-88df-c443ddcef70d", - "rel": "bookmark" - } - ], - "name": "new-server-test" - } - ] -} \ No newline at end of file diff --git a/doc/api_samples/extension-info/extensions-get-resp-v2.json b/doc/api_samples/extension-info/extensions-get-resp-v2.json deleted file mode 100644 index 538ca8c3cbb..00000000000 --- a/doc/api_samples/extension-info/extensions-get-resp-v2.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extension": { - "alias": "os-agents", - "description": "Agents support.", - "links": [], - "name": "Agents", - "namespace": "http://docs.openstack.org/compute/ext/agents/api/v2", - "updated": "2012-10-28T00:00:00Z" - } -} \ No newline at end of file diff --git a/doc/api_samples/all_extensions/extensions-list-resp-v21-compatible.json b/doc/api_samples/extension-info/extensions-list-resp-v21-compatible.json similarity index 93% rename from doc/api_samples/all_extensions/extensions-list-resp-v21-compatible.json rename to doc/api_samples/extension-info/extensions-list-resp-v21-compatible.json index 0b2afb60850..99b3ef360b1 100644 --- a/doc/api_samples/all_extensions/extensions-list-resp-v21-compatible.json +++ b/doc/api_samples/extension-info/extensions-list-resp-v21-compatible.json @@ -34,7 +34,7 @@ }, { "alias": "OS-EXT-IPS", - "description": "", + "description": "Adds type parameter to the ip list.", "links": [], "name": "ExtendedIps", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -42,7 +42,7 @@ }, { "alias": "OS-EXT-IPS-MAC", - "description": "", + "description": "Adds mac address parameter to the ip list.", "links": [], "name": "ExtendedIpsMac", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -58,7 +58,7 @@ }, { "alias": "OS-EXT-STS", - "description": "", + "description": "Extended Status support.", "links": [], "name": "ExtendedStatus", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -66,7 +66,7 @@ }, { "alias": "OS-EXT-VIF-NET", - "description": "", + "description": "Adds network id parameter to the virtual interface list.", "links": [], "name": "ExtendedVIFNet", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -74,7 +74,7 @@ }, { "alias": "OS-FLV-DISABLED", - "description": "", + "description": "Support to show the disabled status of a flavor.", "links": [], "name": "FlavorDisabled", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -82,7 +82,7 @@ }, { "alias": "OS-FLV-EXT-DATA", - "description": "", + "description": "Provide additional data for flavors.", "links": [], "name": "FlavorExtraData", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -170,7 +170,7 @@ }, { "alias": "os-baremetal-ext-status", - "description": "", + "description": "Add extended status in Baremetal Nodes v2 API.", "links": [], "name": "BareMetalExtStatus", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -194,7 +194,7 @@ }, { "alias": "os-block-device-mapping-v2-boot", - "description": "", + "description": "Allow boot with the new BDM data format.", "links": [], "name": "BlockDeviceMappingV2Boot", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -202,7 +202,7 @@ }, { "alias": "os-cell-capacities", - "description": "", + "description": "Adding functionality to get cell capacities.", "links": [], "name": "CellCapacities", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -234,7 +234,7 @@ }, { "alias": "os-cloudpipe-update", - "description": "", + "description": "Adds the ability to set the vpn ip/port for cloudpipe instances.", "links": [], "name": "CloudpipeUpdate", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -282,7 +282,7 @@ }, { "alias": "os-create-server-ext", - "description": "", + "description": "Extended support to the Create Server v1.1 API.", "links": [], "name": "Createserverext", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -306,7 +306,7 @@ }, { "alias": "os-extended-evacuate-find-host", - "description": "", + "description": "Enables server evacuation without target host. Scheduler will select one to target.", "links": [], "name": "ExtendedEvacuateFindHost", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -314,7 +314,7 @@ }, { "alias": "os-extended-floating-ips", - "description": "", + "description": "Adds optional fixed_address to the add floating IP command.", "links": [], "name": "ExtendedFloatingIps", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -322,7 +322,7 @@ }, { "alias": "os-extended-hypervisors", - "description": "", + "description": "Extended hypervisors support.", "links": [], "name": "ExtendedHypervisors", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -330,7 +330,7 @@ }, { "alias": "os-extended-networks", - "description": "", + "description": "Adds additional fields to networks.", "links": [], "name": "ExtendedNetworks", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -338,7 +338,7 @@ }, { "alias": "os-extended-quotas", - "description": "", + "description": "Adds ability for admins to delete quota and optionally force the update Quota command.", "links": [], "name": "ExtendedQuotas", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -346,7 +346,7 @@ }, { "alias": "os-extended-rescue-with-image", - "description": "", + "description": "Allow the user to specify the image to use for rescue.", "links": [], "name": "ExtendedRescueWithImage", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -354,7 +354,7 @@ }, { "alias": "os-extended-services", - "description": "", + "description": "Extended services support.", "links": [], "name": "ExtendedServices", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -362,7 +362,7 @@ }, { "alias": "os-extended-services-delete", - "description": "", + "description": "Extended services deletion support.", "links": [], "name": "ExtendedServicesDelete", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -426,7 +426,7 @@ }, { "alias": "os-flavor-swap", - "description": "", + "description": "Support to show the swap status of a flavor.", "links": [], "name": "FlavorSwap", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -490,7 +490,7 @@ }, { "alias": "os-hypervisor-status", - "description": "", + "description": "Show hypervisor status.", "links": [], "name": "HypervisorStatus", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -658,7 +658,7 @@ }, { "alias": "os-server-group-quotas", - "description": "", + "description": "Adds quota support to server groups.", "links": [], "name": "ServerGroupQuotas", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -674,7 +674,7 @@ }, { "alias": "os-server-list-multi-status", - "description": "", + "description": "Allow to filter the servers by a set of status values.", "links": [], "name": "ServerListMultiStatus", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -690,7 +690,7 @@ }, { "alias": "os-server-sort-keys", - "description": "", + "description": "Add sorting support in get Server v2 API.", "links": [], "name": "ServerSortKeys", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -698,7 +698,7 @@ }, { "alias": "os-server-start-stop", - "description": "", + "description": "Start/Stop instance compute API support.", "links": [], "name": "ServerStartStop", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -754,7 +754,7 @@ }, { "alias": "os-used-limits-for-admin", - "description": "", + "description": "Provide data to admin on limited resources used by other tenants.", "links": [], "name": "UsedLimitsForAdmin", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -770,7 +770,7 @@ }, { "alias": "os-user-quotas", - "description": "", + "description": "Project user quota support.", "links": [], "name": "UserQuotas", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -786,7 +786,7 @@ }, { "alias": "os-volume-attachment-update", - "description": "", + "description": "Support for updating a volume attachment.", "links": [], "name": "VolumeAttachmentUpdate", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", diff --git a/doc/api_samples/all_extensions/extensions-list-resp.json b/doc/api_samples/extension-info/extensions-list-resp.json similarity index 93% rename from doc/api_samples/all_extensions/extensions-list-resp.json rename to doc/api_samples/extension-info/extensions-list-resp.json index 9bfd5b0884c..81626d6e216 100644 --- a/doc/api_samples/all_extensions/extensions-list-resp.json +++ b/doc/api_samples/extension-info/extensions-list-resp.json @@ -34,7 +34,7 @@ }, { "alias": "OS-EXT-IPS", - "description": "", + "description": "Adds type parameter to the ip list.", "links": [], "name": "ExtendedIps", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -42,7 +42,7 @@ }, { "alias": "OS-EXT-IPS-MAC", - "description": "", + "description": "Adds mac address parameter to the ip list.", "links": [], "name": "ExtendedIpsMac", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -58,7 +58,7 @@ }, { "alias": "OS-EXT-STS", - "description": "", + "description": "Extended Status support.", "links": [], "name": "ExtendedStatus", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -66,7 +66,7 @@ }, { "alias": "OS-FLV-DISABLED", - "description": "", + "description": "Support to show the disabled status of a flavor.", "links": [], "name": "FlavorDisabled", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -74,7 +74,7 @@ }, { "alias": "OS-FLV-EXT-DATA", - "description": "", + "description": "Provide additional data for flavors.", "links": [], "name": "FlavorExtraData", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -162,7 +162,7 @@ }, { "alias": "os-baremetal-ext-status", - "description": "", + "description": "Add extended status in Baremetal Nodes v2 API.", "links": [], "name": "BareMetalExtStatus", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -186,7 +186,7 @@ }, { "alias": "os-block-device-mapping-v2-boot", - "description": "", + "description": "Allow boot with the new BDM data format.", "links": [], "name": "BlockDeviceMappingV2Boot", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -194,7 +194,7 @@ }, { "alias": "os-cell-capacities", - "description": "", + "description": "Adding functionality to get cell capacities.", "links": [], "name": "CellCapacities", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -226,7 +226,7 @@ }, { "alias": "os-cloudpipe-update", - "description": "", + "description": "Adds the ability to set the vpn ip/port for cloudpipe instances.", "links": [], "name": "CloudpipeUpdate", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -274,7 +274,7 @@ }, { "alias": "os-create-server-ext", - "description": "", + "description": "Extended support to the Create Server v1.1 API.", "links": [], "name": "Createserverext", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -298,7 +298,7 @@ }, { "alias": "os-extended-evacuate-find-host", - "description": "", + "description": "Enables server evacuation without target host. Scheduler will select one to target.", "links": [], "name": "ExtendedEvacuateFindHost", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -306,7 +306,7 @@ }, { "alias": "os-extended-floating-ips", - "description": "", + "description": "Adds optional fixed_address to the add floating IP command.", "links": [], "name": "ExtendedFloatingIps", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -314,7 +314,7 @@ }, { "alias": "os-extended-hypervisors", - "description": "", + "description": "Extended hypervisors support.", "links": [], "name": "ExtendedHypervisors", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -322,7 +322,7 @@ }, { "alias": "os-extended-networks", - "description": "", + "description": "Adds additional fields to networks.", "links": [], "name": "ExtendedNetworks", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -330,7 +330,7 @@ }, { "alias": "os-extended-quotas", - "description": "", + "description": "Adds ability for admins to delete quota and optionally force the update Quota command.", "links": [], "name": "ExtendedQuotas", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -338,7 +338,7 @@ }, { "alias": "os-extended-rescue-with-image", - "description": "", + "description": "Allow the user to specify the image to use for rescue.", "links": [], "name": "ExtendedRescueWithImage", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -346,7 +346,7 @@ }, { "alias": "os-extended-services", - "description": "", + "description": "Extended services support.", "links": [], "name": "ExtendedServices", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -354,7 +354,7 @@ }, { "alias": "os-extended-services-delete", - "description": "", + "description": "Extended services deletion support.", "links": [], "name": "ExtendedServicesDelete", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -418,7 +418,7 @@ }, { "alias": "os-flavor-swap", - "description": "", + "description": "Support to show the swap status of a flavor.", "links": [], "name": "FlavorSwap", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -482,7 +482,7 @@ }, { "alias": "os-hypervisor-status", - "description": "", + "description": "Show hypervisor status.", "links": [], "name": "HypervisorStatus", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -650,7 +650,7 @@ }, { "alias": "os-server-group-quotas", - "description": "", + "description": "Adds quota support to server groups.", "links": [], "name": "ServerGroupQuotas", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -666,7 +666,7 @@ }, { "alias": "os-server-list-multi-status", - "description": "", + "description": "Allow to filter the servers by a set of status values.", "links": [], "name": "ServerListMultiStatus", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -682,7 +682,7 @@ }, { "alias": "os-server-sort-keys", - "description": "", + "description": "Add sorting support in get Server v2 API.", "links": [], "name": "ServerSortKeys", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -690,7 +690,7 @@ }, { "alias": "os-server-start-stop", - "description": "", + "description": "Start/Stop instance compute API support.", "links": [], "name": "ServerStartStop", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -746,7 +746,7 @@ }, { "alias": "os-used-limits-for-admin", - "description": "", + "description": "Provide data to admin on limited resources used by other tenants.", "links": [], "name": "UsedLimitsForAdmin", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -762,7 +762,7 @@ }, { "alias": "os-user-quotas", - "description": "", + "description": "Project user quota support.", "links": [], "name": "UserQuotas", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", @@ -778,7 +778,7 @@ }, { "alias": "os-volume-attachment-update", - "description": "", + "description": "Support for updating a volume attachment.", "links": [], "name": "VolumeAttachmentUpdate", "namespace": "http://docs.openstack.org/compute/ext/fake_xml", diff --git a/doc/api_samples/flavor-access/flavor-access-create-resp.json b/doc/api_samples/flavor-access/flavor-access-create-resp.json deleted file mode 100644 index 60f49f1ca62..00000000000 --- a/doc/api_samples/flavor-access/flavor-access-create-resp.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "flavor": { - "OS-FLV-DISABLED:disabled": false, - "disk": 10, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": false, - "id": "10", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/10", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/10", - "rel": "bookmark" - } - ], - "name": "test_flavor", - "ram": 1024, - "swap": "", - "vcpus": 2 - } -} diff --git a/doc/api_samples/flavor-access/flavor-access-detail-resp.json b/doc/api_samples/flavor-access/flavor-access-detail-resp.json deleted file mode 100644 index 28a88d55cf0..00000000000 --- a/doc/api_samples/flavor-access/flavor-access-detail-resp.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "flavors": [ - { - "OS-FLV-DISABLED:disabled": false, - "disk": 1, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/1", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ], - "name": "m1.tiny", - "ram": 512, - "swap": "", - "vcpus": 1 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 20, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "2", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/2", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/2", - "rel": "bookmark" - } - ], - "name": "m1.small", - "ram": 2048, - "swap": "", - "vcpus": 1 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 40, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "3", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/3", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/3", - "rel": "bookmark" - } - ], - "name": "m1.medium", - "ram": 4096, - "swap": "", - "vcpus": 2 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 80, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "4", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/4", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/4", - "rel": "bookmark" - } - ], - "name": "m1.large", - "ram": 8192, - "swap": "", - "vcpus": 4 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 160, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "5", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/5", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/5", - "rel": "bookmark" - } - ], - "name": "m1.xlarge", - "ram": 16384, - "swap": "", - "vcpus": 8 - } - ] -} diff --git a/doc/api_samples/flavor-access/flavor-access-show-resp.json b/doc/api_samples/flavor-access/flavor-access-show-resp.json deleted file mode 100644 index e48669eeef4..00000000000 --- a/doc/api_samples/flavor-access/flavor-access-show-resp.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "flavor": { - "OS-FLV-DISABLED:disabled": false, - "disk": 1, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/1", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ], - "name": "m1.tiny", - "ram": 512, - "swap": "", - "vcpus": 1 - } -} diff --git a/doc/api_samples/flavor-access/flavor-access-create-req.json b/doc/api_samples/flavor-access/flavor-create-req.json similarity index 100% rename from doc/api_samples/flavor-access/flavor-access-create-req.json rename to doc/api_samples/flavor-access/flavor-create-req.json diff --git a/doc/api_samples/flavor-access/v2.7/flavor-access-add-tenant-req.json b/doc/api_samples/flavor-access/v2.7/flavor-access-add-tenant-req.json new file mode 100644 index 00000000000..eb23a76c15b --- /dev/null +++ b/doc/api_samples/flavor-access/v2.7/flavor-access-add-tenant-req.json @@ -0,0 +1,5 @@ +{ + "addTenantAccess": { + "tenant": "fake_tenant" + } +} diff --git a/doc/api_samples/flavor-access/v2.7/flavor-create-req.json b/doc/api_samples/flavor-access/v2.7/flavor-create-req.json new file mode 100644 index 00000000000..4ad8a8c15e7 --- /dev/null +++ b/doc/api_samples/flavor-access/v2.7/flavor-create-req.json @@ -0,0 +1,10 @@ +{ + "flavor": { + "name": "test_flavor", + "ram": 1024, + "vcpus": 2, + "disk": 10, + "id": "10", + "os-flavor-access:is_public": true + } +} diff --git a/doc/api_samples/flavor-manage/flavor-create-post-req.json b/doc/api_samples/flavor-manage/flavor-create-post-req.json index 0c5914a0108..bbd71248133 100644 --- a/doc/api_samples/flavor-manage/flavor-create-post-req.json +++ b/doc/api_samples/flavor-manage/flavor-create-post-req.json @@ -4,6 +4,7 @@ "ram": 1024, "vcpus": 2, "disk": 10, - "id": "10" + "id": "10", + "rxtx_factor": 2.0 } } diff --git a/doc/api_samples/flavor-manage/flavor-create-post-resp.json b/doc/api_samples/flavor-manage/flavor-create-post-resp.json index f275ac411aa..cade7bba366 100644 --- a/doc/api_samples/flavor-manage/flavor-create-post-resp.json +++ b/doc/api_samples/flavor-manage/flavor-create-post-resp.json @@ -18,6 +18,7 @@ "name": "test_flavor", "ram": 1024, "swap": "", + "rxtx_factor": 2.0, "vcpus": 2 } } diff --git a/doc/api_samples/flavors/flavor-get-resp.json b/doc/api_samples/flavors/flavor-get-resp.json index e48669eeef4..c80372cbfd8 100644 --- a/doc/api_samples/flavors/flavor-get-resp.json +++ b/doc/api_samples/flavors/flavor-get-resp.json @@ -18,6 +18,7 @@ "name": "m1.tiny", "ram": 512, "swap": "", - "vcpus": 1 + "vcpus": 1, + "rxtx_factor": 1.0 } } diff --git a/doc/api_samples/flavors/flavors-detail-resp.json b/doc/api_samples/flavors/flavors-detail-resp.json index 28a88d55cf0..f59f287dedf 100644 --- a/doc/api_samples/flavors/flavors-detail-resp.json +++ b/doc/api_samples/flavors/flavors-detail-resp.json @@ -19,7 +19,8 @@ "name": "m1.tiny", "ram": 512, "swap": "", - "vcpus": 1 + "vcpus": 1, + "rxtx_factor": 1.0 }, { "OS-FLV-DISABLED:disabled": false, @@ -40,7 +41,8 @@ "name": "m1.small", "ram": 2048, "swap": "", - "vcpus": 1 + "vcpus": 1, + "rxtx_factor": 1.0 }, { "OS-FLV-DISABLED:disabled": false, @@ -61,7 +63,8 @@ "name": "m1.medium", "ram": 4096, "swap": "", - "vcpus": 2 + "vcpus": 2, + "rxtx_factor": 1.0 }, { "OS-FLV-DISABLED:disabled": false, @@ -82,7 +85,8 @@ "name": "m1.large", "ram": 8192, "swap": "", - "vcpus": 4 + "vcpus": 4, + "rxtx_factor": 1.0 }, { "OS-FLV-DISABLED:disabled": false, @@ -103,7 +107,30 @@ "name": "m1.xlarge", "ram": 16384, "swap": "", - "vcpus": 8 + "vcpus": 8, + "rxtx_factor": 1.0 + }, + { + "OS-FLV-DISABLED:disabled": false, + "disk": 1, + "OS-FLV-EXT-DATA:ephemeral": 0, + "os-flavor-access:is_public": true, + "id": "6", + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/6", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/6", + "rel": "bookmark" + } + ], + "name": "m1.tiny.specs", + "ram": 512, + "swap": "", + "vcpus": 1, + "rxtx_factor": 1.0 } ] } diff --git a/doc/api_samples/flavors/flavors-list-resp.json b/doc/api_samples/flavors/flavors-list-resp.json index c585f2bb524..d2039729634 100644 --- a/doc/api_samples/flavors/flavors-list-resp.json +++ b/doc/api_samples/flavors/flavors-list-resp.json @@ -69,6 +69,20 @@ } ], "name": "m1.xlarge" + }, + { + "id": "6", + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/6", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/6", + "rel": "bookmark" + } + ], + "name": "m1.tiny.specs" } ] -} \ No newline at end of file +} diff --git a/doc/api_samples/keypairs/v2.35/keypairs-list-resp.json b/doc/api_samples/keypairs/v2.35/keypairs-list-resp.json new file mode 100644 index 00000000000..69c8ec4f143 --- /dev/null +++ b/doc/api_samples/keypairs/v2.35/keypairs-list-resp.json @@ -0,0 +1,18 @@ +{ + "keypairs": [ + { + "keypair": { + "fingerprint": "7e:eb:ab:24:ba:d1:e1:88:ae:9a:fb:66:53:df:d3:bd", + "name": "keypair-5d935425-31d5-48a7-a0f1-e76e9813f2c3", + "type": "ssh", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkF3MX59OrlBs3dH5CU7lNmvpbrgZxSpyGjlnE8Flkirnc/Up22lpjznoxqeoTAwTW034k7Dz6aYIrZGmQwe2TkE084yqvlj45Dkyoj95fW/sZacm0cZNuL69EObEGHdprfGJQajrpz22NQoCD8TFB8Wv+8om9NH9Le6s+WPe98WC77KLw8qgfQsbIey+JawPWl4O67ZdL5xrypuRjfIPWjgy/VH85IXg/Z/GONZ2nxHgSShMkwqSFECAC5L3PHB+0+/12M/iikdatFSVGjpuHvkLOs3oe7m6HlOfluSJ85BzLWBbvva93qkGmLg4ZAc8rPh2O+YIsBUHNLLMM/oQp Generated-by-Nova\n" + } + } + ], + "keypairs_links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/keypairs?limit=1&marker=keypair-5d935425-31d5-48a7-a0f1-e76e9813f2c3", + "rel": "next" + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/keypairs/v2.35/keypairs-list-user1-resp.json b/doc/api_samples/keypairs/v2.35/keypairs-list-user1-resp.json new file mode 100644 index 00000000000..547cb000c95 --- /dev/null +++ b/doc/api_samples/keypairs/v2.35/keypairs-list-user1-resp.json @@ -0,0 +1,12 @@ +{ + "keypairs": [ + { + "keypair": { + "fingerprint": "7e:eb:ab:24:ba:d1:e1:88:ae:9a:fb:66:53:df:d3:bd", + "name": "keypair-5d935425-31d5-48a7-a0f1-e76e9813f2c3", + "type": "ssh", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkF3MX59OrlBs3dH5CU7lNmvpbrgZxSpyGjlnE8Flkirnc/Up22lpjznoxqeoTAwTW034k7Dz6aYIrZGmQwe2TkE084yqvlj45Dkyoj95fW/sZacm0cZNuL69EObEGHdprfGJQajrpz22NQoCD8TFB8Wv+8om9NH9Le6s+WPe98WC77KLw8qgfQsbIey+JawPWl4O67ZdL5xrypuRjfIPWjgy/VH85IXg/Z/GONZ2nxHgSShMkwqSFECAC5L3PHB+0+/12M/iikdatFSVGjpuHvkLOs3oe7m6HlOfluSJ85BzLWBbvva93qkGmLg4ZAc8rPh2O+YIsBUHNLLMM/oQp Generated-by-Nova\n" + } + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/keypairs/v2.35/keypairs-list-user2-resp.json b/doc/api_samples/keypairs/v2.35/keypairs-list-user2-resp.json new file mode 100644 index 00000000000..3c75f9ef621 --- /dev/null +++ b/doc/api_samples/keypairs/v2.35/keypairs-list-user2-resp.json @@ -0,0 +1,18 @@ +{ + "keypairs": [ + { + "keypair": { + "fingerprint": "7e:eb:ab:24:ba:d1:e1:88:ae:9a:fb:66:53:df:d3:bd", + "name": "keypair-5d935425-31d5-48a7-a0f1-e76e9813f2c3", + "type": "ssh", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkF3MX59OrlBs3dH5CU7lNmvpbrgZxSpyGjlnE8Flkirnc/Up22lpjznoxqeoTAwTW034k7Dz6aYIrZGmQwe2TkE084yqvlj45Dkyoj95fW/sZacm0cZNuL69EObEGHdprfGJQajrpz22NQoCD8TFB8Wv+8om9NH9Le6s+WPe98WC77KLw8qgfQsbIey+JawPWl4O67ZdL5xrypuRjfIPWjgy/VH85IXg/Z/GONZ2nxHgSShMkwqSFECAC5L3PHB+0+/12M/iikdatFSVGjpuHvkLOs3oe7m6HlOfluSJ85BzLWBbvva93qkGmLg4ZAc8rPh2O+YIsBUHNLLMM/oQp Generated-by-Nova\n" + } + } + ], + "keypairs_links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/keypairs?limit=1&marker=keypair-5d935425-31d5-48a7-a0f1-e76e9813f2c3&user_id=user2", + "rel": "next" + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/keypairs/v2.35/keypairs-post-req.json b/doc/api_samples/keypairs/v2.35/keypairs-post-req.json new file mode 100644 index 00000000000..005a3f50453 --- /dev/null +++ b/doc/api_samples/keypairs/v2.35/keypairs-post-req.json @@ -0,0 +1,7 @@ +{ + "keypair": { + "name": "keypair-ab9ff2e6-a6d7-4915-a241-044c369c07f9", + "type": "ssh", + "user_id": "fake" + } +} diff --git a/doc/api_samples/keypairs/v2.35/keypairs-post-resp.json b/doc/api_samples/keypairs/v2.35/keypairs-post-resp.json new file mode 100644 index 00000000000..394960868bf --- /dev/null +++ b/doc/api_samples/keypairs/v2.35/keypairs-post-resp.json @@ -0,0 +1,10 @@ +{ + "keypair": { + "fingerprint": "7e:eb:ab:24:ba:d1:e1:88:ae:9a:fb:66:53:df:d3:bd", + "name": "keypair-ab9ff2e6-a6d7-4915-a241-044c369c07f9", + "type": "ssh", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEApBdzF+fTq5QbN3R+QlO5TZr6W64GcUqcho5ZxPBZZIq53P1K\ndtpaY856ManqEwME1tN+JOw8+mmCK2RpkMHtk5BNPOMqr5Y+OQ5MqI/eX1v7GWnJ\ntHGTbi+vRDmxBh3aa3xiUGo66c9tjUKAg/ExQfFr/vKJvTR/S3urPlj3vfFgu+yi\n8PKoH0LGyHsviWsD1peDuu2XS+ca8qbkY3yD1o4Mv1R/OSF4P2fxjjWdp8R4EkoT\nJMKkhRAgAuS9zxwftPv9djP4opHWrRUlRo6bh75CzrN6Hu5uh5Tn5bkifOQcy1gW\n772vd6pBpi4OGQHPKz4djvmCLAVBzSyzDP6EKQIDAQABAoIBAQCB+tU/ZXKlIe+h\nMNTmoz1QfOe+AY625Rwx9cakGqMk4kKyC62VkgcxshfXCToSjzyhEuyEQOFYloT2\n7FY2xXb0gcS861Efv0pQlcQhbbz/GnQ/wC13ktPu3zTdPTm9l54xsFiMTGmYVaf4\n0mnMmhyjmKIsVGDJEDGZUD/oZj7wJGOFha5M4FZrZlJIrEZC0rGGlcC0kGF2no6B\nj1Mu7HjyK3pTKf4dlp+jeRikUF5Pct+qT+rcv2rZ3fl3inxtlLEwZeFPbp/njf/U\nIGxFzZsuLmiFlsJar6M5nEckTB3p25maWWaR8/0jvJRgsPnuoUrUoGDq87DMKCdk\nlw6by9fRAoGBANhnS9ko7Of+ntqIFR7xOG9p/oPATztgHkFxe4GbQ0leaDRTx3vE\ndQmUCnn24xtyVECaI9a4IV+LP1npw8niWUJ4pjgdAlkF4cCTu9sN+cBO15SfdACI\nzD1DaaHmpFCAWlpTo68VWlvWll6i2ncCkRJR1+q/C/yQz7asvl4AakElAoGBAMId\nxqMT2Sy9xLuHsrAoMUvBOkwaMYZH+IAb4DvUDjVIiKWjmonrmopS5Lpb+ALBKqZe\neVfD6HwWQqGwCFItToaEkZvrNfTapoNCHWWg001D49765UV5lMrArDbM1vXtFfM4\nDRYM6+Y6o/6QH8EBgXtyBxcYthIDBM3wBJa67xG1AoGAKTm8fFlMkIG0N4N3Kpbf\nnnH915GaRoBwIx2AXtd6QQ7oIRfYx95MQY/fUw7SgxcLr+btbulTCkWXwwRClUI2\nqPAdElGMcfMp56r9PaTy8EzUyu55heSJrB4ckIhEw0VAcTa/1wnlVduSd+LkZYmq\no2fOD11n5iycNXvBJF1F4LUCgYAMaRbwCi7SW3eefbiA5rDwJPRzNSGBckyC9EVL\nzezynyaNYH5a3wNMYKxa9dJPasYtSND9OXs9o7ay26xMhLUGiKc+jrUuaGRI9Asp\nGjUoNXT2JphN7s4CgHsCLep4YqYKnMTJah4S5CDj/5boIg6DM/EcGupZEHRYLkY8\n1MrAGQKBgQCi9yeC39ctLUNn+Ix604gttWWChdt3ozufTZ7HybJOSRA9Gh3iD5gm\nzlz0xqpGShKpOY2k+ftvja0poMdGeJLt84P3r2q01IgI7w0LmOj5m0W10dHysH27\nBWpCnHdBJMxnBsMRPoM4MKkmKWD9l5PSTCTWtkIpsyuDCko6D9UwZA==\n-----END RSA PRIVATE KEY-----\n", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkF3MX59OrlBs3dH5CU7lNmvpbrgZxSpyGjlnE8Flkirnc/Up22lpjznoxqeoTAwTW034k7Dz6aYIrZGmQwe2TkE084yqvlj45Dkyoj95fW/sZacm0cZNuL69EObEGHdprfGJQajrpz22NQoCD8TFB8Wv+8om9NH9Le6s+WPe98WC77KLw8qgfQsbIey+JawPWl4O67ZdL5xrypuRjfIPWjgy/VH85IXg/Z/GONZ2nxHgSShMkwqSFECAC5L3PHB+0+/12M/iikdatFSVGjpuHvkLOs3oe7m6HlOfluSJ85BzLWBbvva93qkGmLg4ZAc8rPh2O+YIsBUHNLLMM/oQp Generated-by-Nova\n", + "user_id": "fake" + } +} diff --git a/doc/api_samples/limits/limit-get-resp.json b/doc/api_samples/limits/limit-get-resp.json index 79171f5ff02..28309af04c6 100644 --- a/doc/api_samples/limits/limit-get-resp.json +++ b/doc/api_samples/limits/limit-get-resp.json @@ -13,7 +13,13 @@ "maxTotalKeypairs": 100, "maxTotalRAMSize": 51200, "maxServerGroups": 10, - "maxServerGroupMembers": 10 + "maxServerGroupMembers": 10, + "totalCoresUsed": 0, + "totalInstancesUsed": 0, + "totalRAMUsed": 0, + "totalSecurityGroupsUsed": 0, + "totalFloatingIpsUsed": 0, + "totalServerGroupsUsed": 0 }, "rate": [] } diff --git a/doc/api_samples/limits/v2-limit-get-resp.json b/doc/api_samples/limits/v2-limit-get-resp.json deleted file mode 100644 index 6ccac46acdd..00000000000 --- a/doc/api_samples/limits/v2-limit-get-resp.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "limits": { - "absolute": { - "maxImageMeta": 128, - "maxPersonality": 5, - "maxPersonalitySize": 10240, - "maxSecurityGroupRules": 20, - "maxSecurityGroups": 10, - "maxServerMeta": 128, - "maxTotalCores": 20, - "maxTotalFloatingIps": 10, - "maxTotalInstances": 10, - "maxTotalKeypairs": 100, - "maxTotalRAMSize": 51200, - "maxServerGroups": 10, - "maxServerGroupMembers": 10 - }, - "rate": [ - { - "limit": [ - { - "next-available": "2012-11-27T17:22:18Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "POST" - }, - { - "next-available": "2012-11-27T17:22:18Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "PUT" - }, - { - "next-available": "2012-11-27T17:22:18Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "DELETE" - } - ], - "regex": ".*", - "uri": "*" - }, - { - "limit": [ - { - "next-available": "2012-11-27T17:22:18Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "POST" - } - ], - "regex": "^/servers", - "uri": "*/servers" - }, - { - "limit": [ - { - "next-available": "2012-11-27T17:22:18Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "GET" - } - ], - "regex": ".*changes-since.*", - "uri": "*changes-since*" - }, - { - "limit": [ - { - "next-available": "2012-11-27T17:22:18Z", - "remaining": 12, - "unit": "MINUTE", - "value": 12, - "verb": "GET" - } - ], - "regex": "^/os-fping", - "uri": "*/os-fping" - } - ] - } -} diff --git a/doc/api_samples/limits/v2.36/limit-get-resp.json b/doc/api_samples/limits/v2.36/limit-get-resp.json new file mode 100644 index 00000000000..4943f70eddb --- /dev/null +++ b/doc/api_samples/limits/v2.36/limit-get-resp.json @@ -0,0 +1,21 @@ +{ + "limits": { + "absolute": { + "maxImageMeta": 128, + "maxPersonality": 5, + "maxPersonalitySize": 10240, + "maxServerMeta": 128, + "maxTotalCores": 20, + "maxTotalInstances": 10, + "maxTotalKeypairs": 100, + "maxTotalRAMSize": 51200, + "maxServerGroups": 10, + "maxServerGroupMembers": 10, + "totalCoresUsed": 0, + "totalInstancesUsed": 0, + "totalRAMUsed": 0, + "totalServerGroupsUsed": 0 + }, + "rate": [] + } +} diff --git a/doc/api_samples/limits/v2.39/limit-get-resp.json b/doc/api_samples/limits/v2.39/limit-get-resp.json new file mode 100644 index 00000000000..68b29eacd63 --- /dev/null +++ b/doc/api_samples/limits/v2.39/limit-get-resp.json @@ -0,0 +1,20 @@ +{ + "limits": { + "absolute": { + "maxPersonality": 5, + "maxPersonalitySize": 10240, + "maxServerMeta": 128, + "maxTotalCores": 20, + "maxTotalInstances": 10, + "maxTotalKeypairs": 100, + "maxTotalRAMSize": 51200, + "maxServerGroups": 10, + "maxServerGroupMembers": 10, + "totalCoresUsed": 0, + "totalInstancesUsed": 0, + "totalRAMUsed": 0, + "totalServerGroupsUsed": 0 + }, + "rate": [] + } +} diff --git a/doc/api_samples/os-access-ips/server-action-rebuild-resp.json b/doc/api_samples/os-access-ips/server-action-rebuild-resp.json deleted file mode 100644 index 79b0568347e..00000000000 --- a/doc/api_samples/os-access-ips/server-action-rebuild-resp.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "server": { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "version": 4 - } - ] - }, - "adminPass": "99WHAxN8gpvg", - "created": "2013-11-06T07:51:09Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "5c8072dbcda8ce3f26deb6662bd7718e1a6d349bdf2296911d1be4ac", - "id": "53a63a19-c145-47f8-9ae5-b39d6bff33ec", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/53a63a19-c145-47f8-9ae5-b39d6bff33ec", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/53a63a19-c145-47f8-9ae5-b39d6bff33ec", - "rel": "bookmark" - } - ], - "metadata": { - "meta_var": "meta_val" - }, - "name": "new-server-test", - "accessIPv4": "4.3.2.1", - "accessIPv6": "80fe::", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-11-06T07:51:11Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-access-ips/server-action-rebuild.json b/doc/api_samples/os-access-ips/server-action-rebuild.json deleted file mode 100644 index 15550d0a655..00000000000 --- a/doc/api_samples/os-access-ips/server-action-rebuild.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "rebuild" : { - "accessIPv4": "4.3.2.1", - "accessIPv6": "80fe::", - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "name" : "new-server-test", - "metadata" : { - "meta_var" : "meta_val" - } - } -} diff --git a/doc/api_samples/os-access-ips/server-get-resp.json b/doc/api_samples/os-access-ips/server-get-resp.json deleted file mode 100644 index 130ba4acec2..00000000000 --- a/doc/api_samples/os-access-ips/server-get-resp.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "server": { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-11T03:23:12Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "b3a6fd97c027e18d6d9c7506eea8a236cf2ceca420cfdfe0239a64a8", - "id": "5eedbf0c-c303-4ed3-933a-a4d3732cfa0a", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/5eedbf0c-c303-4ed3-933a-a4d3732cfa0a", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/5eedbf0c-c303-4ed3-933a-a4d3732cfa0a", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "accessIPv4": "1.2.3.4", - "accessIPv6": "fe80::", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-11T03:23:13Z", - "user_id": "fake", - "key_name": null - } -} diff --git a/doc/api_samples/os-access-ips/server-post-req.json b/doc/api_samples/os-access-ips/server-post-req.json deleted file mode 100644 index 8d7489c1042..00000000000 --- a/doc/api_samples/os-access-ips/server-post-req.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "server" : { - "accessIPv4": "1.2.3.4", - "accessIPv6": "fe80::", - "name" : "new-server-test", - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef" : "1", - "metadata" : { - "My Server Name" : "Apache1" - } - } -} diff --git a/doc/api_samples/os-access-ips/server-post-resp.json b/doc/api_samples/os-access-ips/server-post-resp.json deleted file mode 100644 index 9c31e93c9a0..00000000000 --- a/doc/api_samples/os-access-ips/server-post-resp.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "server": { - "adminPass": "n7JGBda664QG", - "id": "934760e1-2b0b-4f9e-a916-eac1e69839dc", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/934760e1-2b0b-4f9e-a916-eac1e69839dc", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/934760e1-2b0b-4f9e-a916-eac1e69839dc", - "rel": "bookmark" - } - ] - } -} \ No newline at end of file diff --git a/doc/api_samples/os-access-ips/servers-details-resp.json b/doc/api_samples/os-access-ips/servers-details-resp.json deleted file mode 100644 index 844506ea315..00000000000 --- a/doc/api_samples/os-access-ips/servers-details-resp.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "servers": [ - { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-11T03:23:11Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "9896cb12c9845becf1b9b06c8ff5b131d20300f83e2cdffc92e3f4a4", - "id": "934760e1-2b0b-4f9e-a916-eac1e69839dc", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/934760e1-2b0b-4f9e-a916-eac1e69839dc", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/934760e1-2b0b-4f9e-a916-eac1e69839dc", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "accessIPv4": "1.2.3.4", - "accessIPv6": "fe80::", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-11T03:23:12Z", - "user_id": "fake", - "key_name": null - } - ] -} diff --git a/doc/api_samples/os-agents/agent-post-req.json b/doc/api_samples/os-agents/agent-post-req.json index 1913498547c..580d44b65c9 100644 --- a/doc/api_samples/os-agents/agent-post-req.json +++ b/doc/api_samples/os-agents/agent-post-req.json @@ -1,6 +1,6 @@ { "agent": { - "hypervisor": "hypervisor", + "hypervisor": "xen", "os": "os", "architecture": "x86", "version": "8.0", diff --git a/doc/api_samples/os-agents/agent-post-resp.json b/doc/api_samples/os-agents/agent-post-resp.json index 24ddede90b4..644c4c73d35 100644 --- a/doc/api_samples/os-agents/agent-post-resp.json +++ b/doc/api_samples/os-agents/agent-post-resp.json @@ -2,7 +2,7 @@ "agent": { "agent_id": 1, "architecture": "x86", - "hypervisor": "hypervisor", + "hypervisor": "xen", "md5hash": "add6bb58e139be103324d04d82d8f545", "os": "os", "url": "http://example.com/path/to/resource", diff --git a/doc/api_samples/os-agents/agents-get-resp.json b/doc/api_samples/os-agents/agents-get-resp.json index 92e14e1dc51..6f31e256b05 100644 --- a/doc/api_samples/os-agents/agents-get-resp.json +++ b/doc/api_samples/os-agents/agents-get-resp.json @@ -3,7 +3,7 @@ { "agent_id": 1, "architecture": "x86", - "hypervisor": "hypervisor", + "hypervisor": "xen", "md5hash": "add6bb58e139be103324d04d82d8f545", "os": "os", "url": "http://example.com/path/to/resource", diff --git a/doc/api_samples/os-aggregates/aggregates-list-get-resp.json b/doc/api_samples/os-aggregates/aggregates-list-get-resp.json index f10de405fcb..3887b096f87 100644 --- a/doc/api_samples/os-aggregates/aggregates-list-get-resp.json +++ b/doc/api_samples/os-aggregates/aggregates-list-get-resp.json @@ -5,7 +5,7 @@ "created_at": "2013-08-18T12:17:56.856455", "deleted": false, "deleted_at": null, - "hosts": [], + "hosts": ["21549b2f665945baaa7101926a00143c"], "id": 1, "metadata": { "availability_zone": "nova" diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json new file mode 100644 index 00000000000..4e6bdfef3f4 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-add-host-post-req.json @@ -0,0 +1,5 @@ +{ + "add_host": { + "host": "compute" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json new file mode 100644 index 00000000000..7331e06a8c0 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-metadata-post-req.json @@ -0,0 +1,9 @@ +{ + "set_metadata": + { + "metadata": + { + "key": "value" + } + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-post-req.json new file mode 100644 index 00000000000..82272c2936b --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-post-req.json @@ -0,0 +1,7 @@ +{ + "aggregate": + { + "name": "name", + "availability_zone": "nova" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregate-post-resp.json new file mode 100644 index 00000000000..07b9ca318cb --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-post-resp.json @@ -0,0 +1,12 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T22:51:32.877711", + "deleted": false, + "deleted_at": null, + "id": 1, + "name": "name", + "updated_at": null, + "uuid": "86a0da0e-9f0c-4f51-a1e0-3c25edab3783" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json new file mode 100644 index 00000000000..e42b053009e --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-remove-host-post-req.json @@ -0,0 +1,5 @@ +{ + "remove_host": { + "host": "compute" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json b/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json new file mode 100644 index 00000000000..0af1a37a4d9 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-req.json @@ -0,0 +1,7 @@ +{ + "aggregate": + { + "name": "newname", + "availability_zone": "nova2" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json new file mode 100644 index 00000000000..4054d5193db --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregate-update-post-resp.json @@ -0,0 +1,16 @@ +{ + "aggregate": { + "availability_zone": "nova2", + "created_at": "2016-12-27T23:47:32.897139", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova2" + }, + "name": "newname", + "updated_at": "2016-12-27T23:47:33.067180", + "uuid": "6f74e3f3-df28-48f3-98e1-ac941b1c5e43" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json new file mode 100644 index 00000000000..0f1a40f2cc3 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-add-host-post-resp.json @@ -0,0 +1,18 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T23:47:30.594805", + "deleted": false, + "deleted_at": null, + "hosts": [ + "compute" + ], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "d1842372-89c5-4fbd-ad5a-5d2e16c85456" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-get-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-get-resp.json new file mode 100644 index 00000000000..5cd1cf75ff8 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-get-resp.json @@ -0,0 +1,16 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T23:47:30.563527", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "fd0a5b12-7e8d-469d-bfd5-64a6823e7407" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json new file mode 100644 index 00000000000..6b68451fd92 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-list-get-resp.json @@ -0,0 +1,20 @@ +{ + "aggregates": [ + { + "availability_zone": "nova", + "created_at": "2016-12-27T23:47:32.911515", + "deleted": false, + "deleted_at": null, + "hosts": [ + "compute" + ], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "6ba28ba7-f29b-45cc-a30b-6e3a40c2fb14" + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json new file mode 100644 index 00000000000..9dda7f83e2b --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-metadata-post-resp.json @@ -0,0 +1,17 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T23:59:18.623100", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova", + "key": "value" + }, + "name": "name", + "updated_at": "2016-12-27T23:59:18.723348", + "uuid": "26002bdb-62cc-41bd-813a-0ad22db32625" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json b/doc/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json new file mode 100644 index 00000000000..f1fde0d3382 --- /dev/null +++ b/doc/api_samples/os-aggregates/v2.41/aggregates-remove-host-post-resp.json @@ -0,0 +1,16 @@ +{ + "aggregate": { + "availability_zone": "nova", + "created_at": "2016-12-27T23:47:30.594805", + "deleted": false, + "deleted_at": null, + "hosts": [], + "id": 1, + "metadata": { + "availability_zone": "nova" + }, + "name": "name", + "updated_at": null, + "uuid": "d1842372-89c5-4fbd-ad5a-5d2e16c85456" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-req.json b/doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-req.json index 8c9a309ea92..997129b490f 100644 --- a/doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-req.json +++ b/doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-req.json @@ -4,7 +4,8 @@ "create_info": { "snapshot_id": "421752a6-acf6-4b2d-bc7a-119f9148cd8c", "type": "qcow2", - "new_file": "new_file_name" + "new_file": "new_file_name", + "id": "421752a6-acf6-4b2d-bc7a-119f9148cd8c" } } } diff --git a/doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-resp.json b/doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-resp.json index acfc149658c..2211af512b7 100644 --- a/doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-resp.json +++ b/doc/api_samples/os-assisted-volume-snapshots/snapshot-create-assisted-resp.json @@ -1,6 +1,6 @@ { "snapshot": { - "id": 100, + "id": "421752a6-acf6-4b2d-bc7a-119f9148cd8c", "volumeId": "521752a6-acf6-4b2d-bc7a-119f9148cd8c" } } \ No newline at end of file diff --git a/doc/api_samples/os-attach-interfaces/attach-interfaces-create-net_id-req.json b/doc/api_samples/os-attach-interfaces/attach-interfaces-create-net_id-req.json new file mode 100644 index 00000000000..b2ce0ea4a18 --- /dev/null +++ b/doc/api_samples/os-attach-interfaces/attach-interfaces-create-net_id-req.json @@ -0,0 +1,10 @@ +{ + "interfaceAttachment": { + "fixed_ips": [ + { + "ip_address": "192.168.1.3" + } + ], + "net_id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6" + } +} diff --git a/doc/api_samples/os-attach-interfaces/v2.49/attach-interfaces-create-req.json b/doc/api_samples/os-attach-interfaces/v2.49/attach-interfaces-create-req.json new file mode 100644 index 00000000000..977afc788ca --- /dev/null +++ b/doc/api_samples/os-attach-interfaces/v2.49/attach-interfaces-create-req.json @@ -0,0 +1,6 @@ +{ + "interfaceAttachment": { + "port_id": "ce531f90-199f-48c0-816c-13e38010b442", + "tag": "foo" + } +} diff --git a/doc/api_samples/os-attach-interfaces/v2.49/attach-interfaces-create-resp.json b/doc/api_samples/os-attach-interfaces/v2.49/attach-interfaces-create-resp.json new file mode 100644 index 00000000000..49f14044839 --- /dev/null +++ b/doc/api_samples/os-attach-interfaces/v2.49/attach-interfaces-create-resp.json @@ -0,0 +1,14 @@ +{ + "interfaceAttachment": { + "fixed_ips": [ + { + "ip_address": "192.168.1.3", + "subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef" + } + ], + "mac_addr": "fa:16:3e:4c:2c:30", + "net_id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6", + "port_id": "ce531f90-199f-48c0-816c-13e38010b442", + "port_state": "ACTIVE" + } +} diff --git a/doc/api_samples/os-availability-zone/availability-zone-post-resp.json b/doc/api_samples/os-availability-zone/availability-zone-post-resp.json index d5821b679c9..a13b8b9a5b3 100644 --- a/doc/api_samples/os-availability-zone/availability-zone-post-resp.json +++ b/doc/api_samples/os-availability-zone/availability-zone-post-resp.json @@ -11,6 +11,12 @@ "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/3e45fa2a-5204-466f-a684-c2a8e1c82d7f", "rel": "bookmark" } + ], + "OS-DCF:diskConfig": "AUTO", + "security_groups": [ + { + "name": "default" + } ] } } \ No newline at end of file diff --git a/doc/api_samples/os-config-drive/server-config-drive-get-resp.json b/doc/api_samples/os-config-drive/server-config-drive-get-resp.json deleted file mode 100644 index cd5279aad83..00000000000 --- a/doc/api_samples/os-config-drive/server-config-drive-get-resp.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "server": { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-22T02:33:23Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "1642bbdbd61a0f1c513b4bb6e418326103172698104bfa278eca106b", - "id": "7838ff1b-b71f-48b9-91e9-7c08de20b249", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/7838ff1b-b71f-48b9-91e9-7c08de20b249", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/7838ff1b-b71f-48b9-91e9-7c08de20b249", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "config_drive": "", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-22T02:33:25Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-config-drive/server-post-req.json b/doc/api_samples/os-config-drive/server-post-req.json deleted file mode 100644 index ca53f3a7c26..00000000000 --- a/doc/api_samples/os-config-drive/server-post-req.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "server" : { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "name" : "new-server-test", - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef" : "1", - "metadata" : { - "My Server Name" : "Apache1" - } - } -} diff --git a/doc/api_samples/os-config-drive/server-post-resp.json b/doc/api_samples/os-config-drive/server-post-resp.json deleted file mode 100644 index 971625574e7..00000000000 --- a/doc/api_samples/os-config-drive/server-post-resp.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "server": { - "adminPass": "h2cx3Lm47BJc", - "id": "f0318e69-11eb-4aed-9840-59b6c72beee8", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f0318e69-11eb-4aed-9840-59b6c72beee8", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f0318e69-11eb-4aed-9840-59b6c72beee8", - "rel": "bookmark" - } - ] - } -} diff --git a/doc/api_samples/os-config-drive/servers-config-drive-details-resp.json b/doc/api_samples/os-config-drive/servers-config-drive-details-resp.json deleted file mode 100644 index e78d1f3b26c..00000000000 --- a/doc/api_samples/os-config-drive/servers-config-drive-details-resp.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "servers": [ - { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-22T02:33:17Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "1ed067c90341cd9d94bbe5da960922b56f107262cdc75719a0d97b78", - "id": "f0318e69-11eb-4aed-9840-59b6c72beee8", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f0318e69-11eb-4aed-9840-59b6c72beee8", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f0318e69-11eb-4aed-9840-59b6c72beee8", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "config_drive": "", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-22T02:33:19Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-create-backup/v2.45/create-backup-req.json b/doc/api_samples/os-create-backup/v2.45/create-backup-req.json new file mode 100644 index 00000000000..39f70358480 --- /dev/null +++ b/doc/api_samples/os-create-backup/v2.45/create-backup-req.json @@ -0,0 +1,7 @@ +{ + "createBackup": { + "name": "Backup 1", + "backup_type": "weekly", + "rotation": 1 + } +} diff --git a/doc/api_samples/os-create-backup/v2.45/create-backup-resp.json b/doc/api_samples/os-create-backup/v2.45/create-backup-resp.json new file mode 100644 index 00000000000..05594917ddc --- /dev/null +++ b/doc/api_samples/os-create-backup/v2.45/create-backup-resp.json @@ -0,0 +1,3 @@ +{ + "image_id": "0e7761dd-ee98-41f0-ba35-05994e446431" +} \ No newline at end of file diff --git a/doc/api_samples/os-disk-config/list-servers-detail-get.json b/doc/api_samples/os-disk-config/list-servers-detail-get.json deleted file mode 100644 index b6efc57c1cd..00000000000 --- a/doc/api_samples/os-disk-config/list-servers-detail-get.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "servers": [ - { - "OS-DCF:diskConfig": "AUTO", - "accessIPv4": "", - "accessIPv6": "", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2012-12-02T02:11:55Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "99428f32351a5d89d0f7727c6eec68c1777c545a0972aaac645508dc", - "id": "05372e62-05b9-4ee2-9343-9a1fdf2a5fda", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/05372e62-05b9-4ee2-9343-9a1fdf2a5fda", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/05372e62-05b9-4ee2-9343-9a1fdf2a5fda", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2012-12-02T02:11:56Z", - "key_name": null, - "user_id": "fake" - } - ] -} \ No newline at end of file diff --git a/doc/api_samples/os-disk-config/server-action-rebuild-req.json b/doc/api_samples/os-disk-config/server-action-rebuild-req.json deleted file mode 100644 index 670e11e79b4..00000000000 --- a/doc/api_samples/os-disk-config/server-action-rebuild-req.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "rebuild": { - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "OS-DCF:diskConfig": "AUTO" - } -} diff --git a/doc/api_samples/os-disk-config/server-action-rebuild-resp.json b/doc/api_samples/os-disk-config/server-action-rebuild-resp.json deleted file mode 100644 index 8c6abaff81a..00000000000 --- a/doc/api_samples/os-disk-config/server-action-rebuild-resp.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "server": { - "OS-DCF:diskConfig": "AUTO", - "accessIPv4": "", - "accessIPv6": "", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "version": 4 - } - ] - }, - "adminPass": "NBjMaJoFL4EF", - "created": "2012-12-02T02:11:56Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "c076393ad900d62c4805a42df10d9b364f629842681c00cce035487f", - "id": "63a8aa13-60fe-41c4-b079-77f6fdf3c841", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/63a8aa13-60fe-41c4-b079-77f6fdf3c841", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/63a8aa13-60fe-41c4-b079-77f6fdf3c841", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2012-12-02T02:11:56Z", - "user_id": "fake" - } -} \ No newline at end of file diff --git a/doc/api_samples/os-disk-config/server-get-resp.json b/doc/api_samples/os-disk-config/server-get-resp.json deleted file mode 100644 index 643f7113842..00000000000 --- a/doc/api_samples/os-disk-config/server-get-resp.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "server": { - "OS-DCF:diskConfig": "AUTO", - "accessIPv4": "", - "accessIPv6": "", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2012-12-02T02:11:55Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "c949ab4256cea23b6089b710aa2df48bf6577ed915278b62e33ad8bb", - "id": "5046e2f2-3b33-4041-b3cf-e085f73e78e7", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/5046e2f2-3b33-4041-b3cf-e085f73e78e7", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/5046e2f2-3b33-4041-b3cf-e085f73e78e7", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2012-12-02T02:11:55Z", - "key_name": null, - "user_id": "fake" - } -} \ No newline at end of file diff --git a/doc/api_samples/os-disk-config/server-post-req.json b/doc/api_samples/os-disk-config/server-post-req.json deleted file mode 100644 index 439f9b97acb..00000000000 --- a/doc/api_samples/os-disk-config/server-post-req.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "server" : { - "name" : "new-server-test", - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef" : "1", - "metadata" : { - "My Server Name" : "Apache1" - } - } -} diff --git a/doc/api_samples/os-disk-config/server-post-resp.json b/doc/api_samples/os-disk-config/server-post-resp.json deleted file mode 100644 index 79614ccabea..00000000000 --- a/doc/api_samples/os-disk-config/server-post-resp.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "server": { - "OS-DCF:diskConfig": "AUTO", - "adminPass": "CQH9gWzgkVno", - "id": "324dfb7d-f4a9-419a-9a19-237df04b443b", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/324dfb7d-f4a9-419a-9a19-237df04b443b", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/324dfb7d-f4a9-419a-9a19-237df04b443b", - "rel": "bookmark" - } - ] - } -} \ No newline at end of file diff --git a/doc/api_samples/os-disk-config/server-resize-post-req.json b/doc/api_samples/os-disk-config/server-resize-post-req.json deleted file mode 100644 index 55e11a31ed8..00000000000 --- a/doc/api_samples/os-disk-config/server-resize-post-req.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resize": { - "flavorRef": "3", - "OS-DCF:diskConfig": "AUTO" - } -} \ No newline at end of file diff --git a/doc/api_samples/os-evacuate/v2.29/server-evacuate-req.json b/doc/api_samples/os-evacuate/v2.29/server-evacuate-req.json new file mode 100644 index 00000000000..c01a0a555b4 --- /dev/null +++ b/doc/api_samples/os-evacuate/v2.29/server-evacuate-req.json @@ -0,0 +1,7 @@ +{ + "evacuate": { + "host": "testHost", + "adminPass": "MySecretPass", + "force": false + } +} diff --git a/doc/api_samples/os-extended-availability-zone/server-get-resp.json b/doc/api_samples/os-extended-availability-zone/server-get-resp.json deleted file mode 100644 index 01bcc70f9d7..00000000000 --- a/doc/api_samples/os-extended-availability-zone/server-get-resp.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "server": { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T02:54:56Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "b75d6736650f9b272223ceb48f4cde001de1856e381613a922117ab7", - "id": "f22e4521-d03a-4e9f-9fd3-016b9e227219", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f22e4521-d03a-4e9f-9fd3-016b9e227219", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f22e4521-d03a-4e9f-9fd3-016b9e227219", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-AZ:availability_zone": "nova", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T02:54:57Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-extended-availability-zone/servers-detail-resp.json b/doc/api_samples/os-extended-availability-zone/servers-detail-resp.json deleted file mode 100644 index 1d8b9763331..00000000000 --- a/doc/api_samples/os-extended-availability-zone/servers-detail-resp.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "servers": [ - { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T02:54:56Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "cf5540800371e53064a60b36ff9d6d1d6a8719ffc870c63a270c6bee", - "id": "55f43fa2-dc7c-4c0b-b21a-76f9abe516f9", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/55f43fa2-dc7c-4c0b-b21a-76f9abe516f9", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/55f43fa2-dc7c-4c0b-b21a-76f9abe516f9", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-AZ:availability_zone": "nova", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T02:54:58Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-extended-server-attributes/server-get-resp.json b/doc/api_samples/os-extended-server-attributes/server-get-resp.json deleted file mode 100644 index 4e9452c7e9a..00000000000 --- a/doc/api_samples/os-extended-server-attributes/server-get-resp.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "server": { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T02:55:07Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "3bf189131c61d0e71b0a8686a897a0f50d1693b48c47b721fe77155b", - "id": "c278163e-36f9-4cf2-b1ac-80db4c63f7a8", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-SRV-ATTR:host": "c5f474bf81474f9dbbc404d5b2e4e9b3", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", - "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T02:55:08Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-extended-server-attributes/server-post-resp.json b/doc/api_samples/os-extended-server-attributes/server-post-resp.json deleted file mode 100644 index 9b72d53c9e7..00000000000 --- a/doc/api_samples/os-extended-server-attributes/server-post-resp.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "server": { - "adminPass": "UCvmH8nHXm66", - "id": "a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "links": [ - { - "href": "http://openstack.example.com/v3/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "rel": "self" - }, - { - "href": "http://openstack.example.com/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "rel": "bookmark" - } - ] - } -} diff --git a/doc/api_samples/os-extended-server-attributes/servers-detail-resp.json b/doc/api_samples/os-extended-server-attributes/servers-detail-resp.json deleted file mode 100644 index 81e792e4e63..00000000000 --- a/doc/api_samples/os-extended-server-attributes/servers-detail-resp.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "servers": [ - { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T02:55:03Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "63cf07a9fd82e1d2294926ec5c0d2e1e0ca449224246df75e16f23dc", - "id": "a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-SRV-ATTR:host": "bc8efe4fdb7148a4bb921a2b03d17de6", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", - "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T02:55:05Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-extended-server-attributes/v2.16/server-get-resp.json b/doc/api_samples/os-extended-server-attributes/v2.16/server-get-resp.json deleted file mode 100644 index aa4afc5f4c6..00000000000 --- a/doc/api_samples/os-extended-server-attributes/v2.16/server-get-resp.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "server": { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T02:55:07Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "3bf189131c61d0e71b0a8686a897a0f50d1693b48c47b721fe77155b", - "id": "c278163e-36f9-4cf2-b1ac-80db4c63f7a8", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-SRV-ATTR:host": "c5f474bf81474f9dbbc404d5b2e4e9b3", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", - "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", - "OS-EXT-SRV-ATTR:hostname": "new-server-test", - "OS-EXT-SRV-ATTR:launch_index": 0, - "OS-EXT-SRV-ATTR:reservation_id": "r-12345678", - "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", - "OS-EXT-SRV-ATTR:kernel_id": null, - "OS-EXT-SRV-ATTR:ramdisk_id": null, - "OS-EXT-SRV-ATTR:user_data": null, - "locked": false, - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "progress": 0, - "status": "ACTIVE", - "host_status": "UP", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T02:55:08Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-extended-server-attributes/v2.16/servers-detail-resp.json b/doc/api_samples/os-extended-server-attributes/v2.16/servers-detail-resp.json deleted file mode 100644 index 5548c8db147..00000000000 --- a/doc/api_samples/os-extended-server-attributes/v2.16/servers-detail-resp.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "servers": [ - { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T02:55:03Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "63cf07a9fd82e1d2294926ec5c0d2e1e0ca449224246df75e16f23dc", - "id": "a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-SRV-ATTR:host": "bc8efe4fdb7148a4bb921a2b03d17de6", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", - "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", - "OS-EXT-SRV-ATTR:hostname": "new-server-test", - "OS-EXT-SRV-ATTR:launch_index": 0, - "OS-EXT-SRV-ATTR:reservation_id": "r-12345678", - "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", - "OS-EXT-SRV-ATTR:kernel_id": null, - "OS-EXT-SRV-ATTR:ramdisk_id": null, - "OS-EXT-SRV-ATTR:user_data": null, - "locked": false, - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "progress": 0, - "status": "ACTIVE", - "host_status": "UP", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T02:55:05Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-extended-server-attributes/v2.3/server-get-resp.json b/doc/api_samples/os-extended-server-attributes/v2.3/server-get-resp.json deleted file mode 100644 index 8e6d0d350c7..00000000000 --- a/doc/api_samples/os-extended-server-attributes/v2.3/server-get-resp.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "server": { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T02:55:07Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "3bf189131c61d0e71b0a8686a897a0f50d1693b48c47b721fe77155b", - "id": "c278163e-36f9-4cf2-b1ac-80db4c63f7a8", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v3/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8", - "rel": "self" - }, - { - "href": "http://openstack.example.com/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-SRV-ATTR:host": "c5f474bf81474f9dbbc404d5b2e4e9b3", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", - "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", - "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", - "OS-EXT-SRV-ATTR:launch_index": 0, - "OS-EXT-SRV-ATTR:kernel_id": "a5f474bf81474f9dbbc404d5b2e4e9b3", - "OS-EXT-SRV-ATTR:ramdisk_id": "b5f474bf81474f9dbbc404d5b2e4e9b3", - "OS-EXT-SRV-ATTR:hostname": "fake-hostname", - "OS-EXT-SRV-ATTR:root_device_name": "/dev/vda", - "OS-EXT-SRV-ATTR:userdata": "fake", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T02:55:08Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-extended-server-attributes/v2.3/servers-detail-resp.json b/doc/api_samples/os-extended-server-attributes/v2.3/servers-detail-resp.json deleted file mode 100644 index 6695ee22e5b..00000000000 --- a/doc/api_samples/os-extended-server-attributes/v2.3/servers-detail-resp.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "servers": [ - { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T02:55:03Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "63cf07a9fd82e1d2294926ec5c0d2e1e0ca449224246df75e16f23dc", - "id": "a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v3/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "rel": "self" - }, - { - "href": "http://openstack.example.com/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-SRV-ATTR:host": "bc8efe4fdb7148a4bb921a2b03d17de6", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", - "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", - "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", - "OS-EXT-SRV-ATTR:launch_index": 0, - "OS-EXT-SRV-ATTR:kernel_id": "a5f474bf81474f9dbbc404d5b2e4e9b3", - "OS-EXT-SRV-ATTR:ramdisk_id": "b5f474bf81474f9dbbc404d5b2e4e9b3", - "OS-EXT-SRV-ATTR:hostname": "fake-hostname", - "OS-EXT-SRV-ATTR:root_device_name": "/dev/vda", - "OS-EXT-SRV-ATTR:userdata": "fake", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T02:55:05Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-extended-status/server-get-resp.json b/doc/api_samples/os-extended-status/server-get-resp.json deleted file mode 100644 index b75fd3ff23a..00000000000 --- a/doc/api_samples/os-extended-status/server-get-resp.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "server": { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T03:07:06Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "46d2aa2d637bd55606304b611a1928627ee1278c149aef2206268d6e", - "id": "a868cb5e-c794-47bf-9cd8-e302b72bb94b", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/a868cb5e-c794-47bf-9cd8-e302b72bb94b", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/a868cb5e-c794-47bf-9cd8-e302b72bb94b", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-STS:power_state": 1, - "OS-EXT-STS:task_state": null, - "OS-EXT-STS:vm_state": "active", - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T03:07:07Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-extended-status/servers-detail-resp.json b/doc/api_samples/os-extended-status/servers-detail-resp.json deleted file mode 100644 index 14d9520dae6..00000000000 --- a/doc/api_samples/os-extended-status/servers-detail-resp.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "servers": [ - { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T03:07:09Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "a275e77473e464558c4aba0d68e1914d1164e7ee2f69affde7aaae2b", - "id": "6c8b5385-e74c-4fd5-add6-2fcf42d74a98", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/6c8b5385-e74c-4fd5-add6-2fcf42d74a98", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/6c8b5385-e74c-4fd5-add6-2fcf42d74a98", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-EXT-STS:power_state": 1, - "OS-EXT-STS:task_state": null, - "OS-EXT-STS:vm_state": "active", - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T03:07:10Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-extended-volumes/server-get-resp.json b/doc/api_samples/os-extended-volumes/server-get-resp.json deleted file mode 100644 index fd55c36b95b..00000000000 --- a/doc/api_samples/os-extended-volumes/server-get-resp.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "server": { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T03:22:28Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "8feef92e2152b9970b51dbdade024afbec7f8f03daf7cb335a3c1cb9", - "id": "7d62983e-23df-4320-bc89-bbc77f2a2e40", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/7d62983e-23df-4320-bc89-bbc77f2a2e40", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/7d62983e-23df-4320-bc89-bbc77f2a2e40", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "os-extended-volumes:volumes_attached": [ - { - "id": "volume_id1" - }, - { - "id": "volume_id2" - } - ], - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T03:22:29Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-extended-volumes/servers-detail-resp.json b/doc/api_samples/os-extended-volumes/servers-detail-resp.json deleted file mode 100644 index 02df4028221..00000000000 --- a/doc/api_samples/os-extended-volumes/servers-detail-resp.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "servers": [ - { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T03:22:32Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "f9622ec1b5ab6e3785661ea1c1e0294f95aecbcf27ac4cb60b06bd02", - "id": "8e479732-7701-48cd-af7a-04d84f51b742", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/8e479732-7701-48cd-af7a-04d84f51b742", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/8e479732-7701-48cd-af7a-04d84f51b742", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "os-extended-volumes:volumes_attached": [ - { - "id": "volume_id1" - }, - { - "id": "volume_id2" - } - ], - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T03:22:34Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-extended-volumes/v2.3/server-get-resp.json b/doc/api_samples/os-extended-volumes/v2.3/server-get-resp.json deleted file mode 100644 index e9847538471..00000000000 --- a/doc/api_samples/os-extended-volumes/v2.3/server-get-resp.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "server": { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T03:22:28Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "8feef92e2152b9970b51dbdade024afbec7f8f03daf7cb335a3c1cb9", - "id": "7d62983e-23df-4320-bc89-bbc77f2a2e40", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/7d62983e-23df-4320-bc89-bbc77f2a2e40", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/7d62983e-23df-4320-bc89-bbc77f2a2e40", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "os-extended-volumes:volumes_attached": [ - { - "id": "volume_id1", - "delete_on_termination": "True" - }, - { - "id": "volume_id2", - "delete_on_termination": "False" - } - ], - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T03:22:29Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-extended-volumes/v2.3/servers-detail-resp.json b/doc/api_samples/os-extended-volumes/v2.3/servers-detail-resp.json deleted file mode 100644 index 040f7baaafc..00000000000 --- a/doc/api_samples/os-extended-volumes/v2.3/servers-detail-resp.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "servers": [ - { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-09-16T03:22:32Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "f9622ec1b5ab6e3785661ea1c1e0294f95aecbcf27ac4cb60b06bd02", - "id": "8e479732-7701-48cd-af7a-04d84f51b742", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/8e479732-7701-48cd-af7a-04d84f51b742", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/8e479732-7701-48cd-af7a-04d84f51b742", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "os-extended-volumes:volumes_attached": [ - { - "id": "volume_id1", - "delete_on_termination": "True" - }, - { - "id": "volume_id2", - "delete_on_termination": "False" - } - ], - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-16T03:22:34Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-flavor-rxtx/flavor-rxtx-get-resp.json b/doc/api_samples/os-flavor-rxtx/flavor-rxtx-get-resp.json deleted file mode 100644 index 7dbf111076b..00000000000 --- a/doc/api_samples/os-flavor-rxtx/flavor-rxtx-get-resp.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "flavor": { - "OS-FLV-DISABLED:disabled": false, - "disk": 1, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/1", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ], - "name": "m1.tiny", - "ram": 512, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 1 - } -} diff --git a/doc/api_samples/os-flavor-rxtx/flavor-rxtx-list-resp.json b/doc/api_samples/os-flavor-rxtx/flavor-rxtx-list-resp.json deleted file mode 100644 index 9e8a4822a60..00000000000 --- a/doc/api_samples/os-flavor-rxtx/flavor-rxtx-list-resp.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "flavors": [ - { - "OS-FLV-DISABLED:disabled": false, - "disk": 1, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/1", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ], - "name": "m1.tiny", - "ram": 512, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 1 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 20, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "2", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/2", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/2", - "rel": "bookmark" - } - ], - "name": "m1.small", - "ram": 2048, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 1 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 40, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "3", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/3", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/3", - "rel": "bookmark" - } - ], - "name": "m1.medium", - "ram": 4096, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 2 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 80, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "4", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/4", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/4", - "rel": "bookmark" - } - ], - "name": "m1.large", - "ram": 8192, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 4 - }, - { - "OS-FLV-DISABLED:disabled": false, - "disk": 160, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "5", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/5", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/5", - "rel": "bookmark" - } - ], - "name": "m1.xlarge", - "ram": 16384, - "rxtx_factor": 1.0, - "swap": "", - "vcpus": 8 - } - ] -} diff --git a/doc/api_samples/os-flavor-rxtx/flavor-rxtx-post-req.json b/doc/api_samples/os-flavor-rxtx/flavor-rxtx-post-req.json deleted file mode 100644 index 937246a8b7c..00000000000 --- a/doc/api_samples/os-flavor-rxtx/flavor-rxtx-post-req.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "flavor": { - "name": "flavortest", - "ram": 1024, - "vcpus": 2, - "disk": 10, - "id": "100", - "rxtx_factor": 2.0 - } -} diff --git a/doc/api_samples/os-flavor-rxtx/flavor-rxtx-post-resp.json b/doc/api_samples/os-flavor-rxtx/flavor-rxtx-post-resp.json deleted file mode 100644 index 9a48582fbc4..00000000000 --- a/doc/api_samples/os-flavor-rxtx/flavor-rxtx-post-resp.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "flavor": { - "OS-FLV-DISABLED:disabled": false, - "disk": 10, - "OS-FLV-EXT-DATA:ephemeral": 0, - "os-flavor-access:is_public": true, - "id": "100", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/100", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/100", - "rel": "bookmark" - } - ], - "name": "flavortest", - "ram": 1024, - "rxtx_factor": 2.0, - "swap": "", - "vcpus": 2 - } -} diff --git a/doc/api_samples/os-hide-server-addresses/server-get-resp.json b/doc/api_samples/os-hide-server-addresses/server-get-resp.json index 0b913ac7d8a..e72c78649df 100644 --- a/doc/api_samples/os-hide-server-addresses/server-get-resp.json +++ b/doc/api_samples/os-hide-server-addresses/server-get-resp.json @@ -39,7 +39,27 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "b8b357f7100d4391828f2177c922ef93", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + {"id": "volume_id1"}, + {"id": "volume_id2"} + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", + "OS-SRV-USG:terminated_at": null, "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], "status": "ACTIVE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2013-09-24T14:39:01Z", diff --git a/doc/api_samples/os-hide-server-addresses/servers-details-resp.json b/doc/api_samples/os-hide-server-addresses/servers-details-resp.json index 645704f3776..e414e492246 100644 --- a/doc/api_samples/os-hide-server-addresses/servers-details-resp.json +++ b/doc/api_samples/os-hide-server-addresses/servers-details-resp.json @@ -40,7 +40,27 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "c3f14e9812ad496baf92ccfb3c61e15f", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + {"id": "volume_id1"}, + {"id": "volume_id2"} + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:53:12.774549", + "OS-SRV-USG:terminated_at": null, "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], "status": "ACTIVE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2013-09-24T14:44:01Z", diff --git a/doc/api_samples/os-hypervisors/v2.28/hypervisors-detail-resp.json b/doc/api_samples/os-hypervisors/v2.28/hypervisors-detail-resp.json new file mode 100644 index 00000000000..db5934bfb76 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.28/hypervisors-detail-resp.json @@ -0,0 +1,43 @@ +{ + "hypervisors": [ + { + "cpu_info": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "features": [ + "pge", + "clflush" + ], + "topology": { + "cores": 1, + "threads": 1, + "sockets": 4 + } + }, + "current_workload": 0, + "status": "enabled", + "state": "up", + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 1000, + "id": 1, + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "e6a37ee802d74863ab8b91ade8f12a67", + "id": 2, + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.28/hypervisors-list-resp.json b/doc/api_samples/os-hypervisors/v2.28/hypervisors-list-resp.json new file mode 100644 index 00000000000..375627499df --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.28/hypervisors-list-resp.json @@ -0,0 +1,10 @@ +{ + "hypervisors": [ + { + "hypervisor_hostname": "fake-mini", + "id": 1, + "state": "up", + "status": "enabled" + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.28/hypervisors-search-resp.json b/doc/api_samples/os-hypervisors/v2.28/hypervisors-search-resp.json new file mode 100644 index 00000000000..375627499df --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.28/hypervisors-search-resp.json @@ -0,0 +1,10 @@ +{ + "hypervisors": [ + { + "hypervisor_hostname": "fake-mini", + "id": 1, + "state": "up", + "status": "enabled" + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.28/hypervisors-show-resp.json b/doc/api_samples/os-hypervisors/v2.28/hypervisors-show-resp.json new file mode 100644 index 00000000000..03c9a5d2492 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.28/hypervisors-show-resp.json @@ -0,0 +1,41 @@ +{ + "hypervisor": { + "cpu_info": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "features": [ + "pge", + "clflush" + ], + "topology": { + "cores": 1, + "threads": 1, + "sockets": 4 + } + }, + "state": "up", + "status": "enabled", + "current_workload": 0, + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 1000, + "id": 1, + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "043b3cacf6f34c90a7245151fc8ebcda", + "id": 2, + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + } +} diff --git a/doc/api_samples/os-hypervisors/v2.28/hypervisors-statistics-resp.json b/doc/api_samples/os-hypervisors/v2.28/hypervisors-statistics-resp.json new file mode 100644 index 00000000000..2cfb51e7030 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.28/hypervisors-statistics-resp.json @@ -0,0 +1,16 @@ +{ + "hypervisor_statistics": { + "count": 1, + "current_workload": 0, + "disk_available_least": 0, + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "vcpus": 1, + "vcpus_used": 0 + } +} \ No newline at end of file diff --git a/doc/api_samples/os-hypervisors/v2.28/hypervisors-uptime-resp.json b/doc/api_samples/os-hypervisors/v2.28/hypervisors-uptime-resp.json new file mode 100644 index 00000000000..78521b37311 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.28/hypervisors-uptime-resp.json @@ -0,0 +1,9 @@ +{ + "hypervisor": { + "hypervisor_hostname": "fake-mini", + "id": 1, + "state": "up", + "status": "enabled", + "uptime": " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14" + } +} diff --git a/doc/api_samples/os-hypervisors/v2.28/hypervisors-with-servers-resp.json b/doc/api_samples/os-hypervisors/v2.28/hypervisors-with-servers-resp.json new file mode 100644 index 00000000000..61ccaa7dd79 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.28/hypervisors-with-servers-resp.json @@ -0,0 +1,20 @@ +{ + "hypervisors": [ + { + "hypervisor_hostname": "fake-mini", + "id": 1, + "state": "up", + "status": "enabled", + "servers": [ + { + "name": "test_server1", + "uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + { + "name": "test_server2", + "uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + } + ] + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.28/hypervisors-without-servers-resp.json b/doc/api_samples/os-hypervisors/v2.28/hypervisors-without-servers-resp.json new file mode 100644 index 00000000000..375627499df --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.28/hypervisors-without-servers-resp.json @@ -0,0 +1,10 @@ +{ + "hypervisors": [ + { + "hypervisor_hostname": "fake-mini", + "id": 1, + "state": "up", + "status": "enabled" + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.33/hypervisors-detail-resp.json b/doc/api_samples/os-hypervisors/v2.33/hypervisors-detail-resp.json new file mode 100644 index 00000000000..cc1962c197c --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.33/hypervisors-detail-resp.json @@ -0,0 +1,49 @@ +{ + "hypervisors": [ + { + "cpu_info": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "features": [ + "pge", + "clflush" + ], + "topology": { + "cores": 1, + "threads": 1, + "sockets": 4 + } + }, + "current_workload": 0, + "status": "enabled", + "state": "up", + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "host1", + "hypervisor_type": "fake", + "hypervisor_version": 1000, + "id": 2, + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "host1", + "id": 7, + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + } + ], + "hypervisors_links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors/detail?limit=1&marker=2", + "rel": "next" + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.33/hypervisors-list-resp.json b/doc/api_samples/os-hypervisors/v2.33/hypervisors-list-resp.json new file mode 100644 index 00000000000..9a5771df022 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.33/hypervisors-list-resp.json @@ -0,0 +1,16 @@ +{ + "hypervisors": [ + { + "hypervisor_hostname": "host1", + "id": 2, + "state": "up", + "status": "enabled" + } + ], + "hypervisors_links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors?limit=1&marker=2", + "rel": "next" + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-detail-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-detail-resp.json new file mode 100644 index 00000000000..8b837780531 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-detail-resp.json @@ -0,0 +1,49 @@ +{ + "hypervisors": [ + { + "cpu_info": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "features": [ + "pge", + "clflush" + ], + "topology": { + "cores": 1, + "threads": 1, + "sockets": 4 + } + }, + "current_workload": 0, + "status": "enabled", + "state": "up", + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "host2", + "hypervisor_type": "fake", + "hypervisor_version": 1000, + "id": "1bb62a04-c576-402c-8147-9e89757a09e3", + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "host1", + "id": "62f62f6e-a713-4cbe-87d3-3ecf8a1e0f8d", + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + } + ], + "hypervisors_links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors/detail?limit=1&marker=1bb62a04-c576-402c-8147-9e89757a09e3", + "rel": "next" + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-detail-with-servers-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-detail-with-servers-resp.json new file mode 100644 index 00000000000..c9342cb416e --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-detail-with-servers-resp.json @@ -0,0 +1,53 @@ +{ + "hypervisors": [ + { + "cpu_info": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "features": [ + "pge", + "clflush" + ], + "topology": { + "cores": 1, + "threads": 1, + "sockets": 4 + } + }, + "current_workload": 0, + "status": "enabled", + "servers": [ + { + "name": "test_server1", + "uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + { + "name": "test_server2", + "uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + } + ], + "state": "up", + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 1000, + "id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d", + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "host1", + "id": "5d343e1d-938e-4284-b98b-6a2b5406ba76", + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-list-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-list-resp.json new file mode 100644 index 00000000000..ec10b4a106e --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-list-resp.json @@ -0,0 +1,16 @@ +{ + "hypervisors": [ + { + "hypervisor_hostname": "fake-mini", + "id": "1bb62a04-c576-402c-8147-9e89757a09e3", + "state": "up", + "status": "enabled" + } + ], + "hypervisors_links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors?limit=1&marker=1bb62a04-c576-402c-8147-9e89757a09e3", + "rel": "next" + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-search-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-search-resp.json new file mode 100644 index 00000000000..9f8258439b2 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-search-resp.json @@ -0,0 +1,10 @@ +{ + "hypervisors": [ + { + "hypervisor_hostname": "fake-mini", + "id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d", + "state": "up", + "status": "enabled" + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-show-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-show-resp.json new file mode 100644 index 00000000000..ba26210541b --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-show-resp.json @@ -0,0 +1,41 @@ +{ + "hypervisor": { + "cpu_info": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "features": [ + "pge", + "clflush" + ], + "topology": { + "cores": 1, + "threads": 1, + "sockets": 4 + } + }, + "state": "up", + "status": "enabled", + "current_workload": 0, + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 1000, + "id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d", + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "043b3cacf6f34c90a7245151fc8ebcda", + "id": "5d343e1d-938e-4284-b98b-6a2b5406ba76", + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + } +} diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-show-with-servers-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-show-with-servers-resp.json new file mode 100644 index 00000000000..5477cb084d6 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-show-with-servers-resp.json @@ -0,0 +1,51 @@ +{ + "hypervisor": { + "cpu_info": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "features": [ + "pge", + "clflush" + ], + "topology": { + "cores": 1, + "threads": 1, + "sockets": 4 + } + }, + "state": "up", + "status": "enabled", + "servers": [ + { + "name": "test_server1", + "uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + { + "name": "test_server2", + "uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + } + ], + "current_workload": 0, + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 1000, + "id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d", + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "043b3cacf6f34c90a7245151fc8ebcda", + "id": "5d343e1d-938e-4284-b98b-6a2b5406ba76", + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + } +} diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-statistics-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-statistics-resp.json new file mode 100644 index 00000000000..2cfb51e7030 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-statistics-resp.json @@ -0,0 +1,16 @@ +{ + "hypervisor_statistics": { + "count": 1, + "current_workload": 0, + "disk_available_least": 0, + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "vcpus": 1, + "vcpus_used": 0 + } +} \ No newline at end of file diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-uptime-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-uptime-resp.json new file mode 100644 index 00000000000..56b97b2bf93 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-uptime-resp.json @@ -0,0 +1,9 @@ +{ + "hypervisor": { + "hypervisor_hostname": "fake-mini", + "id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d", + "state": "up", + "status": "enabled", + "uptime": " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14" + } +} diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-with-servers-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-with-servers-resp.json new file mode 100644 index 00000000000..d9dca46579f --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-with-servers-resp.json @@ -0,0 +1,20 @@ +{ + "hypervisors": [ + { + "hypervisor_hostname": "fake-mini", + "id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d", + "state": "up", + "status": "enabled", + "servers": [ + { + "name": "test_server1", + "uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + { + "name": "test_server2", + "uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + } + ] + } + ] +} diff --git a/doc/api_samples/os-hypervisors/v2.53/hypervisors-without-servers-resp.json b/doc/api_samples/os-hypervisors/v2.53/hypervisors-without-servers-resp.json new file mode 100644 index 00000000000..9f8258439b2 --- /dev/null +++ b/doc/api_samples/os-hypervisors/v2.53/hypervisors-without-servers-resp.json @@ -0,0 +1,10 @@ +{ + "hypervisors": [ + { + "hypervisor_hostname": "fake-mini", + "id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d", + "state": "up", + "status": "enabled" + } + ] +} diff --git a/doc/api_samples/os-instance-actions/v2.51/instance-action-get-non-admin-resp.json b/doc/api_samples/os-instance-actions/v2.51/instance-action-get-non-admin-resp.json new file mode 100644 index 00000000000..7043b4531f9 --- /dev/null +++ b/doc/api_samples/os-instance-actions/v2.51/instance-action-get-non-admin-resp.json @@ -0,0 +1,25 @@ +{ + "instanceAction": { + "action": "reboot", + "events": [ + { + "event": "schedule", + "finish_time": "2012-12-05T01:02:00.000000", + "result": "Success", + "start_time": "2012-12-05T01:00:02.000000" + }, + { + "event": "compute_create", + "finish_time": "2012-12-05T01:04:00.000000", + "result": "Success", + "start_time": "2012-12-05T01:03:00.000000" + } + ], + "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13", + "message": "", + "project_id": "147", + "request_id": "req-3293a3f1-b44c-4609-b8d2-d81b105636b8", + "start_time": "2012-12-05T00:00:00.000000", + "user_id": "789" + } +} diff --git a/doc/api_samples/os-instance-actions/v2.51/instance-action-get-resp.json b/doc/api_samples/os-instance-actions/v2.51/instance-action-get-resp.json new file mode 100644 index 00000000000..cbb6236c4ee --- /dev/null +++ b/doc/api_samples/os-instance-actions/v2.51/instance-action-get-resp.json @@ -0,0 +1,27 @@ +{ + "instanceAction": { + "action": "reboot", + "events": [ + { + "event": "schedule", + "finish_time": "2012-12-05T01:02:00.000000", + "result": "Success", + "start_time": "2012-12-05T01:00:02.000000", + "traceback": "" + }, + { + "event": "compute_create", + "finish_time": "2012-12-05T01:04:00.000000", + "result": "Success", + "start_time": "2012-12-05T01:03:00.000000", + "traceback": "" + } + ], + "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13", + "message": "", + "project_id": "147", + "request_id": "req-3293a3f1-b44c-4609-b8d2-d81b105636b8", + "start_time": "2012-12-05T00:00:00.000000", + "user_id": "789" + } +} diff --git a/doc/api_samples/os-instance-actions/v2.51/instance-actions-list-resp.json b/doc/api_samples/os-instance-actions/v2.51/instance-actions-list-resp.json new file mode 100644 index 00000000000..b9ae31ac53d --- /dev/null +++ b/doc/api_samples/os-instance-actions/v2.51/instance-actions-list-resp.json @@ -0,0 +1,22 @@ +{ + "instanceActions": [ + { + "action": "resize", + "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13", + "message": "", + "project_id": "842", + "request_id": "req-25517360-b757-47d3-be45-0e8d2a01b36a", + "start_time": "2012-12-05T01:00:00.000000", + "user_id": "789" + }, + { + "action": "reboot", + "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13", + "message": "", + "project_id": "147", + "request_id": "req-3293a3f1-b44c-4609-b8d2-d81b105636b8", + "start_time": "2012-12-05T00:00:00.000000", + "user_id": "789" + } + ] +} diff --git a/doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-index-get-resp.json b/doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-index-get-resp.json index 1d308d4ae80..ff493e7aa5d 100644 --- a/doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-index-get-resp.json +++ b/doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-index-get-resp.json @@ -1,17 +1,36 @@ { "instance_usage_audit_logs": { "hosts_not_run": [ - "f4eb7cfd155f4574967f8b55a7faed75" + "samplehost3" ], - "log": {}, - "num_hosts": 1, - "num_hosts_done": 0, + "log": { + "samplehost0": { + "errors": 1, + "instances": 1, + "message": "Instance usage audit ran for host samplehost0, 1 instances in 0.01 seconds.", + "state": "DONE" + }, + "samplehost1": { + "errors": 1, + "instances": 2, + "message": "Instance usage audit ran for host samplehost1, 2 instances in 0.01 seconds.", + "state": "DONE" + }, + "samplehost2": { + "errors": 1, + "instances": 3, + "message": "Instance usage audit ran for host samplehost2, 3 instances in 0.01 seconds.", + "state": "DONE" + } + }, + "num_hosts": 4, + "num_hosts_done": 3, "num_hosts_not_run": 1, "num_hosts_running": 0, - "overall_status": "0 of 1 hosts done. 0 errors.", - "period_beginning": "2012-12-01 00:00:00", - "period_ending": "2013-01-01 00:00:00", - "total_errors": 0, - "total_instances": 0 + "overall_status": "3 of 4 hosts done. 3 errors.", + "period_beginning": "2012-06-01 00:00:00", + "period_ending": "2012-07-01 00:00:00", + "total_errors": 3, + "total_instances": 6 } -} \ No newline at end of file +} diff --git a/doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-show-get-resp.json b/doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-show-get-resp.json index 2b5fe54c184..e7faabef4ff 100644 --- a/doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-show-get-resp.json +++ b/doc/api_samples/os-instance-usage-audit-log/inst-usage-audit-log-show-get-resp.json @@ -1,17 +1,36 @@ { "instance_usage_audit_log": { "hosts_not_run": [ - "8e33da2b48684ef3ab165444d6a7384c" + "samplehost3" ], - "log": {}, - "num_hosts": 1, - "num_hosts_done": 0, + "log": { + "samplehost0": { + "errors": 1, + "instances": 1, + "message": "Instance usage audit ran for host samplehost0, 1 instances in 0.01 seconds.", + "state": "DONE" + }, + "samplehost1": { + "errors": 1, + "instances": 2, + "message": "Instance usage audit ran for host samplehost1, 2 instances in 0.01 seconds.", + "state": "DONE" + }, + "samplehost2": { + "errors": 1, + "instances": 3, + "message": "Instance usage audit ran for host samplehost2, 3 instances in 0.01 seconds.", + "state": "DONE" + } + }, + "num_hosts": 4, + "num_hosts_done": 3, "num_hosts_not_run": 1, "num_hosts_running": 0, - "overall_status": "0 of 1 hosts done. 0 errors.", + "overall_status": "3 of 4 hosts done. 3 errors.", "period_beginning": "2012-06-01 00:00:00", "period_ending": "2012-07-01 00:00:00", - "total_errors": 0, - "total_instances": 0 + "total_errors": 3, + "total_instances": 6 } -} \ No newline at end of file +} diff --git a/doc/api_samples/os-migrate-server/v2.30/live-migrate-server.json b/doc/api_samples/os-migrate-server/v2.30/live-migrate-server.json new file mode 100644 index 00000000000..6267fc26c2b --- /dev/null +++ b/doc/api_samples/os-migrate-server/v2.30/live-migrate-server.json @@ -0,0 +1,7 @@ +{ + "os-migrateLive": { + "host": "01c0cadef72d47e28a672a76060d492c", + "block_migration": "auto", + "force": false + } +} diff --git a/doc/api_samples/os-multinic/multinic-add-fixed-ip-req.json b/doc/api_samples/os-multinic/multinic-add-fixed-ip-req.json index 2fb0fd9fe41..aa1249e69a3 100644 --- a/doc/api_samples/os-multinic/multinic-add-fixed-ip-req.json +++ b/doc/api_samples/os-multinic/multinic-add-fixed-ip-req.json @@ -1,5 +1,5 @@ { "addFixedIp": { - "networkId": 1 + "networkId": "e1882e38-38c2-4239-ade7-35d644cb963a" } -} +} \ No newline at end of file diff --git a/doc/api_samples/os-multinic/multinic-remove-fixed-ip-req.json b/doc/api_samples/os-multinic/multinic-remove-fixed-ip-req.json index 8f0d030deff..f93dab91f6e 100644 --- a/doc/api_samples/os-multinic/multinic-remove-fixed-ip-req.json +++ b/doc/api_samples/os-multinic/multinic-remove-fixed-ip-req.json @@ -1,5 +1,5 @@ { - "removeFixedIp":{ + "removeFixedIp": { "address": "10.0.0.4" - } + } } diff --git a/doc/api_samples/os-multiple-create/multiple-create-no-resv-post-resp.json b/doc/api_samples/os-multiple-create/multiple-create-no-resv-post-resp.json index e99905807d6..aa0a43a0b75 100644 --- a/doc/api_samples/os-multiple-create/multiple-create-no-resv-post-resp.json +++ b/doc/api_samples/os-multiple-create/multiple-create-no-resv-post-resp.json @@ -1,5 +1,6 @@ { "server": { + "OS-DCF:diskConfig": "AUTO", "adminPass": "wfksH3GTTseP", "id": "440cf918-3ee0-4143-b289-f63e1d2000e6", "links": [ @@ -11,6 +12,11 @@ "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/440cf918-3ee0-4143-b289-f63e1d2000e6", "rel": "bookmark" } + ], + "security_groups": [ + { + "name": "default" + } ] } } diff --git a/doc/api_samples/os-networks/network-show-resp.json b/doc/api_samples/os-networks/network-show-resp.json index 9741395c639..78b34950173 100644 --- a/doc/api_samples/os-networks/network-show-resp.json +++ b/doc/api_samples/os-networks/network-show-resp.json @@ -24,7 +24,7 @@ "netmask": "255.255.255.248", "netmask_v6": null, "priority": null, - "project_id": "1234", + "project_id": "6133f8b603924f45bc0c9e21f6df12fa", "rxtx_base": null, "share_address": false, "updated_at": "2011-08-16T09:26:13.048257", diff --git a/doc/api_samples/os-networks/networks-disassociate-req.json b/doc/api_samples/os-networks/networks-disassociate-req.json deleted file mode 100644 index 2e09d15a60a..00000000000 --- a/doc/api_samples/os-networks/networks-disassociate-req.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "disassociate": null -} diff --git a/doc/api_samples/os-networks/networks-list-resp.json b/doc/api_samples/os-networks/networks-list-resp.json index 49bdad58266..655fcaa8cca 100644 --- a/doc/api_samples/os-networks/networks-list-resp.json +++ b/doc/api_samples/os-networks/networks-list-resp.json @@ -25,7 +25,7 @@ "netmask": "255.255.255.248", "netmask_v6": null, "priority": null, - "project_id": "1234", + "project_id": "6133f8b603924f45bc0c9e21f6df12fa", "rxtx_base": null, "share_address": false, "updated_at": "2011-08-16T09:26:13.048257", diff --git a/doc/api_samples/os-pci/hypervisors-pci-detail-resp.json b/doc/api_samples/os-pci/hypervisors-pci-detail-resp.json deleted file mode 100644 index e2633ee8c6b..00000000000 --- a/doc/api_samples/os-pci/hypervisors-pci-detail-resp.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "hypervisors": [ - { - "cpu_info": "{\"arch\": \"x86_64\", \"model\": \"Nehalem\", \"vendor\": \"Intel\", \"features\": [\"pge\", \"clflush\"], \"topology\": {\"cores\": 1, \"threads\": 1, \"sockets\": 4}}", - "state": "up", - "status": "enabled", - "current_workload": 0, - "disk_available_least": 0, - "host_ip": "1.1.1.1", - "free_disk_gb": 1028, - "free_ram_mb": 7680, - "hypervisor_hostname": "fake-mini", - "hypervisor_type": "fake", - "hypervisor_version": 1000, - "id": 1, - "local_gb": 1028, - "local_gb_used": 0, - "memory_mb": 8192, - "memory_mb_used": 512, - "os-pci:pci_stats": [ - { - "count": 5, - "key1": "value1", - "keya": "valuea", - "product_id": "1520", - "vendor_id": "8086", - "numa_node": 1 - } - ], - "running_vms": 0, - "service": { - "host": "043b3cacf6f34c90a7245151fc8ebcda", - "id": 2, - "disabled_reason": null - }, - "vcpus": 1, - "vcpus_used": 0 - } - ] -} diff --git a/doc/api_samples/os-pci/hypervisors-pci-show-resp.json b/doc/api_samples/os-pci/hypervisors-pci-show-resp.json deleted file mode 100644 index f5ac8e2f605..00000000000 --- a/doc/api_samples/os-pci/hypervisors-pci-show-resp.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "hypervisor": { - "cpu_info": "{\"arch\": \"x86_64\", \"model\": \"Nehalem\", \"vendor\": \"Intel\", \"features\": [\"pge\", \"clflush\"], \"topology\": {\"cores\": 1, \"threads\": 1, \"sockets\": 4}}", - "current_workload": 0, - "disk_available_least": 0, - "host_ip": "1.1.1.1", - "state": "up", - "status": "enabled", - "free_disk_gb": 1028, - "free_ram_mb": 7680, - "hypervisor_hostname": "fake-mini", - "hypervisor_type": "fake", - "hypervisor_version": 1000, - "id": 1, - "local_gb": 1028, - "local_gb_used": 0, - "memory_mb": 8192, - "memory_mb_used": 512, - "os-pci:pci_stats": [ - { - "count": 5, - "key1": "value1", - "keya": "valuea", - "product_id": "1520", - "vendor_id": "8086", - "numa_node": 1 - } - ], - "running_vms": 0, - "service": { - "host": "043b3cacf6f34c90a7245151fc8ebcda", - "id": 2, - "disabled_reason": null - }, - "vcpus": 1, - "vcpus_used": 0 - } -} diff --git a/doc/api_samples/os-pci/pci-detail-resp.json b/doc/api_samples/os-pci/pci-detail-resp.json deleted file mode 100644 index 61cb17c6b4e..00000000000 --- a/doc/api_samples/os-pci/pci-detail-resp.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "pci_devices": [ - { - "address": "0000:04:10.0", - "compute_node_id": 1, - "dev_id": "pci_0000_04_10_0", - "dev_type": "type-VF", - "extra_info": { - "key1": "value1", - "key2": "value2" - }, - "id": 1, - "server_uuid": "69ba1044-0766-4ec0-b60d-09595de034a1", - "label": "label_8086_1520", - "product_id": "1520", - "status": "available", - "vendor_id": "8086" - }, - { - "address": "0000:04:10.1", - "compute_node_id": 1, - "dev_id": "pci_0000_04_10_1", - "dev_type": "type-VF", - "extra_info": { - "key3": "value3", - "key4": "value4" - }, - "id": 2, - "server_uuid": "d5b446a6-a1b4-4d01-b4f0-eac37b3a62fc", - "label": "label_8086_1520", - "product_id": "1520", - "status": "available", - "vendor_id": "8086" - } - ] -} diff --git a/doc/api_samples/os-pci/pci-index-resp.json b/doc/api_samples/os-pci/pci-index-resp.json deleted file mode 100644 index 6268f316dfe..00000000000 --- a/doc/api_samples/os-pci/pci-index-resp.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "pci_devices": [ - { - "address": "0000:04:10.0", - "compute_node_id": 1, - "id": 1, - "product_id": "1520", - "status": "available", - "vendor_id": "8086" - }, - { - "address": "0000:04:10.1", - "compute_node_id": 1, - "id": 2, - "product_id": "1520", - "status": "available", - "vendor_id": "8086" - } - ] -} \ No newline at end of file diff --git a/doc/api_samples/os-pci/pci-show-resp.json b/doc/api_samples/os-pci/pci-show-resp.json deleted file mode 100644 index 99777698817..00000000000 --- a/doc/api_samples/os-pci/pci-show-resp.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "pci_device": { - "address": "0000:04:10.0", - "compute_node_id": 1, - "dev_id": "pci_0000_04_10_0", - "dev_type": "type-VF", - "extra_info": { - "key1": "value1", - "key2": "value2" - }, - "id": 1, - "server_uuid": "69ba1044-0766-4ec0-b60d-09595de034a1", - "label": "label_8086_1520", - "product_id": "1520", - "status": "available", - "vendor_id": "8086" - } -} diff --git a/doc/api_samples/os-pci/server-get-resp.json b/doc/api_samples/os-pci/server-get-resp.json deleted file mode 100644 index 9f710e8ab60..00000000000 --- a/doc/api_samples/os-pci/server-get-resp.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "server": { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-11-25T03:45:54Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "b7e88944272df30c113572778bcf5527f02e9c2a745221214536c1a2", - "id": "9dafa6bc-7a9f-45b2-8177-11800ceb7224", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v3/servers/9dafa6bc-7a9f-45b2-8177-11800ceb7224", - "rel": "self" - }, - { - "href": "http://openstack.example.com/servers/9dafa6bc-7a9f-45b2-8177-11800ceb7224", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "os-pci:pci_devices": [ - { - "id": 1 - } - ], - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-11-25T03:45:54Z", - "user_id": "fake" - } -} \ No newline at end of file diff --git a/doc/api_samples/os-pci/servers-detail-resp.json b/doc/api_samples/os-pci/servers-detail-resp.json deleted file mode 100644 index 3e149f1cedb..00000000000 --- a/doc/api_samples/os-pci/servers-detail-resp.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "servers": [ - { - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-11-25T03:45:54Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "416f83c758ea0f9271018b278a9dcedb91b1190deaa598704b87219b", - "id": "ef440f98-04e8-46ea-ae74-e24d437040ea", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v3/servers/ef440f98-04e8-46ea-ae74-e24d437040ea", - "rel": "self" - }, - { - "href": "http://openstack.example.com/servers/ef440f98-04e8-46ea-ae74-e24d437040ea", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "os-pci:pci_devices": [ - { - "id": 1 - } - ], - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-11-25T03:45:54Z", - "user_id": "fake" - } - ] -} \ No newline at end of file diff --git a/doc/api_samples/os-personality/server-action-rebuild-req.json b/doc/api_samples/os-personality/server-action-rebuild-req.json deleted file mode 100644 index 0874437e0c0..00000000000 --- a/doc/api_samples/os-personality/server-action-rebuild-req.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "rebuild": { - "accessIPv4" : "1.2.3.4", - "accessIPv6" : "80fe::", - "imageRef": "70a599e0-31e7-49b7-b260-868f441e862b", - "name": "new-server-test", - "metadata": { - "meta_var": "meta_val" - }, - "personality": [ - { - "path": "/etc/banner.txt", - "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" - } - ] - } -} diff --git a/doc/api_samples/os-personality/server-action-rebuild-resp.json b/doc/api_samples/os-personality/server-action-rebuild-resp.json deleted file mode 100644 index b8888206e09..00000000000 --- a/doc/api_samples/os-personality/server-action-rebuild-resp.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "server": { - "accessIPv4" : "1.2.3.4", - "accessIPv6" : "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "version": 4 - } - ] - }, - "adminPass": "99WHAxN8gpvg", - "created": "2013-11-06T07:51:09Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "5c8072dbcda8ce3f26deb6662bd7718e1a6d349bdf2296911d1be4ac", - "id": "53a63a19-c145-47f8-9ae5-b39d6bff33ec", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/53a63a19-c145-47f8-9ae5-b39d6bff33ec", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/53a63a19-c145-47f8-9ae5-b39d6bff33ec", - "rel": "bookmark" - } - ], - "metadata": { - "meta_var": "meta_val" - }, - "name": "new-server-test", - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-11-06T07:51:11Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-personality/server-post-req.json b/doc/api_samples/os-personality/server-post-req.json deleted file mode 100644 index 5574b730508..00000000000 --- a/doc/api_samples/os-personality/server-post-req.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "server": { - "name": "new-server-test", - "imageRef": "70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef": "1", - "metadata": { - "My Server Name": "Apache1" - }, - "personality": [ - { - "path": "/etc/banner.txt", - "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" - } - ] - } -} diff --git a/doc/api_samples/os-personality/server-post-resp.json b/doc/api_samples/os-personality/server-post-resp.json deleted file mode 100644 index 9c31e93c9a0..00000000000 --- a/doc/api_samples/os-personality/server-post-resp.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "server": { - "adminPass": "n7JGBda664QG", - "id": "934760e1-2b0b-4f9e-a916-eac1e69839dc", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/934760e1-2b0b-4f9e-a916-eac1e69839dc", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/934760e1-2b0b-4f9e-a916-eac1e69839dc", - "rel": "bookmark" - } - ] - } -} \ No newline at end of file diff --git a/doc/api_samples/os-preserve-ephemeral-rebuild/server-action-rebuild-preserve-ephemeral-resp.json b/doc/api_samples/os-preserve-ephemeral-rebuild/server-action-rebuild-preserve-ephemeral-resp.json index 8ca9c86d698..38ef12daaa8 100644 --- a/doc/api_samples/os-preserve-ephemeral-rebuild/server-action-rebuild-preserve-ephemeral-resp.json +++ b/doc/api_samples/os-preserve-ephemeral-rebuild/server-action-rebuild-preserve-ephemeral-resp.json @@ -1,5 +1,6 @@ { "server": { + "OS-DCF:diskConfig": "AUTO", "accessIPv4": "1.2.3.4", "accessIPv6": "80fe::", "addresses": { diff --git a/doc/api_samples/os-quota-class-sets/quota-classes-show-get-resp.json b/doc/api_samples/os-quota-class-sets/quota-classes-show-get-resp.json index 6e477722dda..2fcfa2b9dd6 100644 --- a/doc/api_samples/os-quota-class-sets/quota-classes-show-get-resp.json +++ b/doc/api_samples/os-quota-class-sets/quota-classes-show-get-resp.json @@ -12,6 +12,7 @@ "metadata_items": 128, "ram": 51200, "security_group_rules": 20, - "security_groups": 10 + "security_groups": 10, + "networks": 3 } } diff --git a/doc/api_samples/os-quota-class-sets/quota-classes-update-post-req.json b/doc/api_samples/os-quota-class-sets/quota-classes-update-post-req.json index f074c829ffa..4e3af3fa86b 100644 --- a/doc/api_samples/os-quota-class-sets/quota-classes-update-post-req.json +++ b/doc/api_samples/os-quota-class-sets/quota-classes-update-post-req.json @@ -4,12 +4,14 @@ "cores": 50, "ram": 51200, "floating_ips": 10, + "fixed_ips": -1, "metadata_items": 128, "injected_files": 5, "injected_file_content_bytes": 10240, "injected_file_path_bytes": 255, "security_groups": 10, "security_group_rules": 20, - "key_pairs": 100 + "key_pairs": 100, + "networks": 3 } } diff --git a/doc/api_samples/os-quota-class-sets/quota-classes-update-post-resp.json b/doc/api_samples/os-quota-class-sets/quota-classes-update-post-resp.json index c86f86063b8..c58474b539b 100644 --- a/doc/api_samples/os-quota-class-sets/quota-classes-update-post-resp.json +++ b/doc/api_samples/os-quota-class-sets/quota-classes-update-post-resp.json @@ -11,6 +11,7 @@ "metadata_items": 128, "ram": 51200, "security_group_rules": 20, - "security_groups": 10 + "security_groups": 10, + "networks": 3 } } diff --git a/doc/api_samples/os-quota-class-sets/v2.50/quota-classes-show-get-resp.json b/doc/api_samples/os-quota-class-sets/v2.50/quota-classes-show-get-resp.json new file mode 100644 index 00000000000..36ea6ee519b --- /dev/null +++ b/doc/api_samples/os-quota-class-sets/v2.50/quota-classes-show-get-resp.json @@ -0,0 +1,15 @@ +{ + "quota_class_set": { + "cores": 20, + "id": "test_class", + "injected_file_content_bytes": 10240, + "injected_file_path_bytes": 255, + "injected_files": 5, + "instances": 10, + "key_pairs": 100, + "metadata_items": 128, + "ram": 51200, + "server_groups": 10, + "server_group_members": 10 + } +} diff --git a/doc/api_samples/os-quota-class-sets/v2.50/quota-classes-update-post-req.json b/doc/api_samples/os-quota-class-sets/v2.50/quota-classes-update-post-req.json new file mode 100644 index 00000000000..ecfb85588bd --- /dev/null +++ b/doc/api_samples/os-quota-class-sets/v2.50/quota-classes-update-post-req.json @@ -0,0 +1,14 @@ +{ + "quota_class_set": { + "instances": 50, + "cores": 50, + "ram": 51200, + "metadata_items": 128, + "injected_files": 5, + "injected_file_content_bytes": 10240, + "injected_file_path_bytes": 255, + "key_pairs": 100, + "server_groups": 10, + "server_group_members": 10 + } +} diff --git a/doc/api_samples/os-quota-class-sets/v2.50/quota-classes-update-post-resp.json b/doc/api_samples/os-quota-class-sets/v2.50/quota-classes-update-post-resp.json new file mode 100644 index 00000000000..fa649d52c92 --- /dev/null +++ b/doc/api_samples/os-quota-class-sets/v2.50/quota-classes-update-post-resp.json @@ -0,0 +1,14 @@ +{ + "quota_class_set": { + "cores": 50, + "injected_file_content_bytes": 10240, + "injected_file_path_bytes": 255, + "injected_files": 5, + "instances": 50, + "key_pairs": 100, + "metadata_items": 128, + "ram": 51200, + "server_groups": 10, + "server_group_members": 10 + } +} diff --git a/doc/api_samples/os-quota-sets/quotas-show-defaults-get-resp.json b/doc/api_samples/os-quota-sets/quotas-show-defaults-get-resp.json index c93f809d598..e03bc651eba 100644 --- a/doc/api_samples/os-quota-sets/quotas-show-defaults-get-resp.json +++ b/doc/api_samples/os-quota-sets/quotas-show-defaults-get-resp.json @@ -14,6 +14,7 @@ "security_group_rules": 20, "security_groups": 10, "server_groups": 10, - "server_group_members": 10 + "server_group_members": 10, + "networks": 3 } } diff --git a/doc/api_samples/os-quota-sets/quotas-show-detail-get-resp.json b/doc/api_samples/os-quota-sets/quotas-show-detail-get-resp.json new file mode 100644 index 00000000000..bca3a1e82a0 --- /dev/null +++ b/doc/api_samples/os-quota-sets/quotas-show-detail-get-resp.json @@ -0,0 +1,80 @@ +{ + "quota_set": { + "cores": { + "in_use": 0, + "limit": 20, + "reserved": 0 + }, + "fixed_ips": { + "in_use": 0, + "limit": -1, + "reserved": 0 + }, + "floating_ips": { + "in_use": 0, + "limit": 10, + "reserved": 0 + }, + "id": "fake_tenant", + "injected_file_content_bytes": { + "in_use": 0, + "limit": 10240, + "reserved": 0 + }, + "injected_file_path_bytes": { + "in_use": 0, + "limit": 255, + "reserved": 0 + }, + "injected_files": { + "in_use": 0, + "limit": 5, + "reserved": 0 + }, + "instances": { + "in_use": 0, + "limit": 10, + "reserved": 0 + }, + "key_pairs": { + "in_use": 0, + "limit": 100, + "reserved": 0 + }, + "metadata_items": { + "in_use": 0, + "limit": 128, + "reserved": 0 + }, + "ram": { + "in_use": 0, + "limit": 51200, + "reserved": 0 + }, + "security_group_rules": { + "in_use": 0, + "limit": 20, + "reserved": 0 + }, + "security_groups": { + "in_use": 0, + "limit": 10, + "reserved": 0 + }, + "server_group_members": { + "in_use": 0, + "limit": 10, + "reserved": 0 + }, + "server_groups": { + "in_use": 0, + "limit": 10, + "reserved": 0 + }, + "networks": { + "in_use": 2, + "limit": 3, + "reserved": 0 + } + } +} diff --git a/doc/api_samples/os-quota-sets/quotas-show-get-resp.json b/doc/api_samples/os-quota-sets/quotas-show-get-resp.json index c93f809d598..e03bc651eba 100644 --- a/doc/api_samples/os-quota-sets/quotas-show-get-resp.json +++ b/doc/api_samples/os-quota-sets/quotas-show-get-resp.json @@ -14,6 +14,7 @@ "security_group_rules": 20, "security_groups": 10, "server_groups": 10, - "server_group_members": 10 + "server_group_members": 10, + "networks": 3 } } diff --git a/doc/api_samples/os-quota-sets/quotas-update-force-post-resp.json b/doc/api_samples/os-quota-sets/quotas-update-force-post-resp.json index 66bbd00f44e..ab901db2c92 100644 --- a/doc/api_samples/os-quota-sets/quotas-update-force-post-resp.json +++ b/doc/api_samples/os-quota-sets/quotas-update-force-post-resp.json @@ -13,6 +13,7 @@ "security_group_rules": 20, "security_groups": 10, "server_groups": 10, - "server_group_members": 10 + "server_group_members": 10, + "networks": 3 } } diff --git a/doc/api_samples/os-quota-sets/quotas-update-post-resp.json b/doc/api_samples/os-quota-sets/quotas-update-post-resp.json index a0f3b1a77d2..f17ad087f17 100644 --- a/doc/api_samples/os-quota-sets/quotas-update-post-resp.json +++ b/doc/api_samples/os-quota-sets/quotas-update-post-resp.json @@ -13,6 +13,7 @@ "security_group_rules": 20, "security_groups": 45, "server_groups": 10, - "server_group_members": 10 + "server_group_members": 10, + "networks": 3 } } diff --git a/doc/api_samples/os-quota-sets/user-quotas-show-get-resp.json b/doc/api_samples/os-quota-sets/user-quotas-show-get-resp.json index c93f809d598..e03bc651eba 100644 --- a/doc/api_samples/os-quota-sets/user-quotas-show-get-resp.json +++ b/doc/api_samples/os-quota-sets/user-quotas-show-get-resp.json @@ -14,6 +14,7 @@ "security_group_rules": 20, "security_groups": 10, "server_groups": 10, - "server_group_members": 10 + "server_group_members": 10, + "networks": 3 } } diff --git a/doc/api_samples/os-quota-sets/user-quotas-update-post-resp.json b/doc/api_samples/os-quota-sets/user-quotas-update-post-resp.json index cf4e8c3d665..4ac251c0868 100644 --- a/doc/api_samples/os-quota-sets/user-quotas-update-post-resp.json +++ b/doc/api_samples/os-quota-sets/user-quotas-update-post-resp.json @@ -13,6 +13,7 @@ "security_group_rules": 20, "security_groups": 10, "server_groups": 10, - "server_group_members": 10 + "server_group_members": 10, + "networks": 3 } } diff --git a/doc/api_samples/os-quota-sets/v2.36/quotas-show-defaults-get-resp.json b/doc/api_samples/os-quota-sets/v2.36/quotas-show-defaults-get-resp.json new file mode 100644 index 00000000000..1a4d5e5e15e --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/quotas-show-defaults-get-resp.json @@ -0,0 +1,15 @@ +{ + "quota_set": { + "cores": 20, + "id": "fake_tenant", + "injected_file_content_bytes": 10240, + "injected_file_path_bytes": 255, + "injected_files": 5, + "instances": 10, + "key_pairs": 100, + "metadata_items": 128, + "ram": 51200, + "server_groups": 10, + "server_group_members": 10 + } +} diff --git a/doc/api_samples/os-quota-sets/v2.36/quotas-show-detail-get-resp.json b/doc/api_samples/os-quota-sets/v2.36/quotas-show-detail-get-resp.json new file mode 100644 index 00000000000..fe6762f85df --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/quotas-show-detail-get-resp.json @@ -0,0 +1,55 @@ +{ + "quota_set": { + "cores": { + "in_use": 0, + "limit": 20, + "reserved": 0 + }, + "id": "fake_tenant", + "injected_file_content_bytes": { + "in_use": 0, + "limit": 10240, + "reserved": 0 + }, + "injected_file_path_bytes": { + "in_use": 0, + "limit": 255, + "reserved": 0 + }, + "injected_files": { + "in_use": 0, + "limit": 5, + "reserved": 0 + }, + "instances": { + "in_use": 0, + "limit": 10, + "reserved": 0 + }, + "key_pairs": { + "in_use": 0, + "limit": 100, + "reserved": 0 + }, + "metadata_items": { + "in_use": 0, + "limit": 128, + "reserved": 0 + }, + "ram": { + "in_use": 0, + "limit": 51200, + "reserved": 0 + }, + "server_group_members": { + "in_use": 0, + "limit": 10, + "reserved": 0 + }, + "server_groups": { + "in_use": 0, + "limit": 10, + "reserved": 0 + } + } +} diff --git a/doc/api_samples/os-quota-sets/v2.36/quotas-show-get-resp.json b/doc/api_samples/os-quota-sets/v2.36/quotas-show-get-resp.json new file mode 100644 index 00000000000..1a4d5e5e15e --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/quotas-show-get-resp.json @@ -0,0 +1,15 @@ +{ + "quota_set": { + "cores": 20, + "id": "fake_tenant", + "injected_file_content_bytes": 10240, + "injected_file_path_bytes": 255, + "injected_files": 5, + "instances": 10, + "key_pairs": 100, + "metadata_items": 128, + "ram": 51200, + "server_groups": 10, + "server_group_members": 10 + } +} diff --git a/doc/api_samples/os-quota-sets/v2.36/quotas-update-force-post-req.json b/doc/api_samples/os-quota-sets/v2.36/quotas-update-force-post-req.json new file mode 100644 index 00000000000..460d2ac1c7c --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/quotas-update-force-post-req.json @@ -0,0 +1,6 @@ +{ + "quota_set": { + "force": "True", + "instances": 45 + } +} \ No newline at end of file diff --git a/doc/api_samples/os-quota-sets/v2.36/quotas-update-force-post-resp.json b/doc/api_samples/os-quota-sets/v2.36/quotas-update-force-post-resp.json new file mode 100644 index 00000000000..935766b3380 --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/quotas-update-force-post-resp.json @@ -0,0 +1,14 @@ +{ + "quota_set": { + "cores": 20, + "injected_file_content_bytes": 10240, + "injected_file_path_bytes": 255, + "injected_files": 5, + "instances": 45, + "key_pairs": 100, + "metadata_items": 128, + "ram": 51200, + "server_groups": 10, + "server_group_members": 10 + } +} diff --git a/doc/api_samples/os-quota-sets/v2.36/quotas-update-post-req.json b/doc/api_samples/os-quota-sets/v2.36/quotas-update-post-req.json new file mode 100644 index 00000000000..b11081d7e45 --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/quotas-update-post-req.json @@ -0,0 +1,5 @@ +{ + "quota_set": { + "instances": 45 + } +} \ No newline at end of file diff --git a/doc/api_samples/os-quota-sets/v2.36/quotas-update-post-resp.json b/doc/api_samples/os-quota-sets/v2.36/quotas-update-post-resp.json new file mode 100644 index 00000000000..935766b3380 --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/quotas-update-post-resp.json @@ -0,0 +1,14 @@ +{ + "quota_set": { + "cores": 20, + "injected_file_content_bytes": 10240, + "injected_file_path_bytes": 255, + "injected_files": 5, + "instances": 45, + "key_pairs": 100, + "metadata_items": 128, + "ram": 51200, + "server_groups": 10, + "server_group_members": 10 + } +} diff --git a/doc/api_samples/os-quota-sets/v2.36/user-quotas-show-get-resp.json b/doc/api_samples/os-quota-sets/v2.36/user-quotas-show-get-resp.json new file mode 100644 index 00000000000..1a4d5e5e15e --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/user-quotas-show-get-resp.json @@ -0,0 +1,15 @@ +{ + "quota_set": { + "cores": 20, + "id": "fake_tenant", + "injected_file_content_bytes": 10240, + "injected_file_path_bytes": 255, + "injected_files": 5, + "instances": 10, + "key_pairs": 100, + "metadata_items": 128, + "ram": 51200, + "server_groups": 10, + "server_group_members": 10 + } +} diff --git a/doc/api_samples/os-quota-sets/v2.36/user-quotas-update-post-req.json b/doc/api_samples/os-quota-sets/v2.36/user-quotas-update-post-req.json new file mode 100644 index 00000000000..cbd6f18af1d --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/user-quotas-update-post-req.json @@ -0,0 +1,6 @@ +{ + "quota_set": { + "force": "True", + "instances": 9 + } +} \ No newline at end of file diff --git a/doc/api_samples/os-quota-sets/v2.36/user-quotas-update-post-resp.json b/doc/api_samples/os-quota-sets/v2.36/user-quotas-update-post-resp.json new file mode 100644 index 00000000000..9ae2981a820 --- /dev/null +++ b/doc/api_samples/os-quota-sets/v2.36/user-quotas-update-post-resp.json @@ -0,0 +1,14 @@ +{ + "quota_set": { + "cores": 20, + "injected_file_content_bytes": 10240, + "injected_file_path_bytes": 255, + "injected_files": 5, + "instances": 9, + "key_pairs": 100, + "metadata_items": 128, + "ram": 51200, + "server_groups": 10, + "server_group_members": 10 + } +} diff --git a/doc/api_samples/os-rescue/server-get-resp-rescue.json b/doc/api_samples/os-rescue/server-get-resp-rescue.json index 1f0c4267894..62e741a4340 100644 --- a/doc/api_samples/os-rescue/server-get-resp-rescue.json +++ b/doc/api_samples/os-rescue/server-get-resp-rescue.json @@ -51,6 +51,23 @@ "status": "RESCUE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2013-09-18T07:22:11Z", - "user_id": "fake" + "user_id": "fake", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "b8b357f7100d4391828f2177c922ef93", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "rescued", + "os-extended-volumes:volumes_attached": [], + "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", + "OS-SRV-USG:terminated_at": null, + "security_groups": [ + { + "name": "default" + } + ] } } diff --git a/doc/api_samples/os-rescue/server-get-resp-unrescue.json b/doc/api_samples/os-rescue/server-get-resp-unrescue.json index 9e9c1d5ed07..a8c9f271aaf 100644 --- a/doc/api_samples/os-rescue/server-get-resp-unrescue.json +++ b/doc/api_samples/os-rescue/server-get-resp-unrescue.json @@ -52,6 +52,23 @@ "status": "ACTIVE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2013-09-18T07:22:12Z", - "user_id": "fake" + "user_id": "fake", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "b8b357f7100d4391828f2177c922ef93", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [], + "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", + "OS-SRV-USG:terminated_at": null, + "security_groups": [ + { + "name": "default" + } + ] } } diff --git a/doc/api_samples/os-scheduler-hints/scheduler-hints-post-req.json b/doc/api_samples/os-scheduler-hints/scheduler-hints-post-req.json deleted file mode 100644 index 981b5b63b5c..00000000000 --- a/doc/api_samples/os-scheduler-hints/scheduler-hints-post-req.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "server" : { - "name" : "new-server-test", - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef" : "1" - }, - "OS-SCH-HNT:scheduler_hints": { - "same_host": "48e6a9f6-30af-47e0-bc04-acaed113bb4e" - } -} diff --git a/doc/api_samples/os-scheduler-hints/scheduler-hints-post-resp.json b/doc/api_samples/os-scheduler-hints/scheduler-hints-post-resp.json deleted file mode 100644 index 13471f61385..00000000000 --- a/doc/api_samples/os-scheduler-hints/scheduler-hints-post-resp.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "server": { - "adminPass": "erQXgJ8NBDD4", - "id": "4c8b1df3-46f7-4555-98d8-cdb869aaf9ad", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/4c8b1df3-46f7-4555-98d8-cdb869aaf9ad", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/4c8b1df3-46f7-4555-98d8-cdb869aaf9ad", - "rel": "bookmark" - } - ] - } -} diff --git a/doc/api_samples/os-security-groups/security-group-rules-post-req.json b/doc/api_samples/os-security-groups/security-group-rules-post-req.json new file mode 100644 index 00000000000..c5116c8f0b3 --- /dev/null +++ b/doc/api_samples/os-security-groups/security-group-rules-post-req.json @@ -0,0 +1,9 @@ +{ + "security_group_rule": { + "parent_group_id": "21111111-1111-1111-1111-111111111112", + "ip_protocol": "tcp", + "from_port": 22, + "to_port": 22, + "cidr": "10.0.0.0/24" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-security-groups/security-group-rules-post-resp.json b/doc/api_samples/os-security-groups/security-group-rules-post-resp.json new file mode 100644 index 00000000000..5bfd2891067 --- /dev/null +++ b/doc/api_samples/os-security-groups/security-group-rules-post-resp.json @@ -0,0 +1,13 @@ +{ + "security_group_rule": { + "from_port": 22, + "group": {}, + "id": "00000000-0000-0000-0000-000000000000", + "ip_protocol": "tcp", + "ip_range": { + "cidr": "10.0.0.0/24" + }, + "parent_group_id": "11111111-1111-1111-1111-111111111111", + "to_port": 22 + } +} \ No newline at end of file diff --git a/doc/api_samples/os-security-groups/server-get-resp.json b/doc/api_samples/os-security-groups/server-get-resp.json deleted file mode 100644 index 74734e9a52e..00000000000 --- a/doc/api_samples/os-security-groups/server-get-resp.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "server": { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2014-09-18T10:13:33Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "24451d49cba30e60300a5b928ebc93a2d0b43c084a677b0a14fd678b", - "id": "b08eb8d8-db43-44fb-bd89-dfe3302b84ef", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/b08eb8d8-db43-44fb-bd89-dfe3302b84ef", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/b08eb8d8-db43-44fb-bd89-dfe3302b84ef", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "progress": 0, - "security_groups": [ - { - "name": "test" - } - ], - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2014-09-18T10:13:34Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-security-groups/server-post-req.json b/doc/api_samples/os-security-groups/server-post-req.json deleted file mode 100644 index cc589208cc1..00000000000 --- a/doc/api_samples/os-security-groups/server-post-req.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "server" : { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "name" : "new-server-test", - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef" : "1", - "metadata" : { - "My Server Name" : "Apache1" - }, - "security_groups": [{"name": "test"}] - } -} diff --git a/doc/api_samples/os-security-groups/server-post-resp.json b/doc/api_samples/os-security-groups/server-post-resp.json deleted file mode 100644 index b276c8211e4..00000000000 --- a/doc/api_samples/os-security-groups/server-post-resp.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "server": { - "adminPass": "xhS2khTdkRkT", - "id": "60874907-c72b-4a01-805d-54c992510e47", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/60874907-c72b-4a01-805d-54c992510e47", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/60874907-c72b-4a01-805d-54c992510e47", - "rel": "bookmark" - } - ], - "security_groups": [ - { - "name": "test" - } - ] - } -} \ No newline at end of file diff --git a/doc/api_samples/os-security-groups/servers-detail-resp.json b/doc/api_samples/os-security-groups/servers-detail-resp.json deleted file mode 100644 index 71b75c82c8a..00000000000 --- a/doc/api_samples/os-security-groups/servers-detail-resp.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "servers": [ - { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2014-09-18T10:13:33Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "2ab794bccd321fe64f9f8b679266aa2c96825f467434bbdd71b09b1d", - "id": "d182742c-6f20-479c-8e32-f79f9c9df6e3", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/d182742c-6f20-479c-8e32-f79f9c9df6e3", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/d182742c-6f20-479c-8e32-f79f9c9df6e3", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "progress": 0, - "security_groups": [ - { - "name": "test" - } - ], - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2014-09-18T10:13:34Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-server-diagnostics/v2.48/server-diagnostics-get-resp.json b/doc/api_samples/os-server-diagnostics/v2.48/server-diagnostics-get-resp.json new file mode 100644 index 00000000000..d120973981a --- /dev/null +++ b/doc/api_samples/os-server-diagnostics/v2.48/server-diagnostics-get-resp.json @@ -0,0 +1,46 @@ +{ + "config_drive": true, + "cpu_details": [ + { + "id": 0, + "time": 17300000000, + "utilisation": 15 + } + ], + "disk_details": [ + { + "errors_count": 1, + "read_bytes": 262144, + "read_requests": 112, + "write_bytes": 5778432, + "write_requests": 488 + } + ], + "driver": "libvirt", + "hypervisor": "kvm", + "hypervisor_os": "ubuntu", + "memory_details": { + "maximum": 524288, + "used": 0 + }, + "nic_details": [ + { + "mac_address": "01:23:45:67:89:ab", + "rx_drop": 200, + "rx_errors": 100, + "rx_octets": 2070139, + "rx_packets": 26701, + "rx_rate": 300, + "tx_drop": 500, + "tx_errors": 400, + "tx_octets": 140208, + "tx_packets": 662, + "tx_rate": 600 + } + ], + "num_cpus": 1, + "num_disks": 1, + "num_nics": 1, + "state": "running", + "uptime": 46664 +} diff --git a/doc/api_samples/os-server-tags/v2.26/server-tags-index-resp.json b/doc/api_samples/os-server-tags/v2.26/server-tags-index-resp.json new file mode 100644 index 00000000000..d3bfe89eb53 --- /dev/null +++ b/doc/api_samples/os-server-tags/v2.26/server-tags-index-resp.json @@ -0,0 +1,3 @@ +{ + "tags": ["tag1", "tag2"] +} diff --git a/doc/api_samples/os-server-tags/v2.26/server-tags-put-all-req.json b/doc/api_samples/os-server-tags/v2.26/server-tags-put-all-req.json new file mode 100644 index 00000000000..d3bfe89eb53 --- /dev/null +++ b/doc/api_samples/os-server-tags/v2.26/server-tags-put-all-req.json @@ -0,0 +1,3 @@ +{ + "tags": ["tag1", "tag2"] +} diff --git a/doc/api_samples/os-server-tags/v2.26/server-tags-put-all-resp.json b/doc/api_samples/os-server-tags/v2.26/server-tags-put-all-resp.json new file mode 100644 index 00000000000..d3bfe89eb53 --- /dev/null +++ b/doc/api_samples/os-server-tags/v2.26/server-tags-put-all-resp.json @@ -0,0 +1,3 @@ +{ + "tags": ["tag1", "tag2"] +} diff --git a/doc/api_samples/os-server-tags/v2.26/server-tags-show-details-resp.json b/doc/api_samples/os-server-tags/v2.26/server-tags-show-details-resp.json new file mode 100644 index 00000000000..1fdc541a73e --- /dev/null +++ b/doc/api_samples/os-server-tags/v2.26/server-tags-show-details-resp.json @@ -0,0 +1,85 @@ +{ + "server": { + "tags": ["tag1", "tag2"], + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "addr": "192.168.0.3", + "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", + "OS-EXT-IPS:type": "fixed", + "version": 4 + } + ] + }, + "created": "2012-12-02T02:11:55Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostId": "c949ab4256cea23b6089b710aa2df48bf6577ed915278b62e33ad8bb", + "id": "5046e2f2-3b33-4041-b3cf-e085f73e78e7", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/5046e2f2-3b33-4041-b3cf-e085f73e78e7", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/5046e2f2-3b33-4041-b3cf-e085f73e78e7", + "rel": "bookmark" + } + ], + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "progress": 0, + "status": "ACTIVE", + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2012-12-02T02:11:55Z", + "key_name": null, + "user_id": "fake", + "locked": false, + "description": null, + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "b8b357f7100d4391828f2177c922ef93", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:hostname": "fake-hostname", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [], + "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", + "OS-SRV-USG:terminated_at": null, + "security_groups": [ + { + "name": "default" + } + ], + "host_status": "UP" + } +} diff --git a/doc/api_samples/os-server-tags/v2.26/servers-tags-details-resp.json b/doc/api_samples/os-server-tags/v2.26/servers-tags-details-resp.json new file mode 100644 index 00000000000..c1ea45a29ce --- /dev/null +++ b/doc/api_samples/os-server-tags/v2.26/servers-tags-details-resp.json @@ -0,0 +1,87 @@ +{ + "servers": [ + { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "addr": "192.168.0.3", + "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", + "OS-EXT-IPS:type": "fixed", + "version": 4 + } + ] + }, + "created": "2013-09-03T04:01:32Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostId": "bcf92836fc9ed4203a75cb0337afc7f917d2be504164b995c2334b25", + "id": "f5dc173b-6804-445a-a6d8-c705dad5b5eb", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "key_name": null, + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb", + "rel": "bookmark" + } + ], + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "progress": 0, + "status": "ACTIVE", + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2013-09-03T04:01:32Z", + "user_id": "fake", + "locked": false, + "tags": ["tag1", "tag2"], + "description": null, + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "c3f14e9812ad496baf92ccfb3c61e15f", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:hostname": "fake-hostname", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [], + "OS-SRV-USG:launched_at": "2013-09-23T13:53:12.774549", + "OS-SRV-USG:terminated_at": null, + "security_groups": [ + { + "name": "default" + } + ], + "host_status": "UP" + } + ] +} diff --git a/doc/api_samples/os-server-usage/server-get-resp.json b/doc/api_samples/os-server-usage/server-get-resp.json deleted file mode 100644 index 57ccf0c1d08..00000000000 --- a/doc/api_samples/os-server-usage/server-get-resp.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "server": { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-08-15T08:12:40Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "73cf3a40601b63f5992894be2daa3712dd599d1c919984951e21edda", - "id": "cee6d136-e378-4cfc-9eec-71797f025991", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/cee6d136-e378-4cfc-9eec-71797f025991", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/cee6d136-e378-4cfc-9eec-71797f025991", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-SRV-USG:launched_at": "2013-08-15T08:12:40.108903", - "OS-SRV-USG:terminated_at": null, - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-08-15T08:12:40Z", - "user_id": "fake" - } -} diff --git a/doc/api_samples/os-server-usage/servers-detail-resp.json b/doc/api_samples/os-server-usage/servers-detail-resp.json deleted file mode 100644 index a57d5ed01be..00000000000 --- a/doc/api_samples/os-server-usage/servers-detail-resp.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "servers": [ - { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "addresses": { - "private": [ - { - "addr": "192.168.0.3", - "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", - "OS-EXT-IPS:type": "fixed", - "version": 4 - } - ] - }, - "created": "2013-08-15T12:04:04Z", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", - "rel": "bookmark" - } - ] - }, - "hostId": "117535ce0eda7ee02ebffe2c976173629385481ae3f2bded5e14a66b", - "id": "ae114799-9164-48f5-a036-6ef9310acbc4", - "image": { - "id": "70a599e0-31e7-49b7-b260-868f441e862b", - "links": [ - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", - "rel": "bookmark" - } - ] - }, - "key_name": null, - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/ae114799-9164-48f5-a036-6ef9310acbc4", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/ae114799-9164-48f5-a036-6ef9310acbc4", - "rel": "bookmark" - } - ], - "metadata": { - "My Server Name": "Apache1" - }, - "name": "new-server-test", - "OS-SRV-USG:launched_at": "2013-08-15T12:04:05.368766", - "OS-SRV-USG:terminated_at": null, - "progress": 0, - "status": "ACTIVE", - "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-08-15T12:04:05Z", - "user_id": "fake" - } - ] -} diff --git a/doc/api_samples/os-services/v2.53/service-disable-log-put-req.json b/doc/api_samples/os-services/v2.53/service-disable-log-put-req.json new file mode 100644 index 00000000000..6491f6316c3 --- /dev/null +++ b/doc/api_samples/os-services/v2.53/service-disable-log-put-req.json @@ -0,0 +1,4 @@ +{ + "status": "disabled", + "disabled_reason": "maintenance" +} \ No newline at end of file diff --git a/doc/api_samples/os-services/v2.53/service-disable-log-put-resp.json b/doc/api_samples/os-services/v2.53/service-disable-log-put-resp.json new file mode 100644 index 00000000000..bbf18edaf16 --- /dev/null +++ b/doc/api_samples/os-services/v2.53/service-disable-log-put-resp.json @@ -0,0 +1,13 @@ +{ + "service": { + "id": "e81d66a4-ddd3-4aba-8a84-171d1cb4d339", + "binary": "nova-compute", + "disabled_reason": "maintenance", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:05.000000", + "forced_down": false, + "zone": "nova" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-services/v2.53/service-disable-put-req.json b/doc/api_samples/os-services/v2.53/service-disable-put-req.json new file mode 100644 index 00000000000..c39c93446a5 --- /dev/null +++ b/doc/api_samples/os-services/v2.53/service-disable-put-req.json @@ -0,0 +1,3 @@ +{ + "status": "disabled" +} \ No newline at end of file diff --git a/doc/api_samples/os-services/v2.53/service-disable-put-resp.json b/doc/api_samples/os-services/v2.53/service-disable-put-resp.json new file mode 100644 index 00000000000..5f05d3002e5 --- /dev/null +++ b/doc/api_samples/os-services/v2.53/service-disable-put-resp.json @@ -0,0 +1,13 @@ +{ + "service": { + "id": "e81d66a4-ddd3-4aba-8a84-171d1cb4d339", + "binary": "nova-compute", + "disabled_reason": null, + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:05.000000", + "forced_down": false, + "zone": "nova" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-services/v2.53/service-enable-put-req.json b/doc/api_samples/os-services/v2.53/service-enable-put-req.json new file mode 100644 index 00000000000..274ae2d6b3f --- /dev/null +++ b/doc/api_samples/os-services/v2.53/service-enable-put-req.json @@ -0,0 +1,3 @@ +{ + "status": "enabled" +} \ No newline at end of file diff --git a/doc/api_samples/os-services/v2.53/service-enable-put-resp.json b/doc/api_samples/os-services/v2.53/service-enable-put-resp.json new file mode 100644 index 00000000000..b3018439492 --- /dev/null +++ b/doc/api_samples/os-services/v2.53/service-enable-put-resp.json @@ -0,0 +1,13 @@ +{ + "service": { + "id": "e81d66a4-ddd3-4aba-8a84-171d1cb4d339", + "binary": "nova-compute", + "disabled_reason": null, + "host": "host1", + "state": "up", + "status": "enabled", + "updated_at": "2012-10-29T13:42:05.000000", + "forced_down": false, + "zone": "nova" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-services/v2.53/service-force-down-put-req.json b/doc/api_samples/os-services/v2.53/service-force-down-put-req.json new file mode 100644 index 00000000000..1545475c8ff --- /dev/null +++ b/doc/api_samples/os-services/v2.53/service-force-down-put-req.json @@ -0,0 +1,3 @@ +{ + "forced_down": true +} \ No newline at end of file diff --git a/doc/api_samples/os-services/v2.53/service-force-down-put-resp.json b/doc/api_samples/os-services/v2.53/service-force-down-put-resp.json new file mode 100644 index 00000000000..cb37faeaafd --- /dev/null +++ b/doc/api_samples/os-services/v2.53/service-force-down-put-resp.json @@ -0,0 +1,13 @@ +{ + "service": { + "id": "e81d66a4-ddd3-4aba-8a84-171d1cb4d339", + "binary": "nova-compute", + "disabled_reason": "test2", + "host": "host1", + "state": "down", + "status": "disabled", + "updated_at": "2012-10-29T13:42:05.000000", + "forced_down": true, + "zone": "nova" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-services/v2.53/services-list-get-resp.json b/doc/api_samples/os-services/v2.53/services-list-get-resp.json new file mode 100644 index 00000000000..351908830a8 --- /dev/null +++ b/doc/api_samples/os-services/v2.53/services-list-get-resp.json @@ -0,0 +1,48 @@ +{ + "services": [ + { + "id": "c4726392-27de-4ff9-b2e0-5aa1d08a520f", + "binary": "nova-scheduler", + "disabled_reason": "test1", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:02.000000", + "forced_down": false, + "zone": "internal" + }, + { + "id": "e81d66a4-ddd3-4aba-8a84-171d1cb4d339", + "binary": "nova-compute", + "disabled_reason": "test2", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:05.000000", + "forced_down": false, + "zone": "nova" + }, + { + "id": "bbd684ff-d3f6-492e-a30a-a12a2d2db0e0", + "binary": "nova-scheduler", + "disabled_reason": null, + "host": "host2", + "state": "down", + "status": "enabled", + "updated_at": "2012-09-19T06:55:34.000000", + "forced_down": false, + "zone": "internal" + }, + { + "id": "13aa304e-5340-45a7-a7fb-b6d6e914d272", + "binary": "nova-compute", + "disabled_reason": "test4", + "host": "host2", + "state": "down", + "status": "disabled", + "updated_at": "2012-09-18T08:03:38.000000", + "forced_down": false, + "zone": "nova" + } + ] +} diff --git a/doc/api_samples/os-simple-tenant-usage/simple-tenant-usage-get-detail.json b/doc/api_samples/os-simple-tenant-usage/simple-tenant-usage-get-detail.json new file mode 100644 index 00000000000..25bd3221695 --- /dev/null +++ b/doc/api_samples/os-simple-tenant-usage/simple-tenant-usage-get-detail.json @@ -0,0 +1,29 @@ +{ + "tenant_usages": [ + { + "start": "2012-10-08T20:10:44.587336", + "stop": "2012-10-08T21:10:44.587336", + "tenant_id": "6f70656e737461636b20342065766572", + "total_hours": 1.0, + "total_local_gb_usage": 1.0, + "total_memory_mb_usage": 512.0, + "total_vcpus_usage": 1.0, + "server_usages": [ + { + "ended_at": null, + "flavor": "m1.tiny", + "hours": 1.0, + "instance_id": "1f1deceb-17b5-4c04-84c7-e0d4499c8fe0", + "local_gb": 1, + "memory_mb": 512, + "name": "new-server-test", + "started_at": "2012-10-08T20:10:44.541277", + "state": "active", + "tenant_id": "6f70656e737461636b20342065766572", + "uptime": 3600, + "vcpus": 1 + } + ] + } + ] +} diff --git a/doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get-detail.json b/doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get-detail.json new file mode 100644 index 00000000000..50d64023c63 --- /dev/null +++ b/doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get-detail.json @@ -0,0 +1,35 @@ +{ + "tenant_usages": [ + { + "start": "2012-10-08T20:10:44.587336", + "stop": "2012-10-08T21:10:44.587336", + "tenant_id": "6f70656e737461636b20342065766572", + "total_hours": 1.0, + "total_local_gb_usage": 1.0, + "total_memory_mb_usage": 512.0, + "total_vcpus_usage": 1.0, + "server_usages": [ + { + "ended_at": null, + "flavor": "m1.tiny", + "hours": 1.0, + "instance_id": "1f1deceb-17b5-4c04-84c7-e0d4499c8fe0", + "local_gb": 1, + "memory_mb": 512, + "name": "instance-2", + "started_at": "2012-10-08T20:10:44.541277", + "state": "active", + "tenant_id": "6f70656e737461636b20342065766572", + "uptime": 3600, + "vcpus": 1 + } + ] + } + ], + "tenant_usages_links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/os-simple-tenant-usage?detailed=1&end=2016-10-12+18%3A22%3A04.868106&limit=1&marker=1f1deceb-17b5-4c04-84c7-e0d4499c8fe0&start=2016-10-12+18%3A22%3A04.868106", + "rel": "next" + } + ] +} diff --git a/doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get-specific.json b/doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get-specific.json new file mode 100644 index 00000000000..d065bfedf09 --- /dev/null +++ b/doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get-specific.json @@ -0,0 +1,33 @@ +{ + "tenant_usage": { + "server_usages": [ + { + "ended_at": null, + "flavor": "m1.tiny", + "hours": 1.0, + "instance_id": "1f1deceb-17b5-4c04-84c7-e0d4499c8fe0", + "local_gb": 1, + "memory_mb": 512, + "name": "instance-2", + "started_at": "2012-10-08T20:10:44.541277", + "state": "active", + "tenant_id": "6f70656e737461636b20342065766572", + "uptime": 3600, + "vcpus": 1 + } + ], + "start": "2012-10-08T20:10:44.587336", + "stop": "2012-10-08T21:10:44.587336", + "tenant_id": "6f70656e737461636b20342065766572", + "total_hours": 1.0, + "total_local_gb_usage": 1.0, + "total_memory_mb_usage": 512.0, + "total_vcpus_usage": 1.0 + }, + "tenant_usage_links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/os-simple-tenant-usage/6f70656e737461636b20342065766572?end=2016-10-12+18%3A22%3A04.868106&limit=1&marker=1f1deceb-17b5-4c04-84c7-e0d4499c8fe0&start=2016-10-12+18%3A22%3A04.868106", + "rel": "next" + } + ] +} diff --git a/doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get.json b/doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get.json new file mode 100644 index 00000000000..dd8afc346f6 --- /dev/null +++ b/doc/api_samples/os-simple-tenant-usage/v2.40/simple-tenant-usage-get.json @@ -0,0 +1,19 @@ +{ + "tenant_usages": [ + { + "start": "2012-10-08T21:10:44.587336", + "stop": "2012-10-08T22:10:44.587336", + "tenant_id": "6f70656e737461636b20342065766572", + "total_hours": 1.0, + "total_local_gb_usage": 1.0, + "total_memory_mb_usage": 512.0, + "total_vcpus_usage": 1.0 + } + ], + "tenant_usages_links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/os-simple-tenant-usage?end=2016-10-12+18%3A22%3A04.868106&limit=1&marker=1f1deceb-17b5-4c04-84c7-e0d4499c8fe0&start=2016-10-12+18%3A22%3A04.868106", + "rel": "next" + } + ] +} diff --git a/doc/api_samples/os-used-limits/usedlimits-get-resp.json b/doc/api_samples/os-used-limits/usedlimits-get-resp.json deleted file mode 100644 index 28309af04c6..00000000000 --- a/doc/api_samples/os-used-limits/usedlimits-get-resp.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "limits": { - "absolute": { - "maxImageMeta": 128, - "maxPersonality": 5, - "maxPersonalitySize": 10240, - "maxSecurityGroupRules": 20, - "maxSecurityGroups": 10, - "maxServerMeta": 128, - "maxTotalCores": 20, - "maxTotalFloatingIps": 10, - "maxTotalInstances": 10, - "maxTotalKeypairs": 100, - "maxTotalRAMSize": 51200, - "maxServerGroups": 10, - "maxServerGroupMembers": 10, - "totalCoresUsed": 0, - "totalInstancesUsed": 0, - "totalRAMUsed": 0, - "totalSecurityGroupsUsed": 0, - "totalFloatingIpsUsed": 0, - "totalServerGroupsUsed": 0 - }, - "rate": [] - } -} diff --git a/doc/api_samples/os-used-limits/v2-usedlimits-get-resp.json b/doc/api_samples/os-used-limits/v2-usedlimits-get-resp.json deleted file mode 100644 index 5b3ee33e8a8..00000000000 --- a/doc/api_samples/os-used-limits/v2-usedlimits-get-resp.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "limits": { - "absolute": { - "maxImageMeta": 128, - "maxPersonality": 5, - "maxPersonalitySize": 10240, - "maxSecurityGroupRules": 20, - "maxSecurityGroups": 10, - "maxServerMeta": 128, - "maxTotalCores": 20, - "maxTotalFloatingIps": 10, - "maxTotalInstances": 10, - "maxTotalKeypairs": 100, - "maxTotalRAMSize": 51200, - "maxServerGroups": 10, - "maxServerGroupMembers": 10, - "totalCoresUsed": 0, - "totalInstancesUsed": 0, - "totalRAMUsed": 0, - "totalSecurityGroupsUsed": 0, - "totalFloatingIpsUsed": 0, - "totalServerGroupsUsed": 0 - }, - "rate": [ - { - "limit": [ - { - "next-available": "2012-11-27T17:24:52Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "POST" - }, - { - "next-available": "2012-11-27T17:24:52Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "PUT" - }, - { - "next-available": "2012-11-27T17:24:52Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "DELETE" - } - ], - "regex": ".*", - "uri": "*" - }, - { - "limit": [ - { - "next-available": "2012-11-27T17:24:52Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "POST" - } - ], - "regex": "^/servers", - "uri": "*/servers" - }, - { - "limit": [ - { - "next-available": "2012-11-27T17:24:52Z", - "remaining": 120, - "unit": "MINUTE", - "value": 120, - "verb": "GET" - } - ], - "regex": ".*changes-since.*", - "uri": "*changes-since*" - }, - { - "limit": [ - { - "next-available": "2012-11-27T17:24:52Z", - "remaining": 12, - "unit": "MINUTE", - "value": 12, - "verb": "GET" - } - ], - "regex": "^/os-fping", - "uri": "*/os-fping" - } - ] - } -} diff --git a/doc/api_samples/os-user-data/userdata-post-req.json b/doc/api_samples/os-user-data/userdata-post-req.json deleted file mode 100644 index e8f78ff4b39..00000000000 --- a/doc/api_samples/os-user-data/userdata-post-req.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "server" : { - "name" : "new-server-test", - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef" : "1", - "metadata" : { - "My Server Name" : "Apache1" - }, - "user_data" : "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==" - } -} diff --git a/doc/api_samples/os-user-data/userdata-post-resp.json b/doc/api_samples/os-user-data/userdata-post-resp.json deleted file mode 100644 index 1e14966de1c..00000000000 --- a/doc/api_samples/os-user-data/userdata-post-resp.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "server": { - "adminPass": "2xHoDU7Gd7vw", - "id": "976a62bb-0d4a-4e17-9044-1864e888a557", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/976a62bb-0d4a-4e17-9044-1864e888a557", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/976a62bb-0d4a-4e17-9044-1864e888a557", - "rel": "bookmark" - } - ] - } -} diff --git a/doc/api_samples/os-volumes/v2.49/attach-volume-to-server-req.json b/doc/api_samples/os-volumes/v2.49/attach-volume-to-server-req.json new file mode 100644 index 00000000000..9f49b54d78c --- /dev/null +++ b/doc/api_samples/os-volumes/v2.49/attach-volume-to-server-req.json @@ -0,0 +1,6 @@ +{ + "volumeAttachment": { + "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f803", + "tag": "foo" + } +} diff --git a/doc/api_samples/os-volumes/v2.49/attach-volume-to-server-resp.json b/doc/api_samples/os-volumes/v2.49/attach-volume-to-server-resp.json new file mode 100644 index 00000000000..5f610bcaebe --- /dev/null +++ b/doc/api_samples/os-volumes/v2.49/attach-volume-to-server-resp.json @@ -0,0 +1,8 @@ +{ + "volumeAttachment": { + "device": "/dev/sdb", + "id": "a26887c6-c47b-4654-abb5-dfadf7d3f803", + "serverId": "84ffbfa0-daf4-4e23-bf4b-dc532c459d4e", + "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f803" + } +} diff --git a/doc/api_samples/server-migrations/v2.23/migrations-get.json b/doc/api_samples/server-migrations/v2.23/migrations-get.json index 8be7a017ed5..6ab0e5a9d43 100644 --- a/doc/api_samples/server-migrations/v2.23/migrations-get.json +++ b/doc/api_samples/server-migrations/v2.23/migrations-get.json @@ -11,10 +11,10 @@ "status": "running", "memory_total_bytes": 123456, "memory_processed_bytes": 12345, - "memory_remaining_bytes": 120000, + "memory_remaining_bytes": 111111, "disk_total_bytes": 234567, "disk_processed_bytes": 23456, - "disk_remaining_bytes": 230000, + "disk_remaining_bytes": 211111, "updated_at": "2016-01-29T13:42:02.000000" } } diff --git a/doc/api_samples/server-migrations/v2.23/migrations-index.json b/doc/api_samples/server-migrations/v2.23/migrations-index.json index b4279e35427..1c97697d3af 100644 --- a/doc/api_samples/server-migrations/v2.23/migrations-index.json +++ b/doc/api_samples/server-migrations/v2.23/migrations-index.json @@ -12,10 +12,10 @@ "status": "running", "memory_total_bytes": 123456, "memory_processed_bytes": 12345, - "memory_remaining_bytes": 120000, + "memory_remaining_bytes": 111111, "disk_total_bytes": 234567, "disk_processed_bytes": 23456, - "disk_remaining_bytes": 230000, + "disk_remaining_bytes": 211111, "updated_at": "2016-01-29T13:42:02.000000" } ] diff --git a/doc/api_samples/servers/server-action-addfloatingip-req.json b/doc/api_samples/servers/server-action-addfloatingip-req.json new file mode 100644 index 00000000000..e4ad5638ab2 --- /dev/null +++ b/doc/api_samples/servers/server-action-addfloatingip-req.json @@ -0,0 +1,6 @@ +{ + "addFloatingIp" : { + "address": "10.10.10.10", + "fixed_address": "192.168.0.3" + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/server-action-rebuild-preserve-ephemeral.json b/doc/api_samples/servers/server-action-rebuild-preserve-ephemeral.json deleted file mode 100644 index 1df6ec77683..00000000000 --- a/doc/api_samples/servers/server-action-rebuild-preserve-ephemeral.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "rebuild" : { - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "name" : "foobar", - "adminPass" : "seekr3t", - "metadata" : { - "meta_var" : "meta_val" - }, - "personality" : [ - { - "path" : "/etc/banner.txt", - "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" - } - ], - "preserve_ephemeral": true - } -} diff --git a/doc/api_samples/servers/server-action-rebuild-resp.json b/doc/api_samples/servers/server-action-rebuild-resp.json index b4ce48c9009..b66dc4ce80d 100644 --- a/doc/api_samples/servers/server-action-rebuild-resp.json +++ b/doc/api_samples/servers/server-action-rebuild-resp.json @@ -2,6 +2,7 @@ "server": { "accessIPv4": "1.2.3.4", "accessIPv6": "80fe::", + "OS-DCF:diskConfig": "AUTO", "addresses": { "private": [ { diff --git a/doc/api_samples/servers/server-action-rebuild.json b/doc/api_samples/servers/server-action-rebuild.json index ef089054ea6..787a8da89aa 100644 --- a/doc/api_samples/servers/server-action-rebuild.json +++ b/doc/api_samples/servers/server-action-rebuild.json @@ -2,11 +2,18 @@ "rebuild" : { "accessIPv4" : "1.2.3.4", "accessIPv6" : "80fe::", + "OS-DCF:diskConfig": "AUTO", "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", "name" : "foobar", "adminPass" : "seekr3t", "metadata" : { "meta_var" : "meta_val" - } + }, + "personality": [ + { + "path": "/etc/banner.txt", + "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ] } } diff --git a/doc/api_samples/servers/server-action-removefloatingip-req.json b/doc/api_samples/servers/server-action-removefloatingip-req.json new file mode 100644 index 00000000000..b1f9698feee --- /dev/null +++ b/doc/api_samples/servers/server-action-removefloatingip-req.json @@ -0,0 +1,5 @@ +{ + "removeFloatingIp": { + "address": "172.16.10.7" + } +} diff --git a/doc/api_samples/servers/server-action-resize.json b/doc/api_samples/servers/server-action-resize.json index bdaa37a1763..ca41ee74f31 100644 --- a/doc/api_samples/servers/server-action-resize.json +++ b/doc/api_samples/servers/server-action-resize.json @@ -1,5 +1,6 @@ { "resize" : { - "flavorRef" : "2" + "flavorRef" : "2", + "OS-DCF:diskConfig": "AUTO" } } \ No newline at end of file diff --git a/doc/api_samples/servers/server-create-req-v237.json b/doc/api_samples/servers/server-create-req-v237.json new file mode 100644 index 00000000000..abffb363e43 --- /dev/null +++ b/doc/api_samples/servers/server-create-req-v237.json @@ -0,0 +1,30 @@ +{ + "server" : { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "name" : "new-server-test", + "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", + "flavorRef" : "1", + "availability_zone": "nova", + "OS-DCF:diskConfig": "AUTO", + "metadata" : { + "My Server Name" : "Apache1" + }, + "personality": [ + { + "path": "/etc/banner.txt", + "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ], + "security_groups": [ + { + "name": "default" + } + ], + "user_data" : "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "networks": "auto" + }, + "OS-SCH-HNT:scheduler_hints": { + "same_host": "48e6a9f6-30af-47e0-bc04-acaed113bb4e" + } +} diff --git a/doc/api_samples/servers/server-create-req.json b/doc/api_samples/servers/server-create-req.json index ca53f3a7c26..4ac0157a85e 100644 --- a/doc/api_samples/servers/server-create-req.json +++ b/doc/api_samples/servers/server-create-req.json @@ -5,8 +5,25 @@ "name" : "new-server-test", "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", "flavorRef" : "1", + "availability_zone": "nova", + "OS-DCF:diskConfig": "AUTO", "metadata" : { "My Server Name" : "Apache1" - } + }, + "personality": [ + { + "path": "/etc/banner.txt", + "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ], + "security_groups": [ + { + "name": "default" + } + ], + "user_data" : "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==" + }, + "OS-SCH-HNT:scheduler_hints": { + "same_host": "48e6a9f6-30af-47e0-bc04-acaed113bb4e" } } diff --git a/doc/api_samples/servers/server-get-resp.json b/doc/api_samples/servers/server-get-resp.json index af6297d972d..6c1e246f7cb 100644 --- a/doc/api_samples/servers/server-get-resp.json +++ b/doc/api_samples/servers/server-get-resp.json @@ -48,7 +48,31 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "b8b357f7100d4391828f2177c922ef93", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1" + }, + { + "id": "volume_id2" + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", + "OS-SRV-USG:terminated_at": null, "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], "status": "ACTIVE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2013-09-03T04:01:33Z", diff --git a/doc/api_samples/servers/server-post-req.json b/doc/api_samples/servers/server-post-req.json deleted file mode 100644 index ca53f3a7c26..00000000000 --- a/doc/api_samples/servers/server-post-req.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "server" : { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "name" : "new-server-test", - "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef" : "1", - "metadata" : { - "My Server Name" : "Apache1" - } - } -} diff --git a/doc/api_samples/servers/server-post-resp.json b/doc/api_samples/servers/server-post-resp.json deleted file mode 100644 index 9c3ad3d8c83..00000000000 --- a/doc/api_samples/servers/server-post-resp.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "server": { - "adminPass": "6NpUwoz2QDRN", - "id": "f5dc173b-6804-445a-a6d8-c705dad5b5eb", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb", - "rel": "bookmark" - } - ] - } -} diff --git a/doc/api_samples/servers/servers-details-resp.json b/doc/api_samples/servers/servers-details-resp.json index bb374b9417a..07c6fd3aaa7 100644 --- a/doc/api_samples/servers/servers-details-resp.json +++ b/doc/api_samples/servers/servers-details-resp.json @@ -49,7 +49,31 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "c3f14e9812ad496baf92ccfb3c61e15f", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1" + }, + { + "id": "volume_id2" + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:53:12.774549", + "OS-SRV-USG:terminated_at": null, "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], "status": "ACTIVE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2013-09-03T04:01:32Z", diff --git a/doc/api_samples/servers/v2.16/server-get-resp.json b/doc/api_samples/servers/v2.16/server-get-resp.json index ee500305c23..8737a0e80d3 100644 --- a/doc/api_samples/servers/v2.16/server-get-resp.json +++ b/doc/api_samples/servers/v2.16/server-get-resp.json @@ -1,7 +1,5 @@ { "server": { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", "addresses": { "private": [ { @@ -12,7 +10,7 @@ } ] }, - "created": "2013-09-03T04:01:32Z", + "created": "2013-09-16T02:55:07Z", "flavor": { "id": "1", "links": [ @@ -22,8 +20,8 @@ } ] }, - "hostId": "92154fab69d5883ba2c8622b7e65f745dd33257221c07af363c51b29", - "id": "0e44cc9c-e052-415d-afbf-469b0d384170", + "hostId": "3bf189131c61d0e71b0a8686a897a0f50d1693b48c47b721fe77155b", + "id": "c278163e-36f9-4cf2-b1ac-80db4c63f7a8", "image": { "id": "70a599e0-31e7-49b7-b260-868f441e862b", "links": [ @@ -36,11 +34,11 @@ "key_name": null, "links": [ { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/0e44cc9c-e052-415d-afbf-469b0d384170", + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8", "rel": "self" }, { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/0e44cc9c-e052-415d-afbf-469b0d384170", + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8", "rel": "bookmark" } ], @@ -48,11 +46,47 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "c5f474bf81474f9dbbc404d5b2e4e9b3", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:hostname": "new-server-test", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:reservation_id": "r-12345678", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:kernel_id": null, + "OS-EXT-SRV-ATTR:ramdisk_id": null, + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1", + "delete_on_termination": false + }, + { + "id": "volume_id2", + "delete_on_termination": false + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", + "OS-SRV-USG:terminated_at": null, + "security_groups": [ + { + "name": "default" + } + ], + "locked": false, + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", "progress": 0, "status": "ACTIVE", "host_status": "UP", "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-03T04:01:33Z", + "updated": "2013-09-16T02:55:08Z", "user_id": "fake" } } diff --git a/doc/api_samples/servers/v2.16/servers-details-resp.json b/doc/api_samples/servers/v2.16/servers-details-resp.json index 2b24f82b463..125f8caaaa8 100644 --- a/doc/api_samples/servers/v2.16/servers-details-resp.json +++ b/doc/api_samples/servers/v2.16/servers-details-resp.json @@ -1,8 +1,6 @@ { "servers": [ { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", "addresses": { "private": [ { @@ -13,7 +11,7 @@ } ] }, - "created": "2013-09-03T04:01:32Z", + "created": "2013-09-16T02:55:03Z", "flavor": { "id": "1", "links": [ @@ -23,8 +21,8 @@ } ] }, - "hostId": "bcf92836fc9ed4203a75cb0337afc7f917d2be504164b995c2334b25", - "id": "f5dc173b-6804-445a-a6d8-c705dad5b5eb", + "hostId": "63cf07a9fd82e1d2294926ec5c0d2e1e0ca449224246df75e16f23dc", + "id": "a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", "image": { "id": "70a599e0-31e7-49b7-b260-868f441e862b", "links": [ @@ -37,11 +35,11 @@ "key_name": null, "links": [ { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb", + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", "rel": "self" }, { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb", + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46", "rel": "bookmark" } ], @@ -49,11 +47,47 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "bc8efe4fdb7148a4bb921a2b03d17de6", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:hostname": "new-server-test", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:reservation_id": "r-12345678", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:kernel_id": null, + "OS-EXT-SRV-ATTR:ramdisk_id": null, + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "locked": false, + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1", + "delete_on_termination": false + }, + { + "id": "volume_id2", + "delete_on_termination": false + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:53:12.774549", + "OS-SRV-USG:terminated_at": null, "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], "status": "ACTIVE", "host_status": "UP", "tenant_id": "6f70656e737461636b20342065766572", - "updated": "2013-09-03T04:01:32Z", + "updated": "2013-09-16T02:55:05Z", "user_id": "fake" } ] diff --git a/doc/api_samples/servers/v2.16/servers-list-resp.json b/doc/api_samples/servers/v2.16/servers-list-resp.json new file mode 100644 index 00000000000..f4b2ce3802d --- /dev/null +++ b/doc/api_samples/servers/v2.16/servers-list-resp.json @@ -0,0 +1,18 @@ +{ + "servers": [ + { + "id": "22c91117-08de-4894-9aa9-6ef382400985", + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/22c91117-08de-4894-9aa9-6ef382400985", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/22c91117-08de-4894-9aa9-6ef382400985", + "rel": "bookmark" + } + ], + "name": "new-server-test" + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.19/server-action-rebuild-resp.json b/doc/api_samples/servers/v2.19/server-action-rebuild-resp.json index e17d95274ce..b38d40709d1 100644 --- a/doc/api_samples/servers/v2.19/server-action-rebuild-resp.json +++ b/doc/api_samples/servers/v2.19/server-action-rebuild-resp.json @@ -50,6 +50,7 @@ "description" : "description of foobar", "progress": 0, "status": "ACTIVE", + "OS-DCF:diskConfig": "AUTO", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2013-11-14T06:29:02Z", "user_id": "fake" diff --git a/doc/api_samples/servers/v2.19/server-create-req.json b/doc/api_samples/servers/v2.19/server-create-req.json new file mode 100644 index 00000000000..2a14738ed83 --- /dev/null +++ b/doc/api_samples/servers/v2.19/server-create-req.json @@ -0,0 +1,13 @@ +{ + "server" : { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "name" : "new-server-test", + "description" : "new-server-description", + "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", + "flavorRef" : "http://openstack.example.com/flavors/1", + "metadata" : { + "My Server Name" : "Apache1" + } + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.19/server-create-resp.json b/doc/api_samples/servers/v2.19/server-create-resp.json new file mode 100644 index 00000000000..ff1c136f252 --- /dev/null +++ b/doc/api_samples/servers/v2.19/server-create-resp.json @@ -0,0 +1,22 @@ +{ + "server": { + "adminPass": "rySfUy7xL4C5", + "OS-DCF:diskConfig": "AUTO", + "id": "19923676-e78b-46fb-af62-a5942aece2ac", + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/19923676-e78b-46fb-af62-a5942aece2ac", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/19923676-e78b-46fb-af62-a5942aece2ac", + "rel": "bookmark" + } + ], + "security_groups": [ + { + "name": "default" + } + ] + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.19/server-get-resp.json b/doc/api_samples/servers/v2.19/server-get-resp.json index 7ff8aaeba07..3fd3bc93610 100644 --- a/doc/api_samples/servers/v2.19/server-get-resp.json +++ b/doc/api_samples/servers/v2.19/server-get-resp.json @@ -50,7 +50,41 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "b8b357f7100d4391828f2177c922ef93", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:hostname": "fake-hostname", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1", + "delete_on_termination": false + }, + { + "id": "volume_id2", + "delete_on_termination": false + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", + "OS-SRV-USG:terminated_at": null, "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "host_status": "UP", "status": "ACTIVE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2015-12-07T17:24:15Z", diff --git a/doc/api_samples/servers/v2.19/server-post-req.json b/doc/api_samples/servers/v2.19/server-post-req.json deleted file mode 100644 index 24cdb9c2e5c..00000000000 --- a/doc/api_samples/servers/v2.19/server-post-req.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "server" : { - "accessIPv4": "1.2.3.4", - "accessIPv6": "80fe::", - "name" : "new-server-test", - "description" : "new-server-description", - "imageRef" : "http://glance.openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b", - "flavorRef" : "http://openstack.example.com/flavors/1", - "metadata" : { - "My Server Name" : "Apache1" - } - } -} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.19/server-post-resp.json b/doc/api_samples/servers/v2.19/server-post-resp.json deleted file mode 100644 index c5b9bc685e1..00000000000 --- a/doc/api_samples/servers/v2.19/server-post-resp.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "server": { - "adminPass": "rySfUy7xL4C5", - "id": "19923676-e78b-46fb-af62-a5942aece2ac", - "links": [ - { - "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/19923676-e78b-46fb-af62-a5942aece2ac", - "rel": "self" - }, - { - "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/19923676-e78b-46fb-af62-a5942aece2ac", - "rel": "bookmark" - } - ] - } -} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.19/server-put-resp.json b/doc/api_samples/servers/v2.19/server-put-resp.json index 91c2f3cd086..29c8f6ac9b3 100644 --- a/doc/api_samples/servers/v2.19/server-put-resp.json +++ b/doc/api_samples/servers/v2.19/server-put-resp.json @@ -2,6 +2,7 @@ "server": { "accessIPv4": "1.2.3.4", "accessIPv6": "80fe::", + "OS-DCF:diskConfig": "AUTO", "addresses": { "private": [ { diff --git a/doc/api_samples/servers/v2.19/servers-details-resp.json b/doc/api_samples/servers/v2.19/servers-details-resp.json index 18dc0d6530d..db3905f7b07 100644 --- a/doc/api_samples/servers/v2.19/servers-details-resp.json +++ b/doc/api_samples/servers/v2.19/servers-details-resp.json @@ -51,7 +51,41 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "c3f14e9812ad496baf92ccfb3c61e15f", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:hostname": "fake-hostname", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1", + "delete_on_termination": false + }, + { + "id": "volume_id2", + "delete_on_termination": false + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:53:12.774549", + "OS-SRV-USG:terminated_at": null, "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "host_status": "UP", "status": "ACTIVE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2015-12-07T19:54:49Z", diff --git a/doc/api_samples/servers/v2.26/server-action-rebuild-resp.json b/doc/api_samples/servers/v2.26/server-action-rebuild-resp.json new file mode 100644 index 00000000000..86a7b41c232 --- /dev/null +++ b/doc/api_samples/servers/v2.26/server-action-rebuild-resp.json @@ -0,0 +1,59 @@ +{ + "server": { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "addr": "192.168.0.3", + "version": 4 + } + ] + }, + "adminPass": "seekr3t", + "created": "2013-11-14T06:29:00Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostId": "28d8d56f0e3a77e20891f455721cbb68032e017045e20aa5dfc6cb66", + "id": "a0a80a94-3d81-4a10-822a-daa0cf9e870b", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/a0a80a94-3d81-4a10-822a-daa0cf9e870b", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/a0a80a94-3d81-4a10-822a-daa0cf9e870b", + "rel": "bookmark" + } + ], + "metadata": { + "meta_var": "meta_val" + }, + "name": "foobar", + "OS-DCF:diskConfig": "AUTO", + "progress": 0, + "status": "ACTIVE", + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2013-11-14T06:29:02Z", + "user_id": "fake", + "locked": false, + "description" : "description of foobar", + "tags": ["tag1", "tag2"] + } +} diff --git a/doc/api_samples/servers/v2.26/server-action-rebuild.json b/doc/api_samples/servers/v2.26/server-action-rebuild.json new file mode 100644 index 00000000000..03cccb4835f --- /dev/null +++ b/doc/api_samples/servers/v2.26/server-action-rebuild.json @@ -0,0 +1,21 @@ +{ + "rebuild" : { + "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", + "accessIPv4" : "1.2.3.4", + "accessIPv6" : "80fe::", + "adminPass" : "seekr3t", + "metadata" : { + "meta_var" : "meta_val" + }, + "name" : "foobar", + "OS-DCF:diskConfig": "AUTO", + "personality" : [ + { + "path" : "/etc/banner.txt", + "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ], + "preserve_ephemeral": false, + "description" : "description of foobar" + } +} diff --git a/doc/api_samples/servers/v2.3/server-get-resp.json b/doc/api_samples/servers/v2.3/server-get-resp.json new file mode 100644 index 00000000000..22882a074a3 --- /dev/null +++ b/doc/api_samples/servers/v2.3/server-get-resp.json @@ -0,0 +1,90 @@ +{ + "server": { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "addr": "192.168.0.3", + "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", + "OS-EXT-IPS:type": "fixed", + "version": 4 + } + ] + }, + "created": "2013-09-03T04:01:32Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostId": "92154fab69d5883ba2c8622b7e65f745dd33257221c07af363c51b29", + "id": "0e44cc9c-e052-415d-afbf-469b0d384170", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "key_name": null, + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/0e44cc9c-e052-415d-afbf-469b0d384170", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/0e44cc9c-e052-415d-afbf-469b0d384170", + "rel": "bookmark" + } + ], + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "b8b357f7100d4391828f2177c922ef93", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:hostname": "fake-hostname", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1", + "delete_on_termination": false + }, + { + "id": "volume_id2", + "delete_on_termination": false + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", + "OS-SRV-USG:terminated_at": null, + "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "status": "ACTIVE", + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2013-09-03T04:01:33Z", + "user_id": "fake" + } +} diff --git a/doc/api_samples/servers/v2.3/servers-details-resp.json b/doc/api_samples/servers/v2.3/servers-details-resp.json new file mode 100644 index 00000000000..ff88dd14ca0 --- /dev/null +++ b/doc/api_samples/servers/v2.3/servers-details-resp.json @@ -0,0 +1,92 @@ +{ + "servers": [ + { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "addr": "192.168.0.3", + "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", + "OS-EXT-IPS:type": "fixed", + "version": 4 + } + ] + }, + "created": "2013-09-03T04:01:32Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostId": "bcf92836fc9ed4203a75cb0337afc7f917d2be504164b995c2334b25", + "id": "f5dc173b-6804-445a-a6d8-c705dad5b5eb", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "key_name": null, + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb", + "rel": "bookmark" + } + ], + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "c3f14e9812ad496baf92ccfb3c61e15f", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:hostname": "fake-hostname", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1", + "delete_on_termination": false + }, + { + "id": "volume_id2", + "delete_on_termination": false + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:53:12.774549", + "OS-SRV-USG:terminated_at": null, + "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "status": "ACTIVE", + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2013-09-03T04:01:32Z", + "user_id": "fake" + } + ] +} diff --git a/doc/api_samples/servers/v2.3/servers-list-resp.json b/doc/api_samples/servers/v2.3/servers-list-resp.json new file mode 100644 index 00000000000..f4b2ce3802d --- /dev/null +++ b/doc/api_samples/servers/v2.3/servers-list-resp.json @@ -0,0 +1,18 @@ +{ + "servers": [ + { + "id": "22c91117-08de-4894-9aa9-6ef382400985", + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/22c91117-08de-4894-9aa9-6ef382400985", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/22c91117-08de-4894-9aa9-6ef382400985", + "rel": "bookmark" + } + ], + "name": "new-server-test" + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.32/server-create-req.json b/doc/api_samples/servers/v2.32/server-create-req.json new file mode 100644 index 00000000000..e4f79a43932 --- /dev/null +++ b/doc/api_samples/servers/v2.32/server-create-req.json @@ -0,0 +1,18 @@ +{ + "server" : { + "name" : "device-tagging-server", + "flavorRef" : "http://openstack.example.com/flavors/1", + "networks" : [{ + "uuid" : "ff608d40-75e9-48cb-b745-77bb55b5eaf2", + "tag": "nic1" + }], + "block_device_mapping_v2": [{ + "uuid": "70a599e0-31e7-49b7-b260-868f441e862b", + "source_type": "image", + "destination_type": "volume", + "boot_index": 0, + "volume_size": "1", + "tag": "disk1" + }] + } +} diff --git a/doc/api_samples/servers/v2.32/server-create-resp.json b/doc/api_samples/servers/v2.32/server-create-resp.json new file mode 100644 index 00000000000..25877f96a80 --- /dev/null +++ b/doc/api_samples/servers/v2.32/server-create-resp.json @@ -0,0 +1,22 @@ +{ + "server": { + "adminPass": "rojsEujtu7GB", + "OS-DCF:diskConfig": "AUTO", + "id": "05ec6bde-40bf-47e8-ac07-89c12b2eee03", + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/05ec6bde-40bf-47e8-ac07-89c12b2eee03", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/05ec6bde-40bf-47e8-ac07-89c12b2eee03", + "rel": "bookmark" + } + ], + "security_groups": [ + { + "name": "default" + } + ] + } +} diff --git a/doc/api_samples/servers/v2.37/server-create-req.json b/doc/api_samples/servers/v2.37/server-create-req.json new file mode 100644 index 00000000000..672a33852f8 --- /dev/null +++ b/doc/api_samples/servers/v2.37/server-create-req.json @@ -0,0 +1,8 @@ +{ + "server": { + "name": "auto-allocate-network", + "imageRef": "70a599e0-31e7-49b7-b260-868f441e862b", + "flavorRef": "http://openstack.example.com/flavors/1", + "networks": "auto" + } +} diff --git a/doc/api_samples/servers/v2.37/server-create-resp.json b/doc/api_samples/servers/v2.37/server-create-resp.json new file mode 100644 index 00000000000..ff1c136f252 --- /dev/null +++ b/doc/api_samples/servers/v2.37/server-create-resp.json @@ -0,0 +1,22 @@ +{ + "server": { + "adminPass": "rySfUy7xL4C5", + "OS-DCF:diskConfig": "AUTO", + "id": "19923676-e78b-46fb-af62-a5942aece2ac", + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/19923676-e78b-46fb-af62-a5942aece2ac", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/19923676-e78b-46fb-af62-a5942aece2ac", + "rel": "bookmark" + } + ], + "security_groups": [ + { + "name": "default" + } + ] + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.42/server-create-req.json b/doc/api_samples/servers/v2.42/server-create-req.json new file mode 100644 index 00000000000..4b000b235c0 --- /dev/null +++ b/doc/api_samples/servers/v2.42/server-create-req.json @@ -0,0 +1,18 @@ +{ + "server" : { + "name" : "device-tagging-server", + "flavorRef" : "http://openstack.example.com/flavors/1", + "networks" : [{ + "uuid" : "ff608d40-75e9-48cb-b745-77bb55b5eaf2", + "tag": "nic1" + }], + "block_device_mapping_v2": [{ + "uuid": "70a599e0-31e7-49b7-b260-868f441e862b", + "source_type": "image", + "destination_type": "volume", + "boot_index": 0, + "volume_size": "1", + "tag": "disk1" + }] + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.42/server-create-resp.json b/doc/api_samples/servers/v2.42/server-create-resp.json new file mode 100644 index 00000000000..dd0bb9f2284 --- /dev/null +++ b/doc/api_samples/servers/v2.42/server-create-resp.json @@ -0,0 +1,22 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "adminPass": "S5wqy9sPYUvU", + "id": "97108291-2fd7-4dc2-a909-eaae0306a6a9", + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9", + "rel": "bookmark" + } + ], + "security_groups": [ + { + "name": "default" + } + ] + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.45/server-action-create-image-resp.json b/doc/api_samples/servers/v2.45/server-action-create-image-resp.json new file mode 100644 index 00000000000..05594917ddc --- /dev/null +++ b/doc/api_samples/servers/v2.45/server-action-create-image-resp.json @@ -0,0 +1,3 @@ +{ + "image_id": "0e7761dd-ee98-41f0-ba35-05994e446431" +} \ No newline at end of file diff --git a/doc/api_samples/all_extensions/server-action-create-image.json b/doc/api_samples/servers/v2.45/server-action-create-image.json similarity index 100% rename from doc/api_samples/all_extensions/server-action-create-image.json rename to doc/api_samples/servers/v2.45/server-action-create-image.json diff --git a/doc/api_samples/servers/v2.47/server-action-rebuild-resp.json b/doc/api_samples/servers/v2.47/server-action-rebuild-resp.json new file mode 100644 index 00000000000..7069951d97f --- /dev/null +++ b/doc/api_samples/servers/v2.47/server-action-rebuild-resp.json @@ -0,0 +1,59 @@ +{ + "server": { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "addr": "192.168.0.3", + "version": 4 + } + ] + }, + "adminPass": "seekr3t", + "created": "2013-11-14T06:29:00Z", + "flavor": { + "disk": 1, + "ephemeral": 0, + "extra_specs": {}, + "original_name": "m1.tiny", + "ram": 512, + "swap": 0, + "vcpus": 1 + }, + "hostId": "28d8d56f0e3a77e20891f455721cbb68032e017045e20aa5dfc6cb66", + "id": "a0a80a94-3d81-4a10-822a-daa0cf9e870b", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/a0a80a94-3d81-4a10-822a-daa0cf9e870b", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/a0a80a94-3d81-4a10-822a-daa0cf9e870b", + "rel": "bookmark" + } + ], + "locked": false, + "metadata": { + "meta_var": "meta_val" + }, + "name": "foobar", + "description" : null, + "progress": 0, + "status": "ACTIVE", + "OS-DCF:diskConfig": "AUTO", + "tags": [], + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2013-11-14T06:29:02Z", + "user_id": "fake" + } +} diff --git a/doc/api_samples/servers/v2.47/server-action-rebuild.json b/doc/api_samples/servers/v2.47/server-action-rebuild.json new file mode 100644 index 00000000000..c65c0457afb --- /dev/null +++ b/doc/api_samples/servers/v2.47/server-action-rebuild.json @@ -0,0 +1,19 @@ +{ + "rebuild" : { + "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", + "accessIPv4" : "1.2.3.4", + "accessIPv6" : "80fe::", + "adminPass" : "seekr3t", + "metadata" : { + "meta_var" : "meta_val" + }, + "name" : "foobar", + "OS-DCF:diskConfig": "AUTO", + "personality" : [ + { + "path" : "/etc/banner.txt", + "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ] + } +} diff --git a/doc/api_samples/servers/v2.47/server-create-req.json b/doc/api_samples/servers/v2.47/server-create-req.json new file mode 100644 index 00000000000..4068a9ed1ef --- /dev/null +++ b/doc/api_samples/servers/v2.47/server-create-req.json @@ -0,0 +1,30 @@ +{ + "server" : { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "name" : "new-server-test", + "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", + "flavorRef" : "6", + "availability_zone": "nova", + "OS-DCF:diskConfig": "AUTO", + "metadata" : { + "My Server Name" : "Apache1" + }, + "personality": [ + { + "path": "/etc/banner.txt", + "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ], + "security_groups": [ + { + "name": "default" + } + ], + "user_data" : "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "networks": "auto" + }, + "OS-SCH-HNT:scheduler_hints": { + "same_host": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.47/server-create-resp.json b/doc/api_samples/servers/v2.47/server-create-resp.json new file mode 100644 index 00000000000..dd0bb9f2284 --- /dev/null +++ b/doc/api_samples/servers/v2.47/server-create-resp.json @@ -0,0 +1,22 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "adminPass": "S5wqy9sPYUvU", + "id": "97108291-2fd7-4dc2-a909-eaae0306a6a9", + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9", + "rel": "bookmark" + } + ], + "security_groups": [ + { + "name": "default" + } + ] + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.47/server-get-resp.json b/doc/api_samples/servers/v2.47/server-get-resp.json new file mode 100644 index 00000000000..51dc94949aa --- /dev/null +++ b/doc/api_samples/servers/v2.47/server-get-resp.json @@ -0,0 +1,98 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "compute", + "OS-EXT-SRV-ATTR:hostname": "new-server-test", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:reservation_id": "r-ov3q80zj", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "OS-SRV-USG:launched_at": "2017-02-14T19:23:59.895661", + "OS-SRV-USG:terminated_at": null, + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", + "OS-EXT-IPS:type": "fixed", + "addr": "192.168.0.3", + "version": 4 + } + ] + }, + "config_drive": "", + "created": "2017-02-14T19:23:58Z", + "description": null, + "flavor": { + "disk": 1, + "ephemeral": 0, + "extra_specs": { + "hw:cpu_model": "SandyBridge", + "hw:mem_page_size": "2048", + "hw:cpu_policy": "dedicated" + }, + "original_name": "m1.tiny.specs", + "ram": 512, + "swap": 0, + "vcpus": 1 + }, + "hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6", + "host_status": "UP", + "id": "9168b536-cd40-4630-b43f-b259807c6e87", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "key_name": null, + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/9168b536-cd40-4630-b43f-b259807c6e87", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/9168b536-cd40-4630-b43f-b259807c6e87", + "rel": "bookmark" + } + ], + "locked": false, + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "os-extended-volumes:volumes_attached": [ + { + "delete_on_termination": false, + "id": "volume_id1" + }, + { + "delete_on_termination": false, + "id": "volume_id2" + } + ], + "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "status": "ACTIVE", + "tags": [], + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2017-02-14T19:24:00Z", + "user_id": "fake" + } +} diff --git a/doc/api_samples/servers/v2.47/server-update-req.json b/doc/api_samples/servers/v2.47/server-update-req.json new file mode 100644 index 00000000000..56e1a8c56a6 --- /dev/null +++ b/doc/api_samples/servers/v2.47/server-update-req.json @@ -0,0 +1,8 @@ +{ + "server": { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "OS-DCF:diskConfig": "AUTO", + "name" : "new-server-test" + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.47/server-update-resp.json b/doc/api_samples/servers/v2.47/server-update-resp.json new file mode 100644 index 00000000000..d2b2545f35a --- /dev/null +++ b/doc/api_samples/servers/v2.47/server-update-resp.json @@ -0,0 +1,58 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "addr": "192.168.0.3", + "version": 4 + } + ] + }, + "created": "2012-12-02T02:11:57Z", + "description": null, + "flavor": { + "disk": 1, + "ephemeral": 0, + "extra_specs": {}, + "original_name": "m1.tiny", + "ram": 512, + "swap": 0, + "vcpus": 1 + }, + "hostId": "6e84af987b4e7ec1c039b16d21f508f4a505672bd94fb0218b668d07", + "id": "324dfb7d-f4a9-419a-9a19-237df04b443b", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "links": [ + { + "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/324dfb7d-f4a9-419a-9a19-237df04b443b", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/324dfb7d-f4a9-419a-9a19-237df04b443b", + "rel": "bookmark" + } + ], + "locked": false, + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "progress": 0, + "status": "ACTIVE", + "tags": [], + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2012-12-02T02:11:58Z", + "user_id": "fake" + } +} diff --git a/doc/api_samples/servers/v2.47/servers-details-resp.json b/doc/api_samples/servers/v2.47/servers-details-resp.json new file mode 100644 index 00000000000..003bc65198e --- /dev/null +++ b/doc/api_samples/servers/v2.47/servers-details-resp.json @@ -0,0 +1,100 @@ +{ + "servers": [ + { + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "compute", + "OS-EXT-SRV-ATTR:hostname": "new-server-test", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:reservation_id": "r-iffothgx", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "OS-SRV-USG:launched_at": "2017-02-14T19:24:43.891568", + "OS-SRV-USG:terminated_at": null, + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", + "OS-EXT-IPS:type": "fixed", + "addr": "192.168.0.3", + "version": 4 + } + ] + }, + "config_drive": "", + "created": "2017-02-14T19:24:42Z", + "description": null, + "flavor": { + "disk": 1, + "ephemeral": 0, + "extra_specs": { + "hw:cpu_model": "SandyBridge", + "hw:mem_page_size": "2048", + "hw:cpu_policy": "dedicated" + }, + "original_name": "m1.tiny.specs", + "ram": 512, + "swap": 0, + "vcpus": 1 + }, + "hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6", + "host_status": "UP", + "id": "764e369e-a874-4401-b7ce-43e4760888da", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "key_name": null, + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/764e369e-a874-4401-b7ce-43e4760888da", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/764e369e-a874-4401-b7ce-43e4760888da", + "rel": "bookmark" + } + ], + "locked": false, + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "os-extended-volumes:volumes_attached": [ + { + "delete_on_termination": false, + "id": "volume_id1" + }, + { + "delete_on_termination": false, + "id": "volume_id2" + } + ], + "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "status": "ACTIVE", + "tags": [], + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2017-02-14T19:24:43Z", + "user_id": "fake" + } + ] +} diff --git a/doc/api_samples/servers/v2.47/servers-list-resp.json b/doc/api_samples/servers/v2.47/servers-list-resp.json new file mode 100644 index 00000000000..c197a33167f --- /dev/null +++ b/doc/api_samples/servers/v2.47/servers-list-resp.json @@ -0,0 +1,18 @@ +{ + "servers": [ + { + "id": "6e3a87e6-a133-452e-86e1-a31291c1b1c8", + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/6e3a87e6-a133-452e-86e1-a31291c1b1c8", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/6e3a87e6-a133-452e-86e1-a31291c1b1c8", + "rel": "bookmark" + } + ], + "name": "new-server-test" + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.52/server-create-req.json b/doc/api_samples/servers/v2.52/server-create-req.json new file mode 100644 index 00000000000..36d2b4cf5cc --- /dev/null +++ b/doc/api_samples/servers/v2.52/server-create-req.json @@ -0,0 +1,31 @@ +{ + "server" : { + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "name" : "new-server-test", + "imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b", + "flavorRef" : "http://openstack.example.com/flavors/1", + "availability_zone": "nova", + "OS-DCF:diskConfig": "AUTO", + "metadata" : { + "My Server Name" : "Apache1" + }, + "personality": [ + { + "path": "/etc/banner.txt", + "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ], + "security_groups": [ + { + "name": "default" + } + ], + "user_data" : "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "networks": "auto", + "tags": ["tag1", "tag2"] + }, + "OS-SCH-HNT:scheduler_hints": { + "same_host": "48e6a9f6-30af-47e0-bc04-acaed113bb4e" + } +} diff --git a/doc/api_samples/servers/v2.52/server-create-resp.json b/doc/api_samples/servers/v2.52/server-create-resp.json new file mode 100644 index 00000000000..dd0bb9f2284 --- /dev/null +++ b/doc/api_samples/servers/v2.52/server-create-resp.json @@ -0,0 +1,22 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "adminPass": "S5wqy9sPYUvU", + "id": "97108291-2fd7-4dc2-a909-eaae0306a6a9", + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9", + "rel": "bookmark" + } + ], + "security_groups": [ + { + "name": "default" + } + ] + } +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.52/server-get-resp.json b/doc/api_samples/servers/v2.52/server-get-resp.json new file mode 100644 index 00000000000..ff651f8547a --- /dev/null +++ b/doc/api_samples/servers/v2.52/server-get-resp.json @@ -0,0 +1,94 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "compute", + "OS-EXT-SRV-ATTR:hostname": "new-server-test", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:reservation_id": "r-ov3q80zj", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "OS-SRV-USG:launched_at": "2017-02-14T19:23:59.895661", + "OS-SRV-USG:terminated_at": null, + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", + "OS-EXT-IPS:type": "fixed", + "addr": "192.168.0.3", + "version": 4 + } + ] + }, + "config_drive": "", + "created": "2017-02-14T19:23:58Z", + "description": null, + "flavor": { + "disk": 1, + "ephemeral": 0, + "extra_specs": {}, + "original_name": "m1.tiny", + "ram": 512, + "swap": 0, + "vcpus": 1 + }, + "hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6", + "host_status": "UP", + "id": "9168b536-cd40-4630-b43f-b259807c6e87", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "key_name": null, + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/9168b536-cd40-4630-b43f-b259807c6e87", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/9168b536-cd40-4630-b43f-b259807c6e87", + "rel": "bookmark" + } + ], + "locked": false, + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "os-extended-volumes:volumes_attached": [ + { + "delete_on_termination": false, + "id": "volume_id1" + }, + { + "delete_on_termination": false, + "id": "volume_id2" + } + ], + "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "status": "ACTIVE", + "tags": ["tag1", "tag2"], + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2017-02-14T19:24:00Z", + "user_id": "fake" + } +} diff --git a/doc/api_samples/servers/v2.52/servers-details-resp.json b/doc/api_samples/servers/v2.52/servers-details-resp.json new file mode 100644 index 00000000000..8beef359845 --- /dev/null +++ b/doc/api_samples/servers/v2.52/servers-details-resp.json @@ -0,0 +1,96 @@ +{ + "servers": [ + { + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "compute", + "OS-EXT-SRV-ATTR:hostname": "new-server-test", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:reservation_id": "r-iffothgx", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "OS-SRV-USG:launched_at": "2017-02-14T19:24:43.891568", + "OS-SRV-USG:terminated_at": null, + "accessIPv4": "1.2.3.4", + "accessIPv6": "80fe::", + "addresses": { + "private": [ + { + "OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff", + "OS-EXT-IPS:type": "fixed", + "addr": "192.168.0.3", + "version": 4 + } + ] + }, + "config_drive": "", + "created": "2017-02-14T19:24:42Z", + "description": null, + "flavor": { + "disk": 1, + "ephemeral": 0, + "extra_specs": {}, + "original_name": "m1.tiny", + "ram": 512, + "swap": 0, + "vcpus": 1 + }, + "hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6", + "host_status": "UP", + "id": "764e369e-a874-4401-b7ce-43e4760888da", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "key_name": null, + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/764e369e-a874-4401-b7ce-43e4760888da", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/764e369e-a874-4401-b7ce-43e4760888da", + "rel": "bookmark" + } + ], + "locked": false, + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "os-extended-volumes:volumes_attached": [ + { + "delete_on_termination": false, + "id": "volume_id1" + }, + { + "delete_on_termination": false, + "id": "volume_id2" + } + ], + "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "status": "ACTIVE", + "tags": ["tag1", "tag2"], + "tenant_id": "6f70656e737461636b20342065766572", + "updated": "2017-02-14T19:24:43Z", + "user_id": "fake" + } + ] +} diff --git a/doc/api_samples/servers/v2.52/servers-list-resp.json b/doc/api_samples/servers/v2.52/servers-list-resp.json new file mode 100644 index 00000000000..c197a33167f --- /dev/null +++ b/doc/api_samples/servers/v2.52/servers-list-resp.json @@ -0,0 +1,18 @@ +{ + "servers": [ + { + "id": "6e3a87e6-a133-452e-86e1-a31291c1b1c8", + "links": [ + { + "href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/6e3a87e6-a133-452e-86e1-a31291c1b1c8", + "rel": "self" + }, + { + "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/6e3a87e6-a133-452e-86e1-a31291c1b1c8", + "rel": "bookmark" + } + ], + "name": "new-server-test" + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/servers/v2.9/server-get-resp.json b/doc/api_samples/servers/v2.9/server-get-resp.json index cbd4336469a..c92ff85fd58 100644 --- a/doc/api_samples/servers/v2.9/server-get-resp.json +++ b/doc/api_samples/servers/v2.9/server-get-resp.json @@ -48,7 +48,40 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "b8b357f7100d4391828f2177c922ef93", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:hostname": "fake-hostname", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1", + "delete_on_termination": false + }, + { + "id": "volume_id2", + "delete_on_termination": false + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302", + "OS-SRV-USG:terminated_at": null, "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], "status": "ACTIVE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2013-09-03T04:01:33Z", diff --git a/doc/api_samples/servers/v2.9/servers-details-resp.json b/doc/api_samples/servers/v2.9/servers-details-resp.json index 44bfad504df..4c9768dbe7b 100644 --- a/doc/api_samples/servers/v2.9/servers-details-resp.json +++ b/doc/api_samples/servers/v2.9/servers-details-resp.json @@ -49,7 +49,40 @@ "My Server Name": "Apache1" }, "name": "new-server-test", + "config_drive": "", + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-AZ:availability_zone": "nova", + "OS-EXT-SRV-ATTR:host": "c3f14e9812ad496baf92ccfb3c61e15f", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:reservation_id": "r-00000001", + "OS-EXT-SRV-ATTR:launch_index": 0, + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:hostname": "fake-hostname", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "os-extended-volumes:volumes_attached": [ + { + "id": "volume_id1", + "delete_on_termination": false + }, + { + "id": "volume_id2", + "delete_on_termination": false + } + ], + "OS-SRV-USG:launched_at": "2013-09-23T13:53:12.774549", + "OS-SRV-USG:terminated_at": null, "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], "status": "ACTIVE", "tenant_id": "6f70656e737461636b20342065766572", "updated": "2013-09-03T04:01:32Z", diff --git a/doc/api_samples/versions/v21-version-get-resp.json b/doc/api_samples/versions/v21-version-get-resp.json index 41151e58113..1fc439ffaed 100644 --- a/doc/api_samples/versions/v21-version-get-resp.json +++ b/doc/api_samples/versions/v21-version-get-resp.json @@ -19,7 +19,7 @@ } ], "status": "CURRENT", - "version": "2.25", + "version": "2.53", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/doc/api_samples/versions/versions-get-resp.json b/doc/api_samples/versions/versions-get-resp.json index f5bbb193187..f9965b4167d 100644 --- a/doc/api_samples/versions/versions-get-resp.json +++ b/doc/api_samples/versions/versions-get-resp.json @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.25", + "version": "2.53", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/doc/ext/feature_matrix.py b/doc/ext/feature_matrix.py new file mode 100644 index 00000000000..16577509bc5 --- /dev/null +++ b/doc/ext/feature_matrix.py @@ -0,0 +1,581 @@ +# 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. + +""" +This provides a sphinx extension able to render from an ini file (from the +doc/source/* directory) a feature matrix into the developer documentation. + +It is used via a single directive in the .rst file + + .. feature_matrix:: feature_classification.ini + +""" + +import re +import sys + +from six.moves import configparser + +from docutils import nodes +from docutils.parsers import rst + + +class Matrix(object): + """Matrix represents the entire feature matrix parsed from an ini file + + This includes: + * self.features is a list of MatrixFeature instances, the rows and cells + * self.targets is a dict of (MatrixTarget.key, MatrixTarget), the columns + """ + def __init__(self): + self.features = [] + self.targets = {} + + +class MatrixTarget(object): + + def __init__(self, key, title, driver, hypervisor=None, architecture=None, + link=None): + """MatrixTarget modes a target, a column in the matrix + + This is usually a specific CI system, or collection of related + deployment configurations. + + :param key: Unique identifier for the hypervisor driver + :param title: Human friendly name of the hypervisor + :param driver: Name of the Nova driver + :param hypervisor: (optional) Name of the hypervisor, if many + :param architecture: (optional) Name of the architecture, if many + :param link: (optional) URL to docs about this target + """ + self.key = key + self.title = title + self.driver = driver + self.hypervisor = hypervisor + self.architecture = architecture + self.link = link + + +class MatrixImplementation(object): + + STATUS_COMPLETE = "complete" + STATUS_PARTIAL = "partial" + STATUS_MISSING = "missing" + STATUS_UKNOWN = "unknown" + + STATUS_ALL = [STATUS_COMPLETE, STATUS_PARTIAL, STATUS_MISSING, + STATUS_UKNOWN] + + def __init__(self, status=STATUS_MISSING, notes=None, release=None): + """MatrixImplementation models a cell in the matrix + + This models the current state of a target for a specific feature + + :param status: One off complete, partial, missing, unknown. + See the RST docs for a definition of those. + :param notes: Arbitrary string describing any caveats of the + implementation. Mandatory if status is 'partial'. + :param release: Letter of the release entry was last updated. + i.e. m=mitaka, c=cactus. If not known it is None. + """ + self.status = status + self.notes = notes + self.release = release + + +class MatrixFeature(object): + MATURITY_INCOMPLETE = "incomplete" + MATURITY_EXPERIMENTAL = "experimental" + MATURITY_COMPLETE = "complete" + MATURITY_DEPRECATED = "deprecated" + + MATURITY_ALL = [MATURITY_INCOMPLETE, MATURITY_EXPERIMENTAL, + MATURITY_COMPLETE, MATURITY_DEPRECATED] + + def __init__(self, key, title, + notes=None, cli=None, maturity=None, + api_doc_link=None, admin_doc_link=None, + tempest_test_uuids=None): + """MatrixFeature models a row in the matrix + + This initialises ``self.implementations``, which is a dict of + (MatrixTarget.key, MatrixImplementation) + This is the list of cells for the given row of the matrix. + + :param key: used as the HTML id for the details, and so in the URL + :param title: human friendly short title, used in the matrix + :param notes: Arbitrarily long string describing the feature + :param cli: list of cli commands related to the feature + :param maturity: incomplete, experimental, complete or deprecated + for a full definition see the rst doc + :param api_doc_link: URL to the API ref for this feature + :param admin_doc_link: URL to the admin docs for using this feature + :param tempest_test_uuids: uuids for tests that validate this feature + """ + if cli is None: + cli = [] + if tempest_test_uuids is None: + tempest_test_uuids = [] + self.key = key + self.title = title + self.notes = notes + self.implementations = {} + self.cli = cli + self.maturity = maturity + self.api_doc_link = api_doc_link + self.admin_doc_link = admin_doc_link + self.tempest_test_uuids = tempest_test_uuids + + +class FeatureMatrixDirective(rst.Directive): + """The Sphinx directive plugin + + Has single required argument, the filename for the ini file. + + The usage of the directive looks like this:: + + .. feature_matrix:: feature_matrix_gp.ini + + """ + required_arguments = 1 + + def run(self): + matrix = self._load_feature_matrix() + return self._build_markup(matrix) + + def _load_feature_matrix(self): + """Reads the feature-matrix.ini file and populates an instance + of the Matrix class with all the data. + + :returns: Matrix instance + """ + + # SafeConfigParser was deprecated in Python 3.2 + if sys.version_info >= (3, 2): + cfg = configparser.ConfigParser() + else: + cfg = configparser.SafeConfigParser() + env = self.state.document.settings.env + filename = self.arguments[0] + rel_fpath, fpath = env.relfn2path(filename) + with open(fpath) as fp: + cfg.readfp(fp) + + # This ensures that the docs are rebuilt whenever the + # .ini file changes + env.note_dependency(rel_fpath) + + matrix = Matrix() + matrix.targets = self._get_targets(cfg) + matrix.features = self._get_features(cfg, matrix.targets) + + return matrix + + def _get_targets(self, cfg): + """The 'targets' section is special - it lists all the + hypervisors that this file records data for. + """ + + targets = {} + + for section in cfg.sections(): + if not section.startswith("target."): + continue + + key = section[7:] + title = cfg.get(section, "title") + link = cfg.get(section, "link") + driver = key.split("-")[0] + target = MatrixTarget(key, title, driver, link=link) + + targets[key] = target + + return targets + + def _get_features(self, cfg, targets): + """All sections except 'targets' describe some feature of + the Nova hypervisor driver implementation. + """ + + features = [] + + for section in cfg.sections(): + if section == "targets": + continue + if section.startswith("target."): + continue + if not cfg.has_option(section, "title"): + raise Exception( + "'title' field missing in '[%s]' section" % section) + + title = cfg.get(section, "title") + + maturity = MatrixFeature.MATURITY_INCOMPLETE + if cfg.has_option(section, "maturity"): + maturity = cfg.get(section, "maturity").lower() + if maturity not in MatrixFeature.MATURITY_ALL: + raise Exception( + "'maturity' field value '%s' in ['%s']" + "section must be %s" % + (maturity, section, + ",".join(MatrixFeature.MATURITY_ALL))) + + notes = None + if cfg.has_option(section, "notes"): + notes = cfg.get(section, "notes") + cli = [] + if cfg.has_option(section, "cli"): + cli = cfg.get(section, "cli") + api_doc_link = None + if cfg.has_option(section, "api_doc_link"): + api_doc_link = cfg.get(section, "api_doc_link") + admin_doc_link = None + if cfg.has_option(section, "admin_doc_link"): + admin_doc_link = cfg.get(section, "admin_doc_link") + tempest_test_uuids = [] + if cfg.has_option(section, "tempest_test_uuids"): + tempest_test_uuids = cfg.get(section, "tempest_test_uuids") + + feature = MatrixFeature(section, title, notes, cli, maturity, + api_doc_link, admin_doc_link, tempest_test_uuids) + + # Now we've got the basic feature details, we must process + # the hypervisor driver implementation for each feature + for item in cfg.options(section): + key = item.replace("driver-impl-", "") + + if key not in targets: + # TODO(johngarbutt) would be better to skip known list + if item.startswith("driver-impl-"): + raise Exception( + "Driver impl '%s' in '[%s]' not declared" % + (item, section)) + continue + + impl_status_and_release = cfg.get(section, item) + impl_status_and_release = impl_status_and_release.split(":") + impl_status = impl_status_and_release[0] + release = None + if len(impl_status_and_release) == 2: + release = impl_status_and_release[1] + + if impl_status not in MatrixImplementation.STATUS_ALL: + raise Exception( + "'%s' value '%s' in '[%s]' section must be %s" % + (item, impl_status, section, + ",".join(MatrixImplementation.STATUS_ALL))) + + noteskey = "driver-notes-" + item[12:] + notes = None + if cfg.has_option(section, noteskey): + notes = cfg.get(section, noteskey) + + target = targets[key] + impl = MatrixImplementation(impl_status, notes, release) + feature.implementations[target.key] = impl + + for key in targets: + if key not in feature.implementations: + raise Exception("'%s' missing in '[%s]' section" % + (key, section)) + + features.append(feature) + + return features + + def _build_markup(self, matrix): + """Constructs the docutils content for the feature matrix""" + content = [] + self._build_summary(matrix, content) + self._build_details(matrix, content) + return content + + def _build_summary(self, matrix, content): + """Constructs the docutils content for the summary of + the feature matrix. + + The summary consists of a giant table, with one row + for each feature, and a column for each hypervisor + driver. It provides an 'at a glance' summary of the + status of each driver + """ + + summarytitle = nodes.subtitle(text="Summary") + summary = nodes.table() + cols = len(matrix.targets.keys()) + cols += 2 + summarygroup = nodes.tgroup(cols=cols) + summarybody = nodes.tbody() + summaryhead = nodes.thead() + + for i in range(cols): + summarygroup.append(nodes.colspec(colwidth=1)) + summarygroup.append(summaryhead) + summarygroup.append(summarybody) + summary.append(summarygroup) + content.append(summarytitle) + content.append(summary) + + # This sets up all the column headers - two fixed + # columns for feature name & status + header = nodes.row() + blank = nodes.entry() + blank.append(nodes.emphasis(text="Feature")) + header.append(blank) + blank = nodes.entry() + blank.append(nodes.emphasis(text="Maturity")) + header.append(blank) + summaryhead.append(header) + + # then one column for each hypervisor driver + impls = matrix.targets.keys() + impls.sort() + for key in impls: + target = matrix.targets[key] + implcol = nodes.entry() + header.append(implcol) + if target.link: + uri = target.link + target_ref = nodes.reference("", refuri=uri) + target_txt = nodes.inline() + implcol.append(target_txt) + target_txt.append(target_ref) + target_ref.append(nodes.strong(text=target.title)) + else: + implcol.append(nodes.strong(text=target.title)) + + # We now produce the body of the table, one row for + # each feature to report on + for feature in matrix.features: + item = nodes.row() + + # the hyperlink target name linking to details + id = re.sub("[^a-zA-Z0-9_]", "_", + feature.key) + + # first the to fixed columns for title/status + keycol = nodes.entry() + item.append(keycol) + keyref = nodes.reference(refid=id) + keytxt = nodes.inline() + keycol.append(keytxt) + keytxt.append(keyref) + keyref.append(nodes.strong(text=feature.title)) + + maturitycol = nodes.entry() + item.append(maturitycol) + maturitycol.append(nodes.inline( + text=feature.maturity, + classes=["fm_maturity_" + feature.maturity])) + + # and then one column for each hypervisor driver + impls = matrix.targets.keys() + impls.sort() + for key in impls: + target = matrix.targets[key] + impl = feature.implementations[key] + implcol = nodes.entry() + item.append(implcol) + + id = re.sub("[^a-zA-Z0-9_]", "_", + feature.key + "_" + key) + + implref = nodes.reference(refid=id) + impltxt = nodes.inline() + implcol.append(impltxt) + impltxt.append(implref) + + impl_status = "" + if impl.status == MatrixImplementation.STATUS_COMPLETE: + impl_status = u"\u2714" + elif impl.status == MatrixImplementation.STATUS_MISSING: + impl_status = u"\u2716" + elif impl.status == MatrixImplementation.STATUS_PARTIAL: + impl_status = u"\u2714" + elif impl.status == MatrixImplementation.STATUS_UKNOWN: + impl_status = u"?" + + implref.append(nodes.literal( + text=impl_status, + classes=["fm_impl_summary", "fm_impl_" + impl.status])) + + if impl.release: + implref.append(nodes.inline(text=" %s" % impl.release)) + + summarybody.append(item) + + def _build_details(self, matrix, content): + """Constructs the docutils content for the details of + the feature matrix. + + This is generated as a bullet list of features. + Against each feature we provide the description of + the feature and then the details of the hypervisor + impls, with any driver specific notes that exist + """ + + detailstitle = nodes.subtitle(text="Details") + details = nodes.bullet_list() + + content.append(detailstitle) + content.append(details) + + # One list entry for each feature we're reporting on + for feature in matrix.features: + item = nodes.list_item() + + # The hypervisor target name linked from summary table + id = re.sub("[^a-zA-Z0-9_]", "_", + feature.key) + + # Highlight the feature title name + item.append(nodes.strong(text=feature.title, + ids=[id])) + + if feature.notes is not None: + para_notes = nodes.paragraph() + para_notes.append(nodes.inline(text=feature.notes)) + item.append(para_notes) + + self._add_feature_info(item, feature) + + if feature.cli: + item.append(self._create_cli_paragraph(feature)) + + para_divers = nodes.paragraph() + para_divers.append(nodes.strong(text="drivers:")) + # A sub-list giving details of each hypervisor target + impls = nodes.bullet_list() + for key in feature.implementations: + target = matrix.targets[key] + impl = feature.implementations[key] + subitem = nodes.list_item() + + id = re.sub("[^a-zA-Z0-9_]", "_", + feature.key + "_" + key) + subitem += [ + nodes.strong(text=target.title + ": "), + nodes.literal(text=impl.status, + classes=["fm_impl_" + impl.status], + ids=[id]), + ] + if impl.release: + release_letter = impl.release.upper() + release_text = \ + ' (updated in "%s" release)' % release_letter + subitem.append(nodes.inline(text=release_text)) + if impl.notes is not None: + subitem.append(self._create_notes_paragraph(impl.notes)) + impls.append(subitem) + + para_divers.append(impls) + item.append(para_divers) + details.append(item) + + def _add_feature_info(self, item, feature): + para_info = nodes.paragraph() + para_info.append(nodes.strong(text="info:")) + info_list = nodes.bullet_list() + + maturity_literal = nodes.literal(text=feature.maturity, + classes=["fm_maturity_" + feature.maturity]) + self._append_info_list_item(info_list, + "Maturity", items=[maturity_literal]) + self._append_info_list_item(info_list, + "API Docs", link=feature.api_doc_link) + self._append_info_list_item(info_list, + "Admin Docs", link=feature.admin_doc_link) + + tempest_items = [] + if feature.tempest_test_uuids: + for uuid in feature.tempest_test_uuids.split(";"): + base = "https://github.com/openstack/tempest/search?q=%s" + link = base % uuid + inline_ref = self._get_uri_ref(link, text=uuid) + tempest_items.append(inline_ref) + tempest_items.append(nodes.inline(text=", ")) + # removing trailing punctuation + tempest_items = tempest_items[:-1] + self._append_info_list_item(info_list, + "Tempest tests", items=tempest_items) + + para_info.append(info_list) + item.append(para_info) + + def _get_uri_ref(self, link, text=None): + if not text: + text = link + ref = nodes.reference("", text, refuri=link) + inline = nodes.inline() + inline.append(ref) + return inline + + def _append_info_list_item(self, info_list, title, + text=None, link=None, items=None): + subitem = nodes.list_item() + subitem.append(nodes.strong(text="%s: " % title)) + if items: + for item in items: + subitem.append(item) + elif link: + inline_link = self._get_uri_ref(link, text) + subitem.append(inline_link) + elif text: + subitem.append(nodes.literal(text=text)) + info_list.append(subitem) + + def _create_cli_paragraph(self, feature): + """Create a paragraph which represents the CLI commands of the feature + + The paragraph will have a bullet list of CLI commands. + """ + para = nodes.paragraph() + para.append(nodes.strong(text="CLI commands:")) + commands = nodes.bullet_list() + for c in feature.cli.split(";"): + cli_command = nodes.list_item() + cli_command += nodes.literal(text=c, classes=["fm_cli"]) + commands.append(cli_command) + para.append(commands) + return para + + def _create_notes_paragraph(self, notes): + """Constructs a paragraph which represents the implementation notes + + The paragraph consists of text and clickable URL nodes if links were + given in the notes. + """ + para = nodes.paragraph() + # links could start with http:// or https:// + link_idxs = [m.start() for m in re.finditer('https?://', notes)] + start_idx = 0 + for link_idx in link_idxs: + # assume the notes start with text (could be empty) + para.append(nodes.inline(text=notes[start_idx:link_idx])) + # create a URL node until the next text or the end of the notes + link_end_idx = notes.find(" ", link_idx) + if link_end_idx == -1: + # In case the notes end with a link without a blank + link_end_idx = len(notes) + uri = notes[link_idx:link_end_idx + 1] + para.append(nodes.reference("", uri, refuri=uri)) + start_idx = link_end_idx + 1 + + # get all text after the last link (could be empty) or all of the + # text if no link was given + para.append(nodes.inline(text=notes[start_idx:])) + return para + + +def setup(app): + app.add_directive('feature_matrix', FeatureMatrixDirective) + app.add_stylesheet('feature-matrix.css') diff --git a/doc/ext/nova_todo.py b/doc/ext/nova_todo.py deleted file mode 100644 index eeed8f324c5..00000000000 --- a/doc/ext/nova_todo.py +++ /dev/null @@ -1,104 +0,0 @@ -# -*- coding: utf-8 -*- -# This is a hack of the builtin todo extension, to make the todo_list -# more user friendly. - -from sphinx.ext.todo import * -import re - - -def _(s): - return s - - -def process_todo_nodes(app, doctree, fromdocname): - if not app.config['todo_include_todos']: - for node in doctree.traverse(todo_node): - node.parent.remove(node) - - # Replace all todolist nodes with a list of the collected todos. - # Augment each todo with a backlink to the original location. - env = app.builder.env - - if not hasattr(env, 'todo_all_todos'): - env.todo_all_todos = [] - - # remove the item that was added in the constructor, since I'm tired of - # reading through docutils for the proper way to construct an empty list - lists = [] - for i in range(5): - lists.append(nodes.bullet_list("", nodes.Text('', ''))) - lists[i].remove(lists[i][0]) - lists[i]['classes'].append('todo_list') - - for node in doctree.traverse(todolist): - if not app.config['todo_include_todos']: - node.replace_self([]) - continue - - for todo_info in env.todo_all_todos: - para = nodes.paragraph() - # Create a reference - newnode = nodes.reference('', '') - - filename = env.doc2path(todo_info['docname'], base=None) - link = (_('%(filename)s, line %(line_info)d') % - {'filename': filename, 'line_info': todo_info['lineno']}) - - innernode = nodes.emphasis(link, link) - newnode['refdocname'] = todo_info['docname'] - - try: - newnode['refuri'] = app.builder.get_relative_uri( - fromdocname, todo_info['docname']) - newnode['refuri'] += '#' + todo_info['target']['refid'] - except NoUri: - # ignore if no URI can be determined, e.g. for LaTeX output - pass - - newnode.append(innernode) - para += newnode - para['classes'].append('todo_link') - - todo_entry = todo_info['todo'] - - env.resolve_references(todo_entry, todo_info['docname'], - app.builder) - - item = nodes.list_item('', para) - todo_entry[1]['classes'].append('details') - - comment = todo_entry[1] - - m = re.match(r"^P(\d)", comment.astext()) - priority = 5 - if m: - priority = int(m.group(1)) - if priority < 0: - priority = 1 - if priority > 5: - priority = 5 - - item['classes'].append('todo_p' + str(priority)) - todo_entry['classes'].append('todo_p' + str(priority)) - - item.append(comment) - - lists[priority - 1].insert(0, item) - - node.replace_self(lists) - - -def setup(app): - app.add_config_value('todo_include_todos', False, False) - - app.add_node(todolist) - app.add_node(todo_node, - html=(visit_todo_node, depart_todo_node), - latex=(visit_todo_node, depart_todo_node), - text=(visit_todo_node, depart_todo_node)) - - app.add_directive('todo', Todo) - app.add_directive('todolist', TodoList) - app.connect('doctree-read', process_todos) - app.connect('doctree-resolved', process_todo_nodes) - app.connect('env-purge-doc', purge_todos) diff --git a/doc/ext/support_matrix.py b/doc/ext/support_matrix.py index 67e9b44dfb2..96e0b379e97 100644 --- a/doc/ext/support_matrix.py +++ b/doc/ext/support_matrix.py @@ -23,8 +23,8 @@ """ import re +import sys -import six from six.moves import configparser from docutils import nodes @@ -58,7 +58,9 @@ class SupportMatrixFeature(object): STATUS_CONDITION, STATUS_OPTIONAL] def __init__(self, key, title, status=STATUS_OPTIONAL, - group=None, notes=None, cli=[]): + group=None, notes=None, cli=None): + if not cli: + cli = [] # A unique key (eg 'foo.bar.wizz') to identify the feature self.key = key # A human friendly short title for the feature @@ -129,7 +131,11 @@ def _load_support_matrix(self): :returns: SupportMatrix instance """ - cfg = configparser.SafeConfigParser() + # SafeConfigParser was deprecated in Python 3.2 + if sys.version_info >= (3, 2): + cfg = configparser.ConfigParser() + else: + cfg = configparser.SafeConfigParser() env = self.state.document.settings.env fname = self.arguments[0] rel_fpath, fpath = env.relfn2path(fname) @@ -264,7 +270,7 @@ def _get_features(self, cfg, targets): for key in targets: if key not in feature.implementations: raise Exception("'%s' missing in '[%s]' section" % - (target.key, section)) + (key, section)) features.append(feature) @@ -317,7 +323,7 @@ def _build_summary(self, matrix, content): summaryhead.append(header) # then one column for each hypervisor driver - impls = matrix.targets.keys() + impls = list(matrix.targets.keys()) impls.sort() for key in impls: target = matrix.targets[key] @@ -350,7 +356,7 @@ def _build_summary(self, matrix, content): classes=["sp_feature_" + feature.status])) # and then one column for each hypervisor driver - impls = matrix.targets.keys() + impls = list(matrix.targets.keys()) impls.sort() for key in impls: target = matrix.targets[key] @@ -469,10 +475,10 @@ def _build_notes(self, content): notes.append(item) def _create_cli_paragraph(self, feature): - ''' Create a paragraph which represents the CLI commands of the feature + """Create a paragraph which represents the CLI commands of the feature The paragraph will have a bullet list of CLI commands. - ''' + """ para = nodes.paragraph() para.append(nodes.strong(text="CLI commands:")) commands = nodes.bullet_list() @@ -484,7 +490,7 @@ def _create_cli_paragraph(self, feature): return para def _create_notes_paragraph(self, notes): - """ Constructs a paragraph which represents the implementation notes + """Constructs a paragraph which represents the implementation notes The paragraph consists of text and clickable URL nodes if links were given in the notes. diff --git a/doc/ext/versioned_notifications.py b/doc/ext/versioned_notifications.py index 5f7fe5ed187..5d80ed64210 100644 --- a/doc/ext/versioned_notifications.py +++ b/doc/ext/versioned_notifications.py @@ -20,27 +20,46 @@ """ -from sphinx.util.compat import Directive from docutils import nodes +import importlib +import pkgutil +from sphinx.util.compat import Directive +import nova.notifications.objects +from nova.notifications.objects import base as notification from nova.objects import base -from nova.objects import notification - - -def full_name(cls): - return cls.__module__ + '.' + cls.__name__ class VersionedNotificationDirective(Directive): - LINK_PREFIX = 'https://git.openstack.org/cgit/openstack/nova/plain/' SAMPLE_ROOT = 'doc/notification_samples/' + TOGGLE_SCRIPT = """ + + + + +""" def run(self): notifications = self._collect_notifications() return self._build_markup(notifications) + def _import_all_notification_packages(self): + map(lambda module: importlib.import_module(module), + ('nova.notifications.objects.' + name for _, name, _ in + pkgutil.iter_modules(nova.notifications.objects.__path__))) + def _collect_notifications(self): + self._import_all_notification_packages() + base.NovaObjectRegistry.register_notification_objects() notifications = [] ovos = base.NovaObjectRegistry.obj_classes() for name, cls in ovos.items(): @@ -50,14 +69,15 @@ def _collect_notifications(self): payload_name = cls.fields['payload'].objname payload_cls = ovos[payload_name][0] - - notifications.append((full_name(cls), full_name(payload_cls), - cls.sample)) - return notifications + for sample in cls.samples: + notifications.append((cls.__name__, + payload_cls.__name__, + sample)) + return sorted(notifications) def _build_markup(self, notifications): content = [] - cols = ['Notification class', 'Payload class', 'Sample file link'] + cols = ['Event type', 'Notification class', 'Payload class', 'Sample'] table = nodes.table() content.append(table) group = nodes.tgroup(cols=len(cols)) @@ -66,7 +86,7 @@ def _build_markup(self, notifications): head = nodes.thead() group.append(head) - for i in range(len(cols)): + for _ in cols: group.append(nodes.colspec(colwidth=1)) body = nodes.tbody() @@ -82,9 +102,16 @@ def _build_markup(self, notifications): col.append(text) # fill the table content, one notification per row - for name, payload, sample in notifications: + for name, payload, sample_file in notifications: + event_type = sample_file[0: -5].replace('-', '.') + row = nodes.row() body.append(row) + col = nodes.entry() + row.append(col) + text = nodes.literal(text=event_type) + col.append(text) + col = nodes.entry() row.append(col) text = nodes.literal(text=name) @@ -97,12 +124,19 @@ def _build_markup(self, notifications): col = nodes.entry() row.append(col) - ref = nodes.reference(refuri=self.LINK_PREFIX + - self.SAMPLE_ROOT + sample) - txt = nodes.inline() - col.append(txt) - txt.append(ref) - ref.append(nodes.literal(text=sample)) + + with open(self.SAMPLE_ROOT + sample_file, 'r') as f: + sample_content = f.read() + + event_type = sample_file[0: -5] + html_str = self.TOGGLE_SCRIPT % ((event_type, ) * 3) + html_str += ("" % event_type) + html_str += ("
%s
" + % (event_type, sample_content)) + + raw = nodes.raw('', html_str, format="html") + col.append(raw) return content diff --git a/doc/notification_samples/aggregate-create-end.json b/doc/notification_samples/aggregate-create-end.json new file mode 100644 index 00000000000..ab48994fa41 --- /dev/null +++ b/doc/notification_samples/aggregate-create-end.json @@ -0,0 +1,19 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.1", + "nova_object.namespace": "nova", + "nova_object.name": "AggregatePayload", + "nova_object.data": { + "name": "my-aggregate", + "metadata": { + "availability_zone": "nova" + }, + "hosts": [], + "id": 1, + "uuid": "788608ec-ebdc-45c5-bc7f-e5f24ab92c80" + } + }, + "event_type": "aggregate.create.end", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/aggregate-create-start.json b/doc/notification_samples/aggregate-create-start.json new file mode 100644 index 00000000000..775feea7d55 --- /dev/null +++ b/doc/notification_samples/aggregate-create-start.json @@ -0,0 +1,19 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.1", + "nova_object.namespace": "nova", + "nova_object.name": "AggregatePayload", + "nova_object.data": { + "name": "my-aggregate", + "metadata": { + "availability_zone": "nova" + }, + "hosts": null, + "id": null, + "uuid": "788608ec-ebdc-45c5-bc7f-e5f24ab92c80" + } + }, + "event_type": "aggregate.create.start", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/aggregate-delete-end.json b/doc/notification_samples/aggregate-delete-end.json new file mode 100644 index 00000000000..058c3a9e5a6 --- /dev/null +++ b/doc/notification_samples/aggregate-delete-end.json @@ -0,0 +1,19 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.1", + "nova_object.namespace": "nova", + "nova_object.name": "AggregatePayload", + "nova_object.data": { + "name": "my-aggregate", + "metadata": { + "availability_zone": "nova" + }, + "hosts": [], + "id": 1, + "uuid": "788608ec-ebdc-45c5-bc7f-e5f24ab92c80" + } + }, + "event_type": "aggregate.delete.end", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/aggregate-delete-start.json b/doc/notification_samples/aggregate-delete-start.json new file mode 100644 index 00000000000..b4c75abbe03 --- /dev/null +++ b/doc/notification_samples/aggregate-delete-start.json @@ -0,0 +1,19 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.1", + "nova_object.namespace": "nova", + "nova_object.name": "AggregatePayload", + "nova_object.data": { + "name": "my-aggregate", + "metadata": { + "availability_zone": "nova" + }, + "hosts": [], + "id": 1, + "uuid": "788608ec-ebdc-45c5-bc7f-e5f24ab92c80" + } + }, + "event_type": "aggregate.delete.start", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/compute-exception.json b/doc/notification_samples/compute-exception.json new file mode 100644 index 00000000000..0a4d6a691b3 --- /dev/null +++ b/doc/notification_samples/compute-exception.json @@ -0,0 +1,16 @@ +{ + "event_type": "compute.exception", + "payload": { + "nova_object.data": { + "exception": "AggregateNameExists", + "exception_message": "Aggregate versioned_exc_aggregate already exists.", + "function_name": "_aggregate_create_in_db", + "module_name": "nova.objects.aggregate" + }, + "nova_object.name": "ExceptionPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }, + "priority": "ERROR", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/flavor-create.json b/doc/notification_samples/flavor-create.json new file mode 100644 index 00000000000..4cca7a0e43a --- /dev/null +++ b/doc/notification_samples/flavor-create.json @@ -0,0 +1,25 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.namespace": "nova", + "nova_object.version": "1.3", + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "name": "test_flavor", + "memory_mb": 1024, + "ephemeral_gb": 0, + "disabled": false, + "vcpus": 2, + "swap": 0, + "rxtx_factor": 2.0, + "is_public": true, + "root_gb": 10, + "vcpu_weight": 0, + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "extra_specs": null, + "projects": [] + } + }, + "event_type": "flavor.create", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/flavor-delete.json b/doc/notification_samples/flavor-delete.json new file mode 100644 index 00000000000..1cad9c1f004 --- /dev/null +++ b/doc/notification_samples/flavor-delete.json @@ -0,0 +1,25 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.namespace": "nova", + "nova_object.version": "1.3", + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "name": "test_flavor", + "memory_mb": 1024, + "ephemeral_gb": 0, + "disabled": false, + "vcpus": 2, + "swap": 0, + "rxtx_factor": 2.0, + "is_public": true, + "root_gb": 10, + "vcpu_weight": 0, + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "extra_specs": null, + "projects": null + } + }, + "event_type": "flavor.delete", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/flavor-update.json b/doc/notification_samples/flavor-update.json new file mode 100644 index 00000000000..c76311a5997 --- /dev/null +++ b/doc/notification_samples/flavor-update.json @@ -0,0 +1,28 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.namespace": "nova", + "nova_object.version": "1.3", + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "name": "test_flavor", + "memory_mb": 1024, + "ephemeral_gb": 0, + "disabled": false, + "vcpus": 2, + "extra_specs": { + "key1": "value1", + "key2": "value2" + }, + "projects": ["fake_tenant"], + "swap": 0, + "rxtx_factor": 2.0, + "is_public": false, + "root_gb": 10, + "vcpu_weight": 0, + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3" + } + }, + "event_type": "flavor.update", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/instance-create-end.json b/doc/notification_samples/instance-create-end.json new file mode 100644 index 00000000000..c27444e2e88 --- /dev/null +++ b/doc/notification_samples/instance-create-end.json @@ -0,0 +1,91 @@ +{ + "event_type":"instance.create.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices":[], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "keypairs": [{ + "nova_object.name": "KeypairPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "user_id": "fake", + "name": "my-key", + "fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated-by-Nova", + "type": "ssh" + } + }], + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":null, + "power_state":"running", + "tags":["tag"], + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceCreatePayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.7" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-create-error.json b/doc/notification_samples/instance-create-error.json new file mode 100644 index 00000000000..221f43077ee --- /dev/null +++ b/doc/notification_samples/instance-create-error.json @@ -0,0 +1,88 @@ +{ + "event_type":"instance.create.error", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices":[], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault": { + "nova_object.data": { + "exception": "FlavorDiskTooSmall", + "exception_message": "The created instance's disk would be too small.", + "function_name": "_build_resources", + "module_name": "nova.tests.functional.notification_sample_tests.test_instance" + }, + "nova_object.name": "ExceptionPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [], + "key_name": "my-key", + "keypairs": [{ + "nova_object.name": "KeypairPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "user_id": "fake", + "name": "my-key", + "fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated-by-Nova", + "type": "ssh" + } + }], + "kernel_id":"", + "launched_at":null, + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"building", + "task_state":null, + "power_state":"pending", + "tags":["tag"], + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceCreatePayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.7" + }, + "priority":"ERROR", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-create-start.json b/doc/notification_samples/instance-create-start.json new file mode 100644 index 00000000000..00d790f6adb --- /dev/null +++ b/doc/notification_samples/instance-create-start.json @@ -0,0 +1,78 @@ +{ + "event_type":"instance.create.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices":[], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":null, + "host_name":"some-server", + "ip_addresses": [], + "key_name": "my-key", + "keypairs": [{ + "nova_object.name": "KeypairPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "user_id": "fake", + "name": "my-key", + "fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated-by-Nova", + "type": "ssh" + } + }], + "kernel_id":"", + "launched_at":null, + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":null, + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"building", + "task_state":null, + "power_state":"pending", + "tags":["tag"], + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": null, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceCreatePayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.7" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-delete-end.json b/doc/notification_samples/instance-delete-end.json new file mode 100644 index 00000000000..681b6eb82ba --- /dev/null +++ b/doc/notification_samples/instance-delete-end.json @@ -0,0 +1,65 @@ +{ + "event_type":"instance.delete.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices":[], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":"2012-10-29T13:42:11Z", + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses":[], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"deleted", + "task_state":null, + "power_state":"pending", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":"2012-10-29T13:42:11Z", + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-delete-start.json b/doc/notification_samples/instance-delete-start.json new file mode 100644 index 00000000000..3b78b9c6cf7 --- /dev/null +++ b/doc/notification_samples/instance-delete-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.delete.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"deleting", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-live_migration_rollback-end.json b/doc/notification_samples/instance-live_migration_rollback-end.json new file mode 100644 index 00000000000..622027d13bc --- /dev/null +++ b/doc/notification_samples/instance-live_migration_rollback-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.live_migration_rollback.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state": null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-live_migration_rollback-start.json b/doc/notification_samples/instance-live_migration_rollback-start.json new file mode 100644 index 00000000000..61298610105 --- /dev/null +++ b/doc/notification_samples/instance-live_migration_rollback-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.live_migration_rollback.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state": null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-pause-end.json b/doc/notification_samples/instance-pause-end.json new file mode 100644 index 00000000000..1eb294c76bd --- /dev/null +++ b/doc/notification_samples/instance-pause-end.json @@ -0,0 +1,90 @@ +{ + "event_type":"instance.pause.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"paused", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} + diff --git a/doc/notification_samples/instance-pause-start.json b/doc/notification_samples/instance-pause-start.json new file mode 100644 index 00000000000..c9393a2bec4 --- /dev/null +++ b/doc/notification_samples/instance-pause-start.json @@ -0,0 +1,90 @@ +{ + "event_type":"instance.pause.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"pausing", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} + diff --git a/doc/notification_samples/instance-power_off-end.json b/doc/notification_samples/instance-power_off-end.json new file mode 100644 index 00000000000..925103903d7 --- /dev/null +++ b/doc/notification_samples/instance-power_off-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.power_off.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"stopped", + "task_state":null, + "power_state":"shutdown", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-power_off-start.json b/doc/notification_samples/instance-power_off-start.json new file mode 100644 index 00000000000..8aacfd95ed8 --- /dev/null +++ b/doc/notification_samples/instance-power_off-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.power_off.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"powering-off", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-power_on-end.json b/doc/notification_samples/instance-power_on-end.json new file mode 100644 index 00000000000..c93c2317ad2 --- /dev/null +++ b/doc/notification_samples/instance-power_on-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.power_on.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-power_on-start.json b/doc/notification_samples/instance-power_on-start.json new file mode 100644 index 00000000000..d1117408352 --- /dev/null +++ b/doc/notification_samples/instance-power_on-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.power_on.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"stopped", + "task_state":"powering-on", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-reboot-end.json b/doc/notification_samples/instance-reboot-end.json new file mode 100644 index 00000000000..e5e65b50c87 --- /dev/null +++ b/doc/notification_samples/instance-reboot-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.reboot.end", + "payload":{ + "nova_object.data":{ + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "auto_disk_config":"MANUAL", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-reboot-error.json b/doc/notification_samples/instance-reboot-error.json new file mode 100644 index 00000000000..4032f4791e7 --- /dev/null +++ b/doc/notification_samples/instance-reboot-error.json @@ -0,0 +1,99 @@ +{ + "event_type":"instance.reboot.error", + "payload":{ + "nova_object.data":{ + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault": { + "nova_object.data": { + "exception": "UnsupportedVirtType", + "exception_message": "Virtualization type 'FakeVirt' is not supported by this compute driver", + "function_name": "_hard_reboot", + "module_name": "nova.tests.functional.notification_sample_tests.test_instance" + }, + "nova_object.name": "ExceptionPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "auto_disk_config":"MANUAL", + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"reboot_started_hard", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"ERROR", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-reboot-start.json b/doc/notification_samples/instance-reboot-start.json new file mode 100644 index 00000000000..d99c4fdc717 --- /dev/null +++ b/doc/notification_samples/instance-reboot-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.reboot.start", + "payload":{ + "nova_object.data":{ + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "auto_disk_config":"MANUAL", + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"reboot_pending_hard", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-rebuild-end.json b/doc/notification_samples/instance-rebuild-end.json new file mode 100644 index 00000000000..9b12768421a --- /dev/null +++ b/doc/notification_samples/instance-rebuild-end.json @@ -0,0 +1,89 @@ +{ + "event_type": "instance.rebuild.end", + "publisher_id": "nova-compute:compute", + "payload": { + "nova_object.namespace": "nova", + "nova_object.version": "1.5", + "nova_object.data": { + "node": "fake-mini", + "fault": null, + "host_name": "some-server", + "power_state": "running", + "image_uuid": "a2459075-d96c-40d5-893e-577ff92e721c", + "display_name": "some-server", + "display_description": "some-server", + "auto_disk_config":"MANUAL", + "tenant_id": "6f70656e737461636b20342065766572", + "key_name": "my-key", + "kernel_id": "", + "created_at": "2012-10-29T13:42:11Z", + "host": "compute", + "reservation_id": "r-wczvhcla", + "flavor": { + "nova_object.namespace": "nova", + "nova_object.version": "1.3", + "nova_object.data": { + "disabled": false, + "ephemeral_gb": 0, + "extra_specs": {"hw:watchdog_action": "disabled"}, + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "is_public": true, + "memory_mb": 512, + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "rxtx_factor": 1.0, + "swap": 0, + "vcpu_weight": 0, + "vcpus": 1 + }, + "nova_object.name": "FlavorPayload" + }, + "user_id": "fake", + "terminated_at": null, + "ip_addresses": [ + { + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "label": "private-network", + "address": "192.168.1.3", + "meta": {}, + "mac": "fa:16:3e:4c:2c:30", + "device_name": "tapce531f90-19" + }, + "nova_object.name": "IpPayload" + } + ], + "state": "active", + "launched_at": "2012-10-29T13:42:11Z", + "metadata": {}, + "deleted_at": null, + "os_type": null, + "uuid": "b271fcb9-75c3-4c76-84eb-6ccad1150ece", + "locked": false, + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "ramdisk_id": "", + "architecture": null, + "progress": 0, + "task_state": null, + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name": "InstanceActionPayload" + }, + "priority": "INFO" +} diff --git a/doc/notification_samples/instance-rebuild-error.json b/doc/notification_samples/instance-rebuild-error.json new file mode 100644 index 00000000000..c840b05da96 --- /dev/null +++ b/doc/notification_samples/instance-rebuild-error.json @@ -0,0 +1,99 @@ +{ + "priority": "ERROR", + "payload": { + "nova_object.name": "InstanceActionPayload", + "nova_object.data": { + "state": "active", + "availability_zone": "nova", + "key_name": "my-key", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "kernel_id": "", + "host_name": "some-server", + "progress": 0, + "task_state": "rebuilding", + "deleted_at": null, + "architecture": null, + "auto_disk_config":"MANUAL", + "ramdisk_id": "", + "locked": false, + "created_at": "2012-10-29T13:42:11Z", + "host": "compute", + "display_name": "some-server", + "os_type": null, + "metadata": {}, + "ip_addresses": [ + { + "nova_object.name": "IpPayload", + "nova_object.data": { + "device_name": "tapce531f90-19", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "address": "192.168.1.3", + "version": 4, + "meta": {}, + "label": "private-network", + "mac": "fa:16:3e:4c:2c:30" + }, + "nova_object.version": "1.0", + "nova_object.namespace": "nova" + } + ], + "power_state": "running", + "display_description": "some-server", + "uuid": "5fafd989-4043-44b4-8acc-907e847f4b70", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "disabled": false, + "ephemeral_gb": 0, + "extra_specs": {"hw:watchdog_action": "disabled"}, + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "is_public": true, + "memory_mb": 512, + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "rxtx_factor": 1.0, + "swap": 0, + "vcpu_weight": 0, + "vcpus": 1 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "reservation_id": "r-pfiic52h", + "terminated_at": null, + "tenant_id": "6f70656e737461636b20342065766572", + "node": "fake-mini", + "launched_at": "2012-10-29T13:42:11Z", + "user_id": "fake", + "image_uuid": "a2459075-d96c-40d5-893e-577ff92e721c", + "fault": { + "nova_object.name": "ExceptionPayload", + "nova_object.data": { + "module_name": "nova.tests.functional.notification_sample_tests.test_instance", + "exception_message": "Virtual Interface creation failed", + "function_name": "_virtual_interface_create_failed", + "exception": "VirtualInterfaceCreateException" + }, + "nova_object.version": "1.0", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.version": "1.5", + "nova_object.namespace": "nova" + }, + "publisher_id": "nova-compute:compute", + "event_type": "instance.rebuild.error" +} diff --git a/doc/notification_samples/instance-rebuild-start.json b/doc/notification_samples/instance-rebuild-start.json new file mode 100644 index 00000000000..d8a2543aa7b --- /dev/null +++ b/doc/notification_samples/instance-rebuild-start.json @@ -0,0 +1,89 @@ +{ + "priority": "INFO", + "event_type": "instance.rebuild.start", + "publisher_id": "nova-compute:compute", + "payload": { + "nova_object.name": "InstanceActionPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.5", + "nova_object.data": { + "display_description": "some-server", + "reservation_id": "r-rqe0mlje", + "progress": 0, + "user_id": "fake", + "task_state": "rebuilding", + "auto_disk_config":"MANUAL", + "node": "fake-mini", + "created_at": "2012-10-29T13:42:11Z", + "key_name": "my-key", + "kernel_id": "", + "metadata": {}, + "deleted_at": null, + "host_name": "some-server", + "uuid": "ff0ee97f-3f14-4259-a79c-83949f8493bd", + "os_type": null, + "display_name": "some-server", + "ramdisk_id": "", + "power_state": "running", + "locked": false, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3", + "nova_object.data": { + "disabled": false, + "ephemeral_gb": 0, + "extra_specs": {"hw:watchdog_action": "disabled"}, + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "is_public": true, + "memory_mb": 512, + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "rxtx_factor": 1.0, + "swap": 0, + "vcpu_weight": 0, + "vcpus": 1 + } + }, + "terminated_at": null, + "architecture": null, + "launched_at": "2012-10-29T13:42:11Z", + "ip_addresses": [ + { + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "meta": {}, + "mac": "fa:16:3e:4c:2c:30", + "label": "private-network", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "device_name": "tapce531f90-19", + "address": "192.168.1.3" + } + } + ], + "tenant_id": "6f70656e737461636b20342065766572", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "host": "compute", + "image_uuid": "a2459075-d96c-40d5-893e-577ff92e721c", + "state": "active", + "fault": null, + "updated_at": "2012-10-29T13:42:11Z" + } + } +} diff --git a/doc/notification_samples/instance-resize-end.json b/doc/notification_samples/instance-resize-end.json new file mode 100644 index 00000000000..7f647a9bd57 --- /dev/null +++ b/doc/notification_samples/instance-resize-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.resize.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"resize_migrated", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-resize-start.json b/doc/notification_samples/instance-resize-start.json new file mode 100644 index 00000000000..81e96f96565 --- /dev/null +++ b/doc/notification_samples/instance-resize-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.resize.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"resize_migrating", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-resize_finish-end.json b/doc/notification_samples/instance-resize_finish-end.json new file mode 100644 index 00000000000..ce653793ce7 --- /dev/null +++ b/doc/notification_samples/instance-resize_finish-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.resize_finish.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"resized", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "d5a8bb54-365a-45ae-abdb-38d249df7845", + "name": "other_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 256, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "reset" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-resize_finish-start.json b/doc/notification_samples/instance-resize_finish-start.json new file mode 100644 index 00000000000..153899dcb59 --- /dev/null +++ b/doc/notification_samples/instance-resize_finish-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.resize_finish.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"resize_finish", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "d5a8bb54-365a-45ae-abdb-38d249df7845", + "name": "other_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 256, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "reset" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-restore-end.json b/doc/notification_samples/instance-restore-end.json new file mode 100644 index 00000000000..22f23275bb9 --- /dev/null +++ b/doc/notification_samples/instance-restore-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.restore.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-restore-start.json b/doc/notification_samples/instance-restore-start.json new file mode 100644 index 00000000000..df9a0fe8268 --- /dev/null +++ b/doc/notification_samples/instance-restore-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.restore.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"soft-delete", + "task_state":"restoring", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-resume-end.json b/doc/notification_samples/instance-resume-end.json new file mode 100644 index 00000000000..722fa701752 --- /dev/null +++ b/doc/notification_samples/instance-resume-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.resume.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-resume-start.json b/doc/notification_samples/instance-resume-start.json new file mode 100644 index 00000000000..1eab156d2dd --- /dev/null +++ b/doc/notification_samples/instance-resume-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.resume.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"suspended", + "task_state":"resuming", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-shelve-end.json b/doc/notification_samples/instance-shelve-end.json new file mode 100644 index 00000000000..d4fa1637e21 --- /dev/null +++ b/doc/notification_samples/instance-shelve-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.shelve.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"shelved", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-shelve-start.json b/doc/notification_samples/instance-shelve-start.json new file mode 100644 index 00000000000..991de5a364f --- /dev/null +++ b/doc/notification_samples/instance-shelve-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.shelve.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"shelving", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-shelve_offload-end.json b/doc/notification_samples/instance-shelve_offload-end.json new file mode 100644 index 00000000000..b82bf474747 --- /dev/null +++ b/doc/notification_samples/instance-shelve_offload-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.shelve_offload.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":null, + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":null, + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"shelved_offloaded", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-shelve_offload-start.json b/doc/notification_samples/instance-shelve_offload-start.json new file mode 100644 index 00000000000..4a1bccc07a7 --- /dev/null +++ b/doc/notification_samples/instance-shelve_offload-start.json @@ -0,0 +1,90 @@ +{ + "event_type":"instance.shelve_offload.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"shelved", + "task_state":"shelving_offloading", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} + diff --git a/doc/notification_samples/instance-shutdown-end.json b/doc/notification_samples/instance-shutdown-end.json new file mode 100644 index 00000000000..e05da1b31a7 --- /dev/null +++ b/doc/notification_samples/instance-shutdown-end.json @@ -0,0 +1,76 @@ +{ + "event_type":"instance.shutdown.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "key_name": "my-key", + "ip_addresses": [], + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"deleting", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-shutdown-start.json b/doc/notification_samples/instance-shutdown-start.json new file mode 100644 index 00000000000..781d912735c --- /dev/null +++ b/doc/notification_samples/instance-shutdown-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.shutdown.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"deleting", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-snapshot-end.json b/doc/notification_samples/instance-snapshot-end.json new file mode 100644 index 00000000000..2452086b63b --- /dev/null +++ b/doc/notification_samples/instance-snapshot-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.snapshot.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-snapshot-start.json b/doc/notification_samples/instance-snapshot-start.json new file mode 100644 index 00000000000..c8b833fb611 --- /dev/null +++ b/doc/notification_samples/instance-snapshot-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.snapshot.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"image_snapshot", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-soft_delete-end.json b/doc/notification_samples/instance-soft_delete-end.json new file mode 100644 index 00000000000..d628054dd51 --- /dev/null +++ b/doc/notification_samples/instance-soft_delete-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.soft_delete.end", + "payload":{ + "nova_object.data":{ + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":"2012-10-29T13:42:11Z", + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"soft-delete", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-soft_delete-start.json b/doc/notification_samples/instance-soft_delete-start.json new file mode 100644 index 00000000000..d7da97cf33f --- /dev/null +++ b/doc/notification_samples/instance-soft_delete-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.soft_delete.start", + "payload":{ + "nova_object.data":{ + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":"2012-10-29T13:42:11Z", + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state": "soft-deleting", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "updated_at": "2012-10-29T13:42:11Z", + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-suspend-end.json b/doc/notification_samples/instance-suspend-end.json new file mode 100644 index 00000000000..4d6bb3de8c1 --- /dev/null +++ b/doc/notification_samples/instance-suspend-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.suspend.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"suspended", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-suspend-start.json b/doc/notification_samples/instance-suspend-start.json new file mode 100644 index 00000000000..6d06d802d16 --- /dev/null +++ b/doc/notification_samples/instance-suspend-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.suspend.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":"suspending", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-unpause-end.json b/doc/notification_samples/instance-unpause-end.json new file mode 100644 index 00000000000..45ada48ec10 --- /dev/null +++ b/doc/notification_samples/instance-unpause-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.unpause.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-unpause-start.json b/doc/notification_samples/instance-unpause-start.json new file mode 100644 index 00000000000..d5d23f8af2f --- /dev/null +++ b/doc/notification_samples/instance-unpause-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.unpause.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"paused", + "task_state":"unpausing", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-unshelve-end.json b/doc/notification_samples/instance-unshelve-end.json new file mode 100644 index 00000000000..eb9cec4fac9 --- /dev/null +++ b/doc/notification_samples/instance-unshelve-end.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.unshelve.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state":null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-unshelve-start.json b/doc/notification_samples/instance-unshelve-start.json new file mode 100644 index 00000000000..3ef368d922b --- /dev/null +++ b/doc/notification_samples/instance-unshelve-start.json @@ -0,0 +1,89 @@ +{ + "event_type":"instance.unshelve.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":null, + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "key_name": "my-key", + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":null, + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"shelved_offloaded", + "task_state":"unshelving", + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.5" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-update-tags-action.json b/doc/notification_samples/instance-update-tags-action.json new file mode 100644 index 00000000000..b9b0d849527 --- /dev/null +++ b/doc/notification_samples/instance-update-tags-action.json @@ -0,0 +1,94 @@ +{ + "event_type": "instance.update", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "audit_period": { + "nova_object.data": { + "audit_period_beginning": "2012-10-01T00:00:00Z", + "audit_period_ending": "2012-10-29T13:42:11Z"}, + "nova_object.name": "AuditPeriodPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0"}, + "auto_disk_config": "MANUAL", + "availability_zone": "nova", + "block_devices": [], + "bandwidth": [], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_description": "some-server", + "display_name": "some-server", + "flavor": { + "nova_object.data": { + "disabled": false, + "ephemeral_gb": 0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "is_public": true, + "memory_mb": 512, + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "rxtx_factor": 1.0, + "swap": 0, + "vcpu_weight": 0, + "vcpus": 1}, + "nova_object.name": "FlavorPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3"}, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [ + { + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "mac": "fa:16:3e:4c:2c:30", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4}, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + } + ], + "kernel_id": "", + "key_name": "my-key", + "launched_at": "2012-10-29T13:42:11Z", + "locked": false, + "metadata": {}, + "node": "fake-mini", + "old_display_name": null, + "os_type": null, + "power_state": "running", + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-53ad660s", + "state": "active", + "state_update": { + "nova_object.data": { + "new_task_state": null, + "old_state": "active", + "old_task_state": null, + "state": "active"}, + "nova_object.name": "InstanceStateUpdatePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0"}, + "tags": ["tag1"], + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "user_id": "fake", + "uuid": "d5ca7280-1f12-4238-a86f-378bb5d93c38", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name": "InstanceUpdatePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.6"}, + "priority": "INFO", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/instance-update.json b/doc/notification_samples/instance-update.json new file mode 100644 index 00000000000..5bf7fa20b7b --- /dev/null +++ b/doc/notification_samples/instance-update.json @@ -0,0 +1,83 @@ +{ + "event_type": "instance.update", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "audit_period": { + "nova_object.data": { + "audit_period_beginning": "2012-10-01T00:00:00Z", + "audit_period_ending": "2012-10-29T13:42:11Z"}, + "nova_object.name": "AuditPeriodPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }, + "availability_zone": "nova", + "block_devices": [], + "bandwidth": [], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "display_description":"some-server", + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "key_name": "my-key", + "kernel_id": "", + "launched_at": null, + "metadata": {}, + "locked":false, + "node": "fake-mini", + "old_display_name": null, + "os_type": null, + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-sd3ygfjj", + "state": "building", + "task_state": "scheduling", + "power_state": "pending", + "ip_addresses": [], + "state_update": { + "nova_object.data": { + "new_task_state": null, + "old_state": "building", + "old_task_state": null, + "state": "building"}, + "nova_object.name": "InstanceStateUpdatePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0"}, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "tags": [], + "user_id": "fake", + "uuid": "c03c0bf9-f46e-4e4f-93f1-817568567ee2", + "updated_at": null + }, + "nova_object.name": "InstanceUpdatePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.6"}, + "priority": "INFO", + "publisher_id": "nova-compute:fake-mini" +} diff --git a/doc/notification_samples/instance-volume_attach-end.json b/doc/notification_samples/instance-volume_attach-end.json new file mode 100644 index 00000000000..3e82848bf00 --- /dev/null +++ b/doc/notification_samples/instance-volume_attach-end.json @@ -0,0 +1,90 @@ +{ + "event_type": "instance.volume_attach.end", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "display_description":"some-server", + "fault": null, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [{ + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "mac": "fa:16:3e:4c:2c:30" + }, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "key_name": "my-key", + "kernel_id": "", + "launched_at": "2012-10-29T13:42:11Z", + "metadata": {}, + "locked":false, + "node": "fake-mini", + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113", + "os_type": null, + "power_state":"running", + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-6w6ruqaz", + "state": "active", + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "auto_disk_config": "MANUAL", + "user_id": "fake", + "uuid": "0ab886d0-7443-4107-9265-48371bfa662b", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name": "InstanceActionVolumePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3" + }, + "priority": "INFO", + "publisher_id": "nova-compute:compute" +} diff --git a/doc/notification_samples/instance-volume_attach-error.json b/doc/notification_samples/instance-volume_attach-error.json new file mode 100644 index 00000000000..e0523a81276 --- /dev/null +++ b/doc/notification_samples/instance-volume_attach-error.json @@ -0,0 +1,100 @@ +{ + "event_type": "instance.volume_attach.error", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "display_description":"some-server", + "fault": { + "nova_object.data": { + "exception": "CinderConnectionFailed", + "exception_message": "Connection to cinder host failed: Connection timed out", + "function_name": "attach_volume", + "module_name": "nova.tests.functional.notification_sample_tests.test_instance" + }, + "nova_object.name": "ExceptionPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [{ + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "mac": "fa:16:3e:4c:2c:30" + }, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "key_name": "my-key", + "kernel_id": "", + "launched_at": "2012-10-29T13:42:11Z", + "metadata": {}, + "locked":false, + "node": "fake-mini", + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113", + "os_type": null, + "power_state":"running", + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-6w6ruqaz", + "state": "active", + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "auto_disk_config": "MANUAL", + "user_id": "fake", + "uuid": "0ab886d0-7443-4107-9265-48371bfa662b", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name": "InstanceActionVolumePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3" + }, + "priority": "ERROR", + "publisher_id": "nova-compute:compute" +} diff --git a/doc/notification_samples/instance-volume_attach-start.json b/doc/notification_samples/instance-volume_attach-start.json new file mode 100644 index 00000000000..1b397b38273 --- /dev/null +++ b/doc/notification_samples/instance-volume_attach-start.json @@ -0,0 +1,90 @@ +{ + "event_type": "instance.volume_attach.start", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "display_description":"some-server", + "fault": null, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [{ + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "mac": "fa:16:3e:4c:2c:30" + }, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "key_name": "my-key", + "kernel_id": "", + "launched_at": "2012-10-29T13:42:11Z", + "metadata": {}, + "locked":false, + "node": "fake-mini", + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113", + "os_type": null, + "power_state":"running", + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-6w6ruqaz", + "state": "active", + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "auto_disk_config": "MANUAL", + "user_id": "fake", + "uuid": "0ab886d0-7443-4107-9265-48371bfa662b", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name": "InstanceActionVolumePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3" + }, + "priority": "INFO", + "publisher_id": "nova-compute:compute" +} diff --git a/doc/notification_samples/instance-volume_detach-end.json b/doc/notification_samples/instance-volume_detach-end.json new file mode 100644 index 00000000000..e92d852da10 --- /dev/null +++ b/doc/notification_samples/instance-volume_detach-end.json @@ -0,0 +1,90 @@ +{ + "event_type": "instance.volume_detach.end", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "display_description":"some-server", + "fault": null, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [{ + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "mac": "fa:16:3e:4c:2c:30" + }, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "key_name": "my-key", + "kernel_id": "", + "launched_at": "2012-10-29T13:42:11Z", + "metadata": {}, + "locked":false, + "node": "fake-mini", + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113", + "os_type": null, + "power_state":"running", + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-6w6ruqaz", + "state": "active", + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "auto_disk_config": "MANUAL", + "user_id": "fake", + "uuid": "0ab886d0-7443-4107-9265-48371bfa662b", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name": "InstanceActionVolumePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3" + }, + "priority": "INFO", + "publisher_id": "nova-compute:compute" +} diff --git a/doc/notification_samples/instance-volume_detach-start.json b/doc/notification_samples/instance-volume_detach-start.json new file mode 100644 index 00000000000..d9c02e524cc --- /dev/null +++ b/doc/notification_samples/instance-volume_detach-start.json @@ -0,0 +1,90 @@ +{ + "event_type": "instance.volume_detach.start", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "display_description":"some-server", + "fault": null, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [{ + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "mac": "fa:16:3e:4c:2c:30" + }, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "key_name": "my-key", + "kernel_id": "", + "launched_at": "2012-10-29T13:42:11Z", + "metadata": {}, + "locked":false, + "node": "fake-mini", + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113", + "os_type": null, + "power_state":"running", + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-6w6ruqaz", + "state": "active", + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "auto_disk_config": "MANUAL", + "user_id": "fake", + "uuid": "0ab886d0-7443-4107-9265-48371bfa662b", + "updated_at": "2012-10-29T13:42:11Z" + }, + "nova_object.name": "InstanceActionVolumePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3" + }, + "priority": "INFO", + "publisher_id": "nova-compute:compute" +} diff --git a/doc/notification_samples/instance-volume_swap-end.json b/doc/notification_samples/instance-volume_swap-end.json new file mode 100644 index 00000000000..54c36e51da9 --- /dev/null +++ b/doc/notification_samples/instance-volume_swap-end.json @@ -0,0 +1,91 @@ +{ + "event_type": "instance.volume_swap.end", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "227cc671-f30b-4488-96fd-7d0bf13648d8" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "display_description":"some-server", + "fault": null, + "flavor": { + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.name": "FlavorPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3" + }, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [{ + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "mac": "fa:16:3e:4c:2c:30" + }, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "key_name": "my-key", + "kernel_id": "", + "launched_at": "2012-10-29T13:42:11Z", + "metadata": {}, + "locked":false, + "new_volume_id": "227cc671-f30b-4488-96fd-7d0bf13648d8", + "node": "fake-mini", + "old_volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113", + "os_type": null, + "power_state":"running", + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-6w6ruqaz", + "state": "active", + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "auto_disk_config":"MANUAL", + "updated_at": "2012-10-29T13:42:11Z", + "user_id": "fake", + "uuid": "0ab886d0-7443-4107-9265-48371bfa662b" + }, + "nova_object.name": "InstanceActionVolumeSwapPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.5" + }, + "priority": "INFO", + "publisher_id": "nova-compute:compute" +} diff --git a/doc/notification_samples/instance-volume_swap-error.json b/doc/notification_samples/instance-volume_swap-error.json new file mode 100644 index 00000000000..3d7e81b5a9f --- /dev/null +++ b/doc/notification_samples/instance-volume_swap-error.json @@ -0,0 +1,101 @@ +{ + "event_type": "instance.volume_swap.error", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "display_description":"some-server", + "fault": { + "nova_object.data": { + "exception": "TypeError", + "exception_message": "'tuple' object does not support item assignment", + "function_name": "_init_volume_connection", + "module_name": "nova.compute.manager" + }, + "nova_object.name": "ExceptionPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }, + "flavor": { + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.name": "FlavorPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3" + }, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [{ + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "mac": "fa:16:3e:4c:2c:30" + }, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "key_name": "my-key", + "kernel_id": "", + "launched_at": "2012-10-29T13:42:11Z", + "metadata": {}, + "locked":false, + "new_volume_id": "9c6d9c2d-7a8f-4c80-938d-3bf062b8d489", + "node": "fake-mini", + "old_volume_id": "828419fa-3efb-4533-b458-4267ca5fe9b1", + "os_type": null, + "power_state":"running", + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-6w6ruqaz", + "state": "active", + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "auto_disk_config":"MANUAL", + "updated_at": "2012-10-29T13:42:11Z", + "user_id": "fake", + "uuid": "0ab886d0-7443-4107-9265-48371bfa662b" + }, + "nova_object.name": "InstanceActionVolumeSwapPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.5" + }, + "priority": "ERROR", + "publisher_id": "nova-compute:compute" +} diff --git a/doc/notification_samples/instance-volume_swap-start.json b/doc/notification_samples/instance-volume_swap-start.json new file mode 100644 index 00000000000..493a1d4b495 --- /dev/null +++ b/doc/notification_samples/instance-volume_swap-start.json @@ -0,0 +1,91 @@ +{ + "event_type": "instance.volume_swap.start", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "availability_zone": "nova", + "block_devices": [{ + "nova_object.data": { + "boot_index": null, + "delete_on_termination": false, + "device_name": "/dev/sdb", + "tag": null, + "volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113" + }, + "nova_object.name": "BlockDevicePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "display_description":"some-server", + "fault": null, + "flavor": { + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.name": "FlavorPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.3" + }, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [{ + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4, + "mac": "fa:16:3e:4c:2c:30" + }, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }], + "key_name": "my-key", + "kernel_id": "", + "launched_at": "2012-10-29T13:42:11Z", + "metadata": {}, + "locked":false, + "new_volume_id": "227cc671-f30b-4488-96fd-7d0bf13648d8", + "node": "fake-mini", + "old_volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113", + "os_type": null, + "power_state":"running", + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-6w6ruqaz", + "state": "active", + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "auto_disk_config":"MANUAL", + "updated_at": "2012-10-29T13:42:11Z", + "user_id": "fake", + "uuid": "0ab886d0-7443-4107-9265-48371bfa662b" + }, + "nova_object.name": "InstanceActionVolumeSwapPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.5" + }, + "priority": "INFO", + "publisher_id": "nova-compute:compute" +} diff --git a/doc/notification_samples/keypair-create-end.json b/doc/notification_samples/keypair-create-end.json new file mode 100644 index 00000000000..52ac6cb9fb4 --- /dev/null +++ b/doc/notification_samples/keypair-create-end.json @@ -0,0 +1,17 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.0", + "nova_object.namespace": "nova", + "nova_object.name": "KeypairPayload", + "nova_object.data": { + "user_id": "fake", + "name": "my-key", + "fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated-by-Nova", + "type": "ssh" + } + }, + "event_type": "keypair.create.end", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/keypair-create-start.json b/doc/notification_samples/keypair-create-start.json new file mode 100644 index 00000000000..ccf4d9a9098 --- /dev/null +++ b/doc/notification_samples/keypair-create-start.json @@ -0,0 +1,17 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.0", + "nova_object.namespace": "nova", + "nova_object.name": "KeypairPayload", + "nova_object.data": { + "user_id": "fake", + "name": "my-key", + "fingerprint": null, + "public_key": null, + "type": "ssh" + } + }, + "event_type": "keypair.create.start", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/service-update.json b/doc/notification_samples/service-update.json index cab89fb776f..9e7220e73a6 100644 --- a/doc/notification_samples/service-update.json +++ b/doc/notification_samples/service-update.json @@ -3,7 +3,7 @@ "payload": { "nova_object.namespace": "nova", "nova_object.name": "ServiceStatusPayload", - "nova_object.version": "1.0", + "nova_object.version": "1.1", "nova_object.data": { "host": "host1", "disabled": false, @@ -13,7 +13,9 @@ "disabled_reason": null, "report_count": 1, "forced_down": false, - "version": 9 + "version": 22, + "availability_zone": null, + "uuid": "fa69c544-906b-4a6a-a9c6-c1f7a8078c73" } }, "event_type": "service.update", diff --git a/doc/source/_extra/.htaccess b/doc/source/_extra/.htaccess new file mode 100644 index 00000000000..625e9f8df0f --- /dev/null +++ b/doc/source/_extra/.htaccess @@ -0,0 +1,77 @@ +# The following is generated with: +# +# git log --follow --name-status --format='%H' 2d0dfc632f.. -- doc/source | \ +# grep ^R | grep .rst | cut -f2- | \ +# sed -e 's|doc/source/|redirectmatch 301 ^/nova/([^/]+)/|' -e 's|doc/source/|/nova/$1/|' -e 's/.rst/.html$/' -e 's/.rst/.html/' | \ +# sort + +redirectmatch 301 ^/nova/([^/]+)/addmethod.openstackapi.html$ /nova/$1/contributor/api-2.html +redirectmatch 301 ^/nova/([^/]+)/aggregates.html$ /nova/$1/user/aggregates.html +redirectmatch 301 ^/nova/([^/]+)/api_microversion_dev.html$ /nova/$1/contributor/microversions.html +redirectmatch 301 ^/nova/([^/]+)/api_microversion_history.html$ /nova/$1/reference/api-microversion-history.html +redirectmatch 301 ^/nova/([^/]+)/api_plugins.html$ /nova/$1/contributor/api.html +redirectmatch 301 ^/nova/([^/]+)/architecture.html$ /nova/$1/user/architecture.html +redirectmatch 301 ^/nova/([^/]+)/block_device_mapping.html$ /nova/$1/user/block-device-mapping.html +redirectmatch 301 ^/nova/([^/]+)/blueprints.html$ /nova/$1/contributor/blueprints.html +redirectmatch 301 ^/nova/([^/]+)/cells.html$ /nova/$1/user/cells.html +redirectmatch 301 ^/nova/([^/]+)/code-review.html$ /nova/$1/contributor/code-review.html +redirectmatch 301 ^/nova/([^/]+)/conductor.html$ /nova/$1/user/conductor.html +redirectmatch 301 ^/nova/([^/]+)/development.environment.html$ /nova/$1/contributor/development-environment.html +redirectmatch 301 ^/nova/([^/]+)/devref/api.html /nova/$1/contributor/api.html +redirectmatch 301 ^/nova/([^/]+)/devref/cells.html /nova/$1/user/cells.html +redirectmatch 301 ^/nova/([^/]+)/devref/filter_scheduler.html /nova/$1/user/filter-scheduler.html +# catch all, if we hit something in devref assume it moved to +# reference unless we have already triggered a hit above. +redirectmatch 301 ^/nova/([^/]+)/devref/([^/]+).html /nova/$1/reference/$2.html +redirectmatch 301 ^/nova/([^/]+)/feature_classification.html$ /nova/$1/user/feature-classification.html +redirectmatch 301 ^/nova/([^/]+)/filter_scheduler.html$ /nova/$1/user/filter-scheduler.html +redirectmatch 301 ^/nova/([^/]+)/gmr.html$ /nova/$1/reference/gmr.html +redirectmatch 301 ^/nova/([^/]+)/how_to_get_involved.html$ /nova/$1/contributor/how-to-get-involved.html +redirectmatch 301 ^/nova/([^/]+)/i18n.html$ /nova/$1/reference/i18n.html +redirectmatch 301 ^/nova/([^/]+)/man/index.html$ /nova/$1/cli/index.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-api-metadata.html$ /nova/$1/cli/nova-api-metadata.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-api-os-compute.html$ /nova/$1/cli/nova-api-os-compute.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-api.html$ /nova/$1/cli/nova-api.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-cells.html$ /nova/$1/cli/nova-cells.html +# this is gone and never coming back, indicate that to the end users +redirectmatch 410 ^/nova/([^/]+)/man/nova-cert.html$ +redirectmatch 301 ^/nova/([^/]+)/man/nova-compute.html$ /nova/$1/cli/nova-compute.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-conductor.html$ /nova/$1/cli/nova-conductor.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-console.html$ /nova/$1/cli/nova-console.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-consoleauth.html$ /nova/$1/cli/nova-consoleauth.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-dhcpbridge.html$ /nova/$1/cli/nova-dhcpbridge.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-idmapshift.html$ /nova/$1/cli/nova-idmapshift.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-manage.html$ /nova/$1/cli/nova-manage.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-network.html$ /nova/$1/cli/nova-network.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-novncproxy.html$ /nova/$1/cli/nova-novncproxy.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-rootwrap.html$ /nova/$1/cli/nova-rootwrap.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-scheduler.html$ /nova/$1/cli/nova-scheduler.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-serialproxy.html$ /nova/$1/cli/nova-serialproxy.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-spicehtml5proxy.html$ /nova/$1/cli/nova-spicehtml5proxy.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-status.html$ /nova/$1/cli/nova-status.html +redirectmatch 301 ^/nova/([^/]+)/man/nova-xvpvncproxy.html$ /nova/$1/cli/nova-xvpvncproxy.html +redirectmatch 301 ^/nova/([^/]+)/notifications.html$ /nova/$1/reference/notifications.html +redirectmatch 301 ^/nova/([^/]+)/placement.html$ /nova/$1/user/placement.html +redirectmatch 301 ^/nova/([^/]+)/placement_dev.html$ /nova/$1/contributor/placement.html +redirectmatch 301 ^/nova/([^/]+)/policies.html$ /nova/$1/contributor/policies.html +redirectmatch 301 ^/nova/([^/]+)/policy_enforcement.html$ /nova/$1/reference/policy-enforcement.html +redirectmatch 301 ^/nova/([^/]+)/process.html$ /nova/$1/contributor/process.html +redirectmatch 301 ^/nova/([^/]+)/project_scope.html$ /nova/$1/contributor/project-scope.html +redirectmatch 301 ^/nova/([^/]+)/quotas.html$ /nova/$1/user/quotas.html +redirectmatch 301 ^/nova/([^/]+)/releasenotes.html$ /nova/$1/contributor/releasenotes.html +redirectmatch 301 ^/nova/([^/]+)/rpc.html$ /nova/$1/reference/rpc.html +redirectmatch 301 ^/nova/([^/]+)/sample_config.html$ /nova/$1/configuration/sample-config.html +redirectmatch 301 ^/nova/([^/]+)/sample_policy.html$ /nova/$1/configuration/sample-policy.html +redirectmatch 301 ^/nova/([^/]+)/scheduler_evolution.html$ /nova/$1/reference/scheduler-evolution.html +redirectmatch 301 ^/nova/([^/]+)/services.html$ /nova/$1/reference/services.html +redirectmatch 301 ^/nova/([^/]+)/stable_api.html$ /nova/$1/reference/stable-api.html +redirectmatch 301 ^/nova/([^/]+)/support-matrix.html$ /nova/$1/user/support-matrix.html +redirectmatch 301 ^/nova/([^/]+)/test_strategy.html$ /nova/$1/contributor/testing.html +redirectmatch 301 ^/nova/([^/]+)/testing/libvirt-numa.html$ /nova/$1/contributor/testing/libvirt-numa.html +redirectmatch 301 ^/nova/([^/]+)/testing/serial-console.html$ /nova/$1/contributor/testing/serial-console.html +redirectmatch 301 ^/nova/([^/]+)/testing/zero-downtime-upgrade.html$ /nova/$1/contributor/testing/zero-downtime-upgrade.html +redirectmatch 301 ^/nova/([^/]+)/threading.html$ /nova/$1/reference/threading.html +redirectmatch 301 ^/nova/([^/]+)/upgrade.html$ /nova/$1/user/upgrade.html +redirectmatch 301 ^/nova/([^/]+)/vendordata.html$ /nova/$1/user/vendordata.html +redirectmatch 301 ^/nova/([^/]+)/vmstates.html$ /nova/$1/reference/vm-states.html +redirectmatch 301 ^/nova/([^/]+)/wsgi.html$ /nova/$1/user/wsgi.html diff --git a/doc/source/_ga/layout.html b/doc/source/_ga/layout.html deleted file mode 100644 index add67fa7cda..00000000000 --- a/doc/source/_ga/layout.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "!layout.html" %} - -{% block footer %} -{{ super() }} - - -{% endblock %} - diff --git a/doc/source/_static/feature-matrix.css b/doc/source/_static/feature-matrix.css new file mode 100644 index 00000000000..3b0cf5e82af --- /dev/null +++ b/doc/source/_static/feature-matrix.css @@ -0,0 +1,32 @@ +.fm_maturity_complete, +.fm_impl_complete { + color: rgb(0, 120, 0); + font-weight: normal; +} + +.fm_maturity_deprecated, +.fm_impl_missing { + color: rgb(120, 0, 0); + font-weight: normal; +} + +.fm_maturity_experimental, +.fm_impl_partial { + color: rgb(170, 170, 0); + font-weight: normal; +} + +.fm_maturity_incomplete, +.fm_impl_unknown { + color: rgb(170, 170, 170); + font-weight: normal; +} + +.fm_impl_summary { + font-size: 2em; +} + +.fm_cli { + font-family: monospace; + background-color: #F5F5F5; +} diff --git a/doc/source/image_src/architecture.dia b/doc/source/_static/images/architecture.dia similarity index 100% rename from doc/source/image_src/architecture.dia rename to doc/source/_static/images/architecture.dia diff --git a/doc/source/images/architecture.svg b/doc/source/_static/images/architecture.svg similarity index 100% rename from doc/source/images/architecture.svg rename to doc/source/_static/images/architecture.svg diff --git a/doc/source/image_src/create_vm_states.diag b/doc/source/_static/images/create-vm-states.dia similarity index 100% rename from doc/source/image_src/create_vm_states.diag rename to doc/source/_static/images/create-vm-states.dia diff --git a/doc/source/images/create_vm_states.svg b/doc/source/_static/images/create-vm-states.svg similarity index 100% rename from doc/source/images/create_vm_states.svg rename to doc/source/_static/images/create-vm-states.svg diff --git a/doc/source/_static/images/evolution-of-api.png b/doc/source/_static/images/evolution-of-api.png new file mode 100644 index 00000000000..03d27d38c1e Binary files /dev/null and b/doc/source/_static/images/evolution-of-api.png differ diff --git a/doc/source/images/filteringWorkflow1.png b/doc/source/_static/images/filtering-workflow-1.png similarity index 100% rename from doc/source/images/filteringWorkflow1.png rename to doc/source/_static/images/filtering-workflow-1.png diff --git a/doc/source/images/filteringWorkflow2.png b/doc/source/_static/images/filtering-workflow-2.png similarity index 100% rename from doc/source/images/filteringWorkflow2.png rename to doc/source/_static/images/filtering-workflow-2.png diff --git a/doc/source/image_src/Nova_spec_process.graphml b/doc/source/_static/images/nova-spec-process.graphml similarity index 100% rename from doc/source/image_src/Nova_spec_process.graphml rename to doc/source/_static/images/nova-spec-process.graphml diff --git a/doc/source/images/Nova_spec_process.svg b/doc/source/_static/images/nova-spec-process.svg similarity index 100% rename from doc/source/images/Nova_spec_process.svg rename to doc/source/_static/images/nova-spec-process.svg diff --git a/doc/source/images/rpc/arch.png b/doc/source/_static/images/rpc-arch.png similarity index 100% rename from doc/source/images/rpc/arch.png rename to doc/source/_static/images/rpc-arch.png diff --git a/doc/source/images/rpc/arch.svg b/doc/source/_static/images/rpc-arch.svg similarity index 100% rename from doc/source/images/rpc/arch.svg rename to doc/source/_static/images/rpc-arch.svg diff --git a/doc/source/images/rpc/flow1.png b/doc/source/_static/images/rpc-flow-1.png similarity index 100% rename from doc/source/images/rpc/flow1.png rename to doc/source/_static/images/rpc-flow-1.png diff --git a/doc/source/images/rpc/flow1.svg b/doc/source/_static/images/rpc-flow-1.svg similarity index 100% rename from doc/source/images/rpc/flow1.svg rename to doc/source/_static/images/rpc-flow-1.svg diff --git a/doc/source/images/rpc/flow2.png b/doc/source/_static/images/rpc-flow-2.png similarity index 100% rename from doc/source/images/rpc/flow2.png rename to doc/source/_static/images/rpc-flow-2.png diff --git a/doc/source/images/rpc/flow2.svg b/doc/source/_static/images/rpc-flow-2.svg similarity index 100% rename from doc/source/images/rpc/flow2.svg rename to doc/source/_static/images/rpc-flow-2.svg diff --git a/doc/source/images/rpc/rabt.png b/doc/source/_static/images/rpc-rabt.png similarity index 100% rename from doc/source/images/rpc/rabt.png rename to doc/source/_static/images/rpc-rabt.png diff --git a/doc/source/images/rpc/rabt.svg b/doc/source/_static/images/rpc-rabt.svg similarity index 100% rename from doc/source/images/rpc/rabt.svg rename to doc/source/_static/images/rpc-rabt.svg diff --git a/doc/source/images/rpc/state.png b/doc/source/_static/images/rpc-state.png similarity index 100% rename from doc/source/images/rpc/state.png rename to doc/source/_static/images/rpc-state.png diff --git a/doc/source/addmethod.openstackapi.rst b/doc/source/addmethod.openstackapi.rst deleted file mode 100644 index b1e1b57c6c6..00000000000 --- a/doc/source/addmethod.openstackapi.rst +++ /dev/null @@ -1,52 +0,0 @@ -.. - Copyright 2010-2011 OpenStack Foundation - 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. - -Adding a Method to the OpenStack API -==================================== - -The interface is a mostly RESTful API. REST stands for Representational State Transfer and provides an architecture "style" for distributed systems using HTTP for transport. Figure out a way to express your request and response in terms of resources that are being created, modified, read, or destroyed. - -Routing -------- - -To map URLs to controllers+actions, OpenStack uses the Routes package, a clone of Rails routes for Python implementations. See http://routes.groovie.org/ for more information. - -URLs are mapped to "action" methods on "controller" classes in ``nova/api/openstack/__init__/ApiRouter.__init__`` . - -See http://routes.groovie.org/manual.html for all syntax, but you'll probably just need these two: - - mapper.connect() lets you map a single URL to a single action on a controller. - - mapper.resource() connects many standard URLs to actions on a controller. - -Controllers and actions ------------------------ - -Controllers live in ``nova/api/openstack``, and inherit from nova.wsgi.Controller. - -See ``nova/api/openstack/compute/servers.py`` for an example. - -Action methods take parameters that are sucked out of the URL by mapper.connect() or .resource(). The first two parameters are self and the WebOb request, from which you can get the req.environ, req.body, req.headers, etc. - -Serialization -------------- - -Actions return a dictionary, and wsgi.Controller serializes that to JSON. - -Faults ------- - -If you need to return a non-200, you should -return faults.Fault(webob.exc.HTTPNotFound()) -replacing the exception as appropriate. diff --git a/doc/source/admin/admin-password-injection.rst b/doc/source/admin/admin-password-injection.rst new file mode 100644 index 00000000000..d5d6bab0697 --- /dev/null +++ b/doc/source/admin/admin-password-injection.rst @@ -0,0 +1,59 @@ +==================================== +Injecting the administrator password +==================================== + +Compute can generate a random administrator (root) password and inject that +password into an instance. If this feature is enabled, users can run +:command:`ssh` to an instance without an :command:`ssh` keypair. The random +password appears in the output of the :command:`openstack server create` +command. You can also view and set the admin password from the dashboard. + +.. rubric:: Password injection using the dashboard + +By default, the dashboard will display the ``admin`` password and allow the +user to modify it. + +If you do not want to support password injection, disable the password fields +by editing the dashboard's ``local_settings.py`` file. + +.. code-block:: none + + OPENSTACK_HYPERVISOR_FEATURES = { + ... + 'can_set_password': False, + } + +.. rubric:: Password injection on libvirt-based hypervisors + +For hypervisors that use the libvirt back end (such as KVM, QEMU, and LXC), +admin password injection is disabled by default. To enable it, set this option +in ``/etc/nova/nova.conf``: + +.. code-block:: ini + + [libvirt] + inject_password=true + +When enabled, Compute will modify the password of the admin account by editing +the ``/etc/shadow`` file inside the virtual machine instance. + +.. note:: + + Users can only use :command:`ssh` to access the instance by using the admin + password if the virtual machine image is a Linux distribution, and it has + been configured to allow users to use :command:`ssh` as the root user. This + is not the case for `Ubuntu cloud images `_ + which, by default, does not allow users to use :command:`ssh` to access the + root account. + +.. rubric:: Password injection and XenAPI (XenServer/XCP) + +When using the XenAPI hypervisor back end, Compute uses the XenAPI agent to +inject passwords into guests. The virtual machine image must be configured with +the agent for password injection to work. + +.. rubric:: Password injection and Windows images (all hypervisors) + +For Windows virtual machines, configure the Windows image to retrieve the admin +password on boot by installing an agent such as `cloudbase-init +`_. diff --git a/doc/source/admin/adv-config.rst b/doc/source/admin/adv-config.rst new file mode 100644 index 00000000000..a5d81624b38 --- /dev/null +++ b/doc/source/admin/adv-config.rst @@ -0,0 +1,28 @@ +====================== +Advanced configuration +====================== + +OpenStack clouds run on platforms that differ greatly in the capabilities that +they provide. By default, the Compute service seeks to abstract the underlying +hardware that it runs on, rather than exposing specifics about the underlying +host platforms. This abstraction manifests itself in many ways. For example, +rather than exposing the types and topologies of CPUs running on hosts, the +service exposes a number of generic CPUs (virtual CPUs, or vCPUs) and allows +for overcommitting of these. In a similar manner, rather than exposing the +individual types of network devices available on hosts, generic +software-powered network ports are provided. These features are designed to +allow high resource utilization and allows the service to provide a generic +cost-effective and highly scalable cloud upon which to build applications. + +This abstraction is beneficial for most workloads. However, there are some +workloads where determinism and per-instance performance are important, if not +vital. In these cases, instances can be expected to deliver near-native +performance. The Compute service provides features to improve individual +instance for these kind of workloads. + +.. toctree:: + :maxdepth: 2 + + pci-passthrough + cpu-topologies + huge-pages diff --git a/doc/source/admin/arch.rst b/doc/source/admin/arch.rst new file mode 100644 index 00000000000..6aca6667363 --- /dev/null +++ b/doc/source/admin/arch.rst @@ -0,0 +1,361 @@ +=================== +System architecture +=================== + +OpenStack Compute contains several main components. + +- The cloud controller represents the global state and interacts with the + other components. The ``API server`` acts as the web services front end for + the cloud controller. The ``compute controller`` provides compute server + resources and usually also contains the Compute service. + +- The ``object store`` is an optional component that provides storage + services; you can also use OpenStack Object Storage instead. + +- An ``auth manager`` provides authentication and authorization services when + used with the Compute system; you can also use OpenStack Identity as a + separate authentication service instead. + +- A ``volume controller`` provides fast and permanent block-level storage for + the compute servers. + +- The ``network controller`` provides virtual networks to enable compute + servers to interact with each other and with the public network. You can also + use OpenStack Networking instead. + +- The ``scheduler`` is used to select the most suitable compute controller to + host an instance. + +Compute uses a messaging-based, ``shared nothing`` architecture. All major +components exist on multiple servers, including the compute, volume, and +network controllers, and the Object Storage or Image service. The state of the +entire system is stored in a database. The cloud controller communicates with +the internal object store using HTTP, but it communicates with the scheduler, +network controller, and volume controller using Advanced Message Queuing +Protocol (AMQP). To avoid blocking a component while waiting for a response, +Compute uses asynchronous calls, with a callback that is triggered when a +response is received. + +Hypervisors +~~~~~~~~~~~ + +Compute controls hypervisors through an API server. Selecting the best +hypervisor to use can be difficult, and you must take budget, resource +constraints, supported features, and required technical specifications into +account. However, the majority of OpenStack development is done on systems +using KVM and Xen-based hypervisors. For a detailed list of features and +support across different hypervisors, see the `Feature Support Matrix +`_. + +You can also orchestrate clouds using multiple hypervisors in different +availability zones. Compute supports the following hypervisors: + +- `Baremetal `__ + +- `Docker `__ + +- `Hyper-V + `__ + +- `Kernel-based Virtual Machine (KVM) + `__ + +- `Linux Containers (LXC) `__ + +- `Quick Emulator (QEMU) `__ + +- `User Mode Linux (UML) `__ + +- `VMware vSphere + `__ + +- `Xen `__ + +For more information about hypervisors, see +:doc:`/admin/configuration/hypervisors` +section in the Nova Configuration Reference. + +Projects, users, and roles +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Compute system is designed to be used by different consumers in the form of +projects on a shared system, and role-based access assignments. Roles control +the actions that a user is allowed to perform. + +Projects are isolated resource containers that form the principal +organizational structure within the Compute service. They consist of an +individual VLAN, and volumes, instances, images, keys, and users. A user can +specify the project by appending ``project_id`` to their access key. If no +project is specified in the API request, Compute attempts to use a project with +the same ID as the user. + +For projects, you can use quota controls to limit the: + +- Number of volumes that can be launched. + +- Number of processor cores and the amount of RAM that can be allocated. + +- Floating IP addresses assigned to any instance when it launches. This allows + instances to have the same publicly accessible IP addresses. + +- Fixed IP addresses assigned to the same instance when it launches. This + allows instances to have the same publicly or privately accessible IP + addresses. + +Roles control the actions a user is allowed to perform. By default, most +actions do not require a particular role, but you can configure them by editing +the ``policy.json`` file for user roles. For example, a rule can be defined so +that a user must have the ``admin`` role in order to be able to allocate a +public IP address. + +A project limits users' access to particular images. Each user is assigned a +user name and password. Keypairs granting access to an instance are enabled for +each user, but quotas are set, so that each project can control resource +consumption across available hardware resources. + +.. note:: + + Earlier versions of OpenStack used the term ``tenant`` instead of + ``project``. Because of this legacy terminology, some command-line tools use + ``--tenant_id`` where you would normally expect to enter a project ID. + +Block storage +~~~~~~~~~~~~~ + +OpenStack provides two classes of block storage: ephemeral storage and +persistent volume. + +.. rubric:: Ephemeral storage + +Ephemeral storage includes a root ephemeral volume and an additional ephemeral +volume. + +The root disk is associated with an instance, and exists only for the life of +this very instance. Generally, it is used to store an instance's root file +system, persists across the guest operating system reboots, and is removed on +an instance deletion. The amount of the root ephemeral volume is defined by the +flavor of an instance. + +In addition to the ephemeral root volume, all default types of flavors, except +``m1.tiny``, which is the smallest one, provide an additional ephemeral block +device sized between 20 and 160 GB (a configurable value to suit an +environment). It is represented as a raw block device with no partition table +or file system. A cloud-aware operating system can discover, format, and mount +such a storage device. OpenStack Compute defines the default file system for +different operating systems as Ext4 for Linux distributions, VFAT for non-Linux +and non-Windows operating systems, and NTFS for Windows. However, it is +possible to specify any other filesystem type by using ``virt_mkfs`` or +``default_ephemeral_format`` configuration options. + +.. note:: + + For example, the ``cloud-init`` package included into an Ubuntu's stock + cloud image, by default, formats this space as an Ext4 file system and + mounts it on ``/mnt``. This is a cloud-init feature, and is not an OpenStack + mechanism. OpenStack only provisions the raw storage. + +.. rubric:: Persistent volume + +A persistent volume is represented by a persistent virtualized block device +independent of any particular instance, and provided by OpenStack Block +Storage. + +Only a single configured instance can access a persistent volume. Multiple +instances cannot access a persistent volume. This type of configuration +requires a traditional network file system to allow multiple instances +accessing the persistent volume. It also requires a traditional network file +system like NFS, CIFS, or a cluster file system such as GlusterFS. These +systems can be built within an OpenStack cluster, or provisioned outside of it, +but OpenStack software does not provide these features. + +You can configure a persistent volume as bootable and use it to provide a +persistent virtual instance similar to the traditional non-cloud-based +virtualization system. It is still possible for the resulting instance to keep +ephemeral storage, depending on the flavor selected. In this case, the root +file system can be on the persistent volume, and its state is maintained, even +if the instance is shut down. For more information about this type of +configuration, see `Introduction to the Block Storage service +`_ +in the OpenStack Configuration Reference. + +.. note:: + + A persistent volume does not provide concurrent access from multiple + instances. That type of configuration requires a traditional network file + system like NFS, or CIFS, or a cluster file system such as GlusterFS. These + systems can be built within an OpenStack cluster, or provisioned outside of + it, but OpenStack software does not provide these features. + +EC2 compatibility API +~~~~~~~~~~~~~~~~~~~~~ + +.. todo:: Does this need to be removed now? + +In addition to the native compute API, OpenStack provides an EC2-compatible +API. This API allows EC2 legacy workflows built for EC2 to work with OpenStack. + +.. warning:: + + Nova in tree EC2-compatible API is deprecated. The `ec2-api project + `_ is working to + implement the EC2 API. + +You can use numerous third-party tools and language-specific SDKs to interact +with OpenStack clouds. You can use both native and compatibility APIs. Some of +the more popular third-party tools are: + +Euca2ools + A popular open source command-line tool for interacting with the EC2 API. + This is convenient for multi-cloud environments where EC2 is the common API, + or for transitioning from EC2-based clouds to OpenStack. For more + information, see the `Eucalyptus Documentation + `__. + +Hybridfox + A Firefox browser add-on that provides a graphical interface to many popular + public and private cloud technologies, including OpenStack. For more + information, see the `hybridfox site + `__. + +boto + Python library for interacting with Amazon Web Services. You can use this + library to access OpenStack through the EC2 compatibility API. For more + information, see the `boto project page on GitHub + `__. + +fog + A Ruby cloud services library. It provides methods to interact with a large + number of cloud and virtualization platforms, including OpenStack. For more + information, see the `fog site `__. + +php-opencloud + A PHP SDK designed to work with most OpenStack-based cloud deployments, as + well as Rackspace public cloud. For more information, see the `php-opencloud + site `__. + +Building blocks +~~~~~~~~~~~~~~~ + +In OpenStack the base operating system is usually copied from an image stored +in the OpenStack Image service. This is the most common case and results in an +ephemeral instance that starts from a known template state and loses all +accumulated states on virtual machine deletion. It is also possible to put an +operating system on a persistent volume in the OpenStack Block Storage volume +system. This gives a more traditional persistent system that accumulates states +which are preserved on the OpenStack Block Storage volume across the deletion +and re-creation of the virtual machine. To get a list of available images on +your system, run: + +.. code-block:: console + + $ openstack image list + +--------------------------------------+-----------------------------+--------+ + | ID | Name | Status | + +--------------------------------------+-----------------------------+--------+ + | aee1d242-730f-431f-88c1-87630c0f07ba | Ubuntu 14.04 cloudimg amd64 | active | + | 0b27baa1-0ca6-49a7-b3f4-48388e440245 | Ubuntu 14.10 cloudimg amd64 | active | + | df8d56fc-9cea-4dfd-a8d3-28764de3cb08 | jenkins | active | + +--------------------------------------+-----------------------------+--------+ + +The displayed image attributes are: + +``ID`` + Automatically generated UUID of the image + +``Name`` + Free form, human-readable name for image + +``Status`` + The status of the image. Images marked ``ACTIVE`` are available for use. + +``Server`` + For images that are created as snapshots of running instances, this is the + UUID of the instance the snapshot derives from. For uploaded images, this + field is blank. + +Virtual hardware templates are called ``flavors``. By default, these are +configurable by admin users, however that behavior can be changed by redefining +the access controls for ``compute_extension:flavormanage`` in +``/etc/nova/policy.json`` on the ``compute-api`` server. + +For a list of flavors that are available on your system: + +.. code-block:: console + + $ openstack flavor list + +-----+-----------+-------+------+-----------+-------+-----------+ + | ID | Name | RAM | Disk | Ephemeral | VCPUs | Is_Public | + +-----+-----------+-------+------+-----------+-------+-----------+ + | 1 | m1.tiny | 512 | 1 | 0 | 1 | True | + | 2 | m1.small | 2048 | 20 | 0 | 1 | True | + | 3 | m1.medium | 4096 | 40 | 0 | 2 | True | + | 4 | m1.large | 8192 | 80 | 0 | 4 | True | + | 5 | m1.xlarge | 16384 | 160 | 0 | 8 | True | + +-----+-----------+-------+------+-----------+-------+-----------+ + +Compute service architecture +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These basic categories describe the service architecture and information about +the cloud controller. + +.. rubric:: API server + +At the heart of the cloud framework is an API server, which makes command and +control of the hypervisor, storage, and networking programmatically available +to users. + +The API endpoints are basic HTTP web services which handle authentication, +authorization, and basic command and control functions using various API +interfaces under the Amazon, Rackspace, and related models. This enables API +compatibility with multiple existing tool sets created for interaction with +offerings from other vendors. This broad compatibility prevents vendor lock-in. + +.. rubric:: Message queue + +A messaging queue brokers the interaction between compute nodes (processing), +the networking controllers (software which controls network infrastructure), +API endpoints, the scheduler (determines which physical hardware to allocate to +a virtual resource), and similar components. Communication to and from the +cloud controller is handled by HTTP requests through multiple API endpoints. + +A typical message passing event begins with the API server receiving a request +from a user. The API server authenticates the user and ensures that they are +permitted to issue the subject command. The availability of objects implicated +in the request is evaluated and, if available, the request is routed to the +queuing engine for the relevant workers. Workers continually listen to the +queue based on their role, and occasionally their type host name. When an +applicable work request arrives on the queue, the worker takes assignment of +the task and begins executing it. Upon completion, a response is dispatched to +the queue which is received by the API server and relayed to the originating +user. Database entries are queried, added, or removed as necessary during the +process. + +.. rubric:: Compute worker + +Compute workers manage computing instances on host machines. The API dispatches +commands to compute workers to complete these tasks: + +- Run instances + +- Delete instances (Terminate instances) + +- Reboot instances + +- Attach volumes + +- Detach volumes + +- Get console output + +.. rubric:: Network Controller + +The Network Controller manages the networking resources on host machines. The +API server dispatches commands through the message queue, which are +subsequently processed by Network Controllers. Specific operations include: + +- Allocating fixed IP addresses + +- Configuring VLANs for projects + +- Configuring networks for compute nodes diff --git a/doc/source/admin/availability-zones.rst b/doc/source/admin/availability-zones.rst new file mode 100644 index 00000000000..dc4f8963344 --- /dev/null +++ b/doc/source/admin/availability-zones.rst @@ -0,0 +1,70 @@ +========================================= +Select hosts where instances are launched +========================================= + +With the appropriate permissions, you can select which host instances are +launched on and which roles can boot instances on this host. + +#. To select the host where instances are launched, use the + ``--availability-zone ZONE:HOST:NODE`` parameter on the :command:`openstack + server create` command. + + For example: + + .. code-block:: console + + $ openstack server create --image IMAGE --flavor m1.tiny \ + --key-name KEY --availability-zone ZONE:HOST:NODE \ + --nic net-id=UUID SERVER + + .. note:: + + HOST and NODE are optional parameters. In such cases, use the + ``--availability-zone ZONE::NODE``, ``--availability-zone ZONE:HOST`` or + ``--availability-zone ZONE``. + +#. To specify which roles can launch an instance on a specified host, enable + the ``create:forced_host`` option in the ``policy.json`` file. By default, + this option is enabled for only the admin role. If you see ``Forbidden (HTTP + 403)`` in return, then you are not using admin credentials. + +#. To view the list of valid zones, use the :command:`openstack availability + zone list` command. + + .. code-block:: console + + $ openstack availability zone list + +-----------+-------------+ + | Zone Name | Zone Status | + +-----------+-------------+ + | zone1 | available | + | zone2 | available | + +-----------+-------------+ + +#. To view the list of valid compute hosts, use the :command:`openstack host + list` command. + + .. code-block:: console + + $ openstack host list + +----------------+-------------+----------+ + | Host Name | Service | Zone | + +----------------+-------------+----------+ + | compute01 | compute | nova | + | compute02 | compute | nova | + +----------------+-------------+----------+ + + +#. To view the list of valid compute nodes, use the :command:`openstack + hypervisor list` command. + + .. code-block:: console + + $ openstack hypervisor list + +----+---------------------+ + | ID | Hypervisor Hostname | + +----+---------------------+ + | 1 | server2 | + | 2 | server3 | + | 3 | server4 | + +----+---------------------+ diff --git a/doc/source/admin/common/nova-show-usage-statistics-for-hosts-instances.rst b/doc/source/admin/common/nova-show-usage-statistics-for-hosts-instances.rst new file mode 100644 index 00000000000..05dc48b579b --- /dev/null +++ b/doc/source/admin/common/nova-show-usage-statistics-for-hosts-instances.rst @@ -0,0 +1,128 @@ +============================================= +Show usage statistics for hosts and instances +============================================= + +You can show basic statistics on resource usage for hosts and instances. + +.. note:: + + For more sophisticated monitoring, see the + `ceilometer `__ project. You can + also use tools, such as `Ganglia `__ or + `Graphite `__, to gather more detailed + data. + +Show host usage statistics +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following examples show the host usage statistics for a host called +``devstack``. + +* List the hosts and the nova-related services that run on them: + + .. code-block:: console + + $ openstack host list + +-----------+-------------+----------+ + | Host Name | Service | Zone | + +-----------+-------------+----------+ + | devstack | conductor | internal | + | devstack | compute | nova | + | devstack | cert | internal | + | devstack | network | internal | + | devstack | scheduler | internal | + | devstack | consoleauth | internal | + +-----------+-------------+----------+ + +* Get a summary of resource usage of all of the instances running on the host: + + .. code-block:: console + + $ openstack host show devstack + +----------+----------------------------------+-----+-----------+---------+ + | Host | Project | CPU | MEMORY MB | DISK GB | + +----------+----------------------------------+-----+-----------+---------+ + | devstack | (total) | 2 | 4003 | 157 | + | devstack | (used_now) | 3 | 5120 | 40 | + | devstack | (used_max) | 3 | 4608 | 40 | + | devstack | b70d90d65e464582b6b2161cf3603ced | 1 | 512 | 0 | + | devstack | 66265572db174a7aa66eba661f58eb9e | 2 | 4096 | 40 | + +----------+----------------------------------+-----+-----------+---------+ + + The ``CPU`` column shows the sum of the virtual CPUs for instances running on + the host. + + The ``MEMORY MB`` column shows the sum of the memory (in MB) allocated to the + instances that run on the host. + + The ``DISK GB`` column shows the sum of the root and ephemeral disk sizes (in + GB) of the instances that run on the host. + + The row that has the value ``used_now`` in the ``PROJECT`` column shows the + sum of the resources allocated to the instances that run on the host, plus + the resources allocated to the virtual machine of the host itself. + + The row that has the value ``used_max`` in the ``PROJECT`` column shows the + sum of the resources allocated to the instances that run on the host. + + .. note:: + + These values are computed by using information about the flavors of the + instances that run on the hosts. This command does not query the CPU + usage, memory usage, or hard disk usage of the physical host. + +Show instance usage statistics +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Get CPU, memory, I/O, and network statistics for an instance. + + #. List instances: + + .. code-block:: console + + $ openstack server list + +----------+----------------------+--------+------------+-------------+------------------+------------+ + | ID | Name | Status | Task State | Power State | Networks | Image Name | + +----------+----------------------+--------+------------+-------------+------------------+------------+ + | 84c6e... | myCirrosServer | ACTIVE | None | Running | private=10.0.0.3 | cirros | + | 8a995... | myInstanceFromVolume | ACTIVE | None | Running | private=10.0.0.4 | ubuntu | + +----------+----------------------+--------+------------+-------------+------------------+------------+ + + #. Get diagnostic statistics: + + .. code-block:: console + + $ nova diagnostics myCirrosServer + +---------------------------+--------+ + | Property | Value | + +---------------------------+--------+ + | memory | 524288 | + | memory-actual | 524288 | + | memory-rss | 6444 | + | tap1fec8fb8-7a_rx | 22137 | + | tap1fec8fb8-7a_rx_drop | 0 | + | tap1fec8fb8-7a_rx_errors | 0 | + | tap1fec8fb8-7a_rx_packets | 166 | + | tap1fec8fb8-7a_tx | 18032 | + | tap1fec8fb8-7a_tx_drop | 0 | + | tap1fec8fb8-7a_tx_errors | 0 | + | tap1fec8fb8-7a_tx_packets | 130 | + | vda_errors | -1 | + | vda_read | 2048 | + | vda_read_req | 2 | + | vda_write | 182272 | + | vda_write_req | 74 | + +---------------------------+--------+ + +* Get summary statistics for each project: + + .. code-block:: console + + $ openstack usage list + Usage from 2013-06-25 to 2013-07-24: + +---------+---------+--------------+-----------+---------------+ + | Project | Servers | RAM MB-Hours | CPU Hours | Disk GB-Hours | + +---------+---------+--------------+-----------+---------------+ + | demo | 1 | 344064.44 | 672.00 | 0.00 | + | stack | 3 | 671626.76 | 327.94 | 6558.86 | + +---------+---------+--------------+-----------+---------------+ diff --git a/doc/source/admin/configuration/api.rst b/doc/source/admin/configuration/api.rst new file mode 100644 index 00000000000..21048265259 --- /dev/null +++ b/doc/source/admin/configuration/api.rst @@ -0,0 +1,25 @@ +========================= +Compute API configuration +========================= + +The Compute API, run by the ``nova-api`` daemon, is the component of OpenStack +Compute that receives and responds to user requests, whether they be direct API +calls, or via the CLI tools or dashboard. + +Configure Compute API password handling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The OpenStack Compute API enables users to specify an administrative password +when they create or rebuild a server instance. If the user does not specify a +password, a random password is generated and returned in the API response. + +In practice, how the admin password is handled depends on the hypervisor in use +and might require additional configuration of the instance. For example, you +might have to install an agent to handle the password setting. If the +hypervisor and instance configuration do not support setting a password at +server create time, the password that is returned by the create API call is +misleading because it was ignored. + +To prevent this confusion, use the ``enable_instance_password`` configuration +option to disable the return of the admin password for installations that do +not support setting instance passwords. diff --git a/doc/source/admin/configuration/cells.rst b/doc/source/admin/configuration/cells.rst new file mode 100644 index 00000000000..ddc2c1775aa --- /dev/null +++ b/doc/source/admin/configuration/cells.rst @@ -0,0 +1,295 @@ +========== +Cells (v1) +========== + +.. warning:: + + Configuring and implementing Cells v1 is not recommended for new deployments + of the Compute service (nova). Cells v2 replaces cells v1, and v2 is + required to install or upgrade the Compute service to the 15.0.0 Ocata + release. More information on cells v2 can be found in :doc:`/user/cells`. + +`Cells` functionality enables you to scale an OpenStack Compute cloud in a more +distributed fashion without having to use complicated technologies like +database and message queue clustering. It supports very large deployments. + +When this functionality is enabled, the hosts in an OpenStack Compute cloud are +partitioned into groups called cells. Cells are configured as a tree. The +top-level cell should have a host that runs a ``nova-api`` service, but no +``nova-compute`` services. Each child cell should run all of the typical +``nova-*`` services in a regular Compute cloud except for ``nova-api``. You can +think of cells as a normal Compute deployment in that each cell has its own +database server and message queue broker. + +The ``nova-cells`` service handles communication between cells and selects +cells for new instances. This service is required for every cell. Communication +between cells is pluggable, and currently the only option is communication +through RPC. + +Cells scheduling is separate from host scheduling. ``nova-cells`` first picks +a cell. Once a cell is selected and the new build request reaches its +``nova-cells`` service, it is sent over to the host scheduler in that cell and +the build proceeds as it would have without cells. + +Cell configuration options +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. todo:: This is duplication. We should be able to use the + oslo.config.sphinxext module to generate this for us + +Cells are disabled by default. All cell-related configuration options appear in +the ``[cells]`` section in ``nova.conf``. The following cell-related options +are currently supported: + +``enable`` + Set to ``True`` to turn on cell functionality. Default is ``false``. + +``name`` + Name of the current cell. Must be unique for each cell. + +``capabilities`` + List of arbitrary ``key=value`` pairs defining capabilities of the current + cell. Values include ``hypervisor=xenserver;kvm,os=linux;windows``. + +``call_timeout`` + How long in seconds to wait for replies from calls between cells. + +``scheduler_filter_classes`` + Filter classes that the cells scheduler should use. By default, uses + ``nova.cells.filters.all_filters`` to map to all cells filters included with + Compute. + +``scheduler_weight_classes`` + Weight classes that the scheduler for cells uses. By default, uses + ``nova.cells.weights.all_weighers`` to map to all cells weight algorithms + included with Compute. + +``ram_weight_multiplier`` + Multiplier used to weight RAM. Negative numbers indicate that Compute should + stack VMs on one host instead of spreading out new VMs to more hosts in the + cell. The default value is 10.0. + +Configure the API (top-level) cell +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The cell type must be changed in the API cell so that requests can be proxied +through ``nova-cells`` down to the correct cell properly. Edit the +``nova.conf`` file in the API cell, and specify ``api`` in the ``cell_type`` +key: + +.. code-block:: ini + + [DEFAULT] + compute_api_class=nova.compute.cells_api.ComputeCellsAPI + # ... + + [cells] + cell_type= api + +Configure the child cells +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Edit the ``nova.conf`` file in the child cells, and specify ``compute`` in the +``cell_type`` key: + +.. code-block:: ini + + [DEFAULT] + # Disable quota checking in child cells. Let API cell do it exclusively. + quota_driver=nova.quota.NoopQuotaDriver + + [cells] + cell_type = compute + +Configure the database in each cell +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before bringing the services online, the database in each cell needs to be +configured with information about related cells. In particular, the API cell +needs to know about its immediate children, and the child cells must know about +their immediate agents. The information needed is the ``RabbitMQ`` server +credentials for the particular cell. + +Use the :command:`nova-manage cell create` command to add this information to +the database in each cell: + +.. code-block:: console + + # nova-manage cell create -h + usage: nova-manage cell create [-h] [--name ] + [--cell_type ] + [--username ] [--password ] + [--broker_hosts ] + [--hostname ] [--port ] + [--virtual_host ] + [--woffset ] [--wscale ] + + optional arguments: + -h, --help show this help message and exit + --name Name for the new cell + --cell_type + Whether the cell is parent/api or child/compute + --username + Username for the message broker in this cell + --password + Password for the message broker in this cell + --broker_hosts + Comma separated list of message brokers in this cell. + Each Broker is specified as hostname:port with both + mandatory. This option overrides the --hostname and + --port options (if provided). + --hostname + Address of the message broker in this cell + --port Port number of the message broker in this cell + --virtual_host + The virtual host of the message broker in this cell + --woffset + --wscale + +As an example, assume an API cell named ``api`` and a child cell named +``cell1``. + +Within the ``api`` cell, specify the following ``RabbitMQ`` server information: + +.. code-block:: ini + + rabbit_host=10.0.0.10 + rabbit_port=5672 + rabbit_username=api_user + rabbit_password=api_passwd + rabbit_virtual_host=api_vhost + +Within the ``cell1`` child cell, specify the following ``RabbitMQ`` server +information: + +.. code-block:: ini + + rabbit_host=10.0.1.10 + rabbit_port=5673 + rabbit_username=cell1_user + rabbit_password=cell1_passwd + rabbit_virtual_host=cell1_vhost + +You can run this in the API cell as root: + +.. code-block:: console + + # nova-manage cell create --name cell1 --cell_type child \ + --username cell1_user --password cell1_passwd --hostname 10.0.1.10 \ + --port 5673 --virtual_host cell1_vhost --woffset 1.0 --wscale 1.0 + +Repeat the previous steps for all child cells. + +In the child cell, run the following, as root: + +.. code-block:: console + + # nova-manage cell create --name api --cell_type parent \ + --username api_user --password api_passwd --hostname 10.0.0.10 \ + --port 5672 --virtual_host api_vhost --woffset 1.0 --wscale 1.0 + +To customize the Compute cells, use the configuration option settings +documented above. + +Cell scheduling configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To determine the best cell to use to launch a new instance, Compute uses a set +of filters and weights defined in the ``/etc/nova/nova.conf`` file. The +following options are available to prioritize cells for scheduling: + +``scheduler_filter_classes`` + List of filter classes. By default ``nova.cells.filters.all_filters`` + is specified, which maps to all cells filters included with Compute + (see the section called :ref:`Filters `). + +``scheduler_weight_classes`` + List of weight classes. By default ``nova.cells.weights.all_weighers`` is + specified, which maps to all cell weight algorithms included with Compute. + The following modules are available: + + ``mute_child`` + Downgrades the likelihood of child cells being chosen for scheduling + requests, which haven't sent capacity or capability updates in a while. + Options include ``mute_weight_multiplier`` (multiplier for mute children; + value should be negative). + + ``ram_by_instance_type`` + Select cells with the most RAM capacity for the instance type being + requested. Because higher weights win, Compute returns the number of + available units for the instance type requested. The + ``ram_weight_multiplier`` option defaults to 10.0 that adds to the weight + by a factor of 10. + + Use a negative number to stack VMs on one host instead of spreading + out new VMs to more hosts in the cell. + + ``weight_offset`` + Allows modifying the database to weight a particular cell. You can use this + when you want to disable a cell (for example, '0'), or to set a default + cell by making its ``weight_offset`` very high (for example, + ``999999999999999``). The highest weight will be the first cell to be + scheduled for launching an instance. + +Additionally, the following options are available for the cell scheduler: + +``scheduler_retries`` + Specifies how many times the scheduler tries to launch a new instance when no + cells are available (default=10). + +``scheduler_retry_delay`` + Specifies the delay (in seconds) between retries (default=2). + +As an admin user, you can also add a filter that directs builds to a particular +cell. The ``policy.json`` file must have a line with +``"cells_scheduler_filter:TargetCellFilter" : "is_admin:True"`` to let an admin +user specify a scheduler hint to direct a build to a particular cell. + +Optional cell configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Cells store all inter-cell communication data, including user names and +passwords, in the database. Because the cells data is not updated very +frequently, use the ``[cells]cells_config`` option to specify a JSON file to +store cells data. With this configuration, the database is no longer consulted +when reloading the cells data. The file must have columns present in the Cell +model (excluding common database fields and the ``id`` column). You must +specify the queue connection information through a ``transport_url`` field, +instead of ``username``, ``password``, and so on. + +The ``transport_url`` has the following form:: + + rabbit://USERNAME:PASSWORD@HOSTNAME:PORT/VIRTUAL_HOST + +The scheme can only be ``rabbit``. + +The following sample shows this optional configuration: + +.. code-block:: json + + { + "parent": { + "name": "parent", + "api_url": "http://api.example.com:8774", + "transport_url": "rabbit://rabbit.example.com", + "weight_offset": 0.0, + "weight_scale": 1.0, + "is_parent": true + }, + "cell1": { + "name": "cell1", + "api_url": "http://api.example.com:8774", + "transport_url": "rabbit://rabbit1.example.com", + "weight_offset": 0.0, + "weight_scale": 1.0, + "is_parent": false + }, + "cell2": { + "name": "cell2", + "api_url": "http://api.example.com:8774", + "transport_url": "rabbit://rabbit2.example.com", + "weight_offset": 0.0, + "weight_scale": 1.0, + "is_parent": false + } + } diff --git a/doc/source/admin/configuration/fibre-channel.rst b/doc/source/admin/configuration/fibre-channel.rst new file mode 100644 index 00000000000..a579c443d76 --- /dev/null +++ b/doc/source/admin/configuration/fibre-channel.rst @@ -0,0 +1,28 @@ +================================= +Configuring Fibre Channel Support +================================= + +Fibre Channel support in OpenStack Compute is remote block storage attached to +compute nodes for VMs. + +.. todo:: This below statement needs to be verified for current release + +Fibre Channel supported only the KVM hypervisor. + +Compute and Block Storage support Fibre Channel automatic zoning on Brocade and +Cisco switches. On other hardware Fibre Channel arrays must be pre-zoned or +directly attached to the KVM hosts. + +KVM host requirements +~~~~~~~~~~~~~~~~~~~~~ + +You must install these packages on the KVM host: + +``sysfsutils`` + Nova uses the ``systool`` application in this package. + +``sg3-utils`` or ``sg3_utils`` + Nova uses the ``sg_scan`` and ``sginfo`` applications. + +Installing the ``multipath-tools`` or ``device-mapper-multipath`` package is +optional. diff --git a/doc/source/admin/configuration/hypervisor-basics.rst b/doc/source/admin/configuration/hypervisor-basics.rst new file mode 100644 index 00000000000..9ac1e785e1f --- /dev/null +++ b/doc/source/admin/configuration/hypervisor-basics.rst @@ -0,0 +1,14 @@ +=============================== +Hypervisor Configuration Basics +=============================== + +The node where the ``nova-compute`` service is installed and operates on the +same node that runs all of the virtual machines. This is referred to as the +compute node in this guide. + +By default, the selected hypervisor is KVM. To change to another hypervisor, +change the ``virt_type`` option in the ``[libvirt]`` section of ``nova.conf`` +and restart the ``nova-compute`` service. + +Specific options for particular hypervisors can be found in +the following sections. diff --git a/doc/source/admin/configuration/hypervisor-hyper-v.rst b/doc/source/admin/configuration/hypervisor-hyper-v.rst new file mode 100644 index 00000000000..5fb8a9fb953 --- /dev/null +++ b/doc/source/admin/configuration/hypervisor-hyper-v.rst @@ -0,0 +1,464 @@ +=============================== +Hyper-V virtualization platform +=============================== + +.. todo:: This is really installation guide material and should probably be + moved. + +It is possible to use Hyper-V as a compute node within an OpenStack Deployment. +The ``nova-compute`` service runs as ``openstack-compute``, a 32-bit service +directly upon the Windows platform with the Hyper-V role enabled. The necessary +Python components as well as the ``nova-compute`` service are installed +directly onto the Windows platform. Windows Clustering Services are not needed +for functionality within the OpenStack infrastructure. The use of the Windows +Server 2012 platform is recommend for the best experience and is the platform +for active development. The following Windows platforms have been tested as +compute nodes: + +- Windows Server 2012 +- Windows Server 2012 R2 Server and Core (with the Hyper-V role enabled) +- Hyper-V Server + +Hyper-V configuration +~~~~~~~~~~~~~~~~~~~~~ + +The only OpenStack services required on a Hyper-V node are ``nova-compute`` and +``neutron-hyperv-agent``. Regarding the resources needed for this host you have +to consider that Hyper-V will require 16 GB - 20 GB of disk space for the OS +itself, including updates. Two NICs are required, one connected to the +management network and one to the guest data network. + +The following sections discuss how to prepare the Windows Hyper-V node for +operation as an OpenStack compute node. Unless stated otherwise, any +configuration information should work for the Windows 2012 and 2012 R2 +platforms. + +Local storage considerations +---------------------------- + +The Hyper-V compute node needs to have ample storage for storing the virtual +machine images running on the compute nodes. You may use a single volume for +all, or partition it into an OS volume and VM volume. + +.. _configure-ntp-windows: + +Configure NTP +------------- + +Network time services must be configured to ensure proper operation of the +OpenStack nodes. To set network time on your Windows host you must run the +following commands: + +.. code-block:: bat + + C:\>net stop w32time + C:\>w32tm /config /manualpeerlist:pool.ntp.org,0x8 /syncfromflags:MANUAL + C:\>net start w32time + +Keep in mind that the node will have to be time synchronized with the other +nodes of your OpenStack environment, so it is important to use the same NTP +server. Note that in case of an Active Directory environment, you may do this +only for the AD Domain Controller. + +Configure Hyper-V virtual switching +----------------------------------- + +Information regarding the Hyper-V virtual Switch can be found in the `Hyper-V +Virtual Switch Overview`__. + +To quickly enable an interface to be used as a Virtual Interface the +following PowerShell may be used: + +.. code-block:: none + + PS C:\> $if = Get-NetIPAddress -IPAddress 192* | Get-NetIPInterface + PS C:\> New-VMSwitch -NetAdapterName $if.ifAlias -Name YOUR_BRIDGE_NAME -AllowManagementOS $false + +.. note:: + + It is very important to make sure that when you are using a Hyper-V node + with only 1 NIC the -AllowManagementOS option is set on ``True``, otherwise + you will lose connectivity to the Hyper-V node. + +__ https://technet.microsoft.com/en-us/library/hh831823.aspx + +Enable iSCSI initiator service +------------------------------ + +To prepare the Hyper-V node to be able to attach to volumes provided by cinder +you must first make sure the Windows iSCSI initiator service is running and +started automatically. + +.. code-block:: none + + PS C:\> Set-Service -Name MSiSCSI -StartupType Automatic + PS C:\> Start-Service MSiSCSI + +Configure shared nothing live migration +--------------------------------------- + +Detailed information on the configuration of live migration can be found in +`this guide`__ + +The following outlines the steps of shared nothing live migration. + +#. The target host ensures that live migration is enabled and properly + configured in Hyper-V. + +#. The target host checks if the image to be migrated requires a base VHD and + pulls it from the Image service if not already available on the target host. + +#. The source host ensures that live migration is enabled and properly + configured in Hyper-V. + +#. The source host initiates a Hyper-V live migration. + +#. The source host communicates to the manager the outcome of the operation. + +The following three configuration options are needed in order to support +Hyper-V live migration and must be added to your ``nova.conf`` on the Hyper-V +compute node: + +* This is needed to support shared nothing Hyper-V live migrations. It is used + in ``nova/compute/manager.py``. + + .. code-block:: ini + + instances_shared_storage = False + +* This flag is needed to support live migration to hosts with different CPU + features. This flag is checked during instance creation in order to limit the + CPU features used by the VM. + + .. code-block:: ini + + limit_cpu_features = True + +* This option is used to specify where instances are stored on disk. + + .. code-block:: ini + + instances_path = DRIVELETTER:\PATH\TO\YOUR\INSTANCES + +Additional Requirements: + +* Hyper-V 2012 R2 or Windows Server 2012 R2 with Hyper-V role enabled + +* A Windows domain controller with the Hyper-V compute nodes as domain members + +* The instances_path command-line option/flag needs to be the same on all hosts + +* The ``openstack-compute`` service deployed with the setup must run with + domain credentials. You can set the service credentials with: + +.. code-block:: bat + + C:\>sc config openstack-compute obj="DOMAIN\username" password="password" + +__ https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/manage/Use-live-migration-without-Failover-Clustering-to-move-a-virtual-machine + +How to setup live migration on Hyper-V +-------------------------------------- + +To enable 'shared nothing live' migration, run the 3 instructions below on each +Hyper-V host: + +.. code-block:: none + + PS C:\> Enable-VMMigration + PS C:\> Set-VMMigrationNetwork IP_ADDRESS + PS C:\> Set-VMHost -VirtualMachineMigrationAuthenticationTypeKerberos + +.. note:: + + Replace the ``IP_ADDRESS`` with the address of the interface which will + provide live migration. + +Additional Reading +------------------ + +This article clarifies the various live migration options in Hyper-V: + +`Hyper-V Live Migration of Yesterday +`_ + +Install nova-compute using OpenStack Hyper-V installer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In case you want to avoid all the manual setup, you can use Cloudbase +Solutions' installer. You can find it here: + +`HyperVNovaCompute_Beta download +`_ + +The tool installs an independent Python environment in order to avoid conflicts +with existing applications, and dynamically generates a ``nova.conf`` file +based on the parameters provided by you. + +The tool can also be used for an automated and unattended mode for deployments +on a massive number of servers. More details about how to use the installer and +its features can be found here: + +`Cloudbase `_ + +.. _windows-requirements: + +Requirements +~~~~~~~~~~~~ + +Python +------ + +Python 2.7 32bit must be installed as most of the libraries are not working +properly on the 64bit version. + +**Setting up Python prerequisites** + +#. Download and install Python 2.7 using the MSI installer from here: + + `python-2.7.3.msi download + `_ + + .. code-block:: none + + PS C:\> $src = "https://www.python.org/ftp/python/2.7.3/python-2.7.3.msi" + PS C:\> $dest = "$env:temp\python-2.7.3.msi" + PS C:\> Invoke-WebRequest –Uri $src –OutFile $dest + PS C:\> Unblock-File $dest + PS C:\> Start-Process $dest + +#. Make sure that the ``Python`` and ``Python\Scripts`` paths are set up in the + ``PATH`` environment variable. + + .. code-block:: none + + PS C:\> $oldPath = [System.Environment]::GetEnvironmentVariable("Path") + PS C:\> $newPath = $oldPath + ";C:\python27\;C:\python27\Scripts\" + PS C:\> [System.Environment]::SetEnvironmentVariable("Path", $newPath, [System.EnvironmentVariableTarget]::User + +Python dependencies +------------------- + +The following packages need to be downloaded and manually installed: + +``setuptools`` + https://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11.win32-py2.7.exe + +``pip`` + https://pip.pypa.io/en/latest/installing/ + +``PyMySQL`` + http://codegood.com/download/10/ + +``PyWin32`` + https://sourceforge.net/projects/pywin32/files/pywin32/Build%20217/pywin32-217.win32-py2.7.exe + +``Greenlet`` + http://www.lfd.uci.edu/~gohlke/pythonlibs/#greenlet + +``PyCryto`` + http://www.voidspace.org.uk/downloads/pycrypto26/pycrypto-2.6.win32-py2.7.exe + +The following packages must be installed with pip: + +* ``ecdsa`` +* ``amqp`` +* ``wmi`` + +.. code-block:: none + + PS C:\> pip install ecdsa + PS C:\> pip install amqp + PS C:\> pip install wmi + +Other dependencies +------------------ + +``qemu-img`` is required for some of the image related operations. You can get +it from here: http://qemu.weilnetz.de/. You must make sure that the +``qemu-img`` path is set in the PATH environment variable. + +Some Python packages need to be compiled, so you may use MinGW or Visual +Studio. You can get MinGW from here: http://sourceforge.net/projects/mingw/. +You must configure which compiler is to be used for this purpose by using the +``distutils.cfg`` file in ``$Python27\Lib\distutils``, which can contain: + +.. code-block:: ini + + [build] + compiler = mingw32 + +As a last step for setting up MinGW, make sure that the MinGW binaries' +directories are set up in PATH. + +Install nova-compute +~~~~~~~~~~~~~~~~~~~~ + +Download the nova code +---------------------- + +#. Use Git to download the necessary source code. The installer to run Git on + Windows can be downloaded here: + + https://github.com/msysgit/msysgit/releases/download/Git-1.9.2-preview20140411/Git-1.9.2-preview20140411.exe + +#. Download the installer. Once the download is complete, run the installer and + follow the prompts in the installation wizard. The default should be + acceptable for the purposes of this guide. + + .. code-block:: none + + PS C:\> $src = "https://github.com/msysgit/msysgit/releases/download/Git-1.9.2-preview20140411/Git-1.9.2-preview20140411.exe" + PS C:\> $dest = "$env:temp\Git-1.9.2-preview20140411.exe" + PS C:\> Invoke-WebRequest –Uri $src –OutFile $dest + PS C:\> Unblock-File $dest + PS C:\> Start-Process $dest + +#. Run the following to clone the nova code. + + .. code-block:: none + + PS C:\> git.exe clone https://git.openstack.org/openstack/nova + +Install nova-compute service +---------------------------- + +To install ``nova-compute``, run: + +.. code-block:: none + + PS C:\> cd c:\nova + PS C:\> python setup.py install + +Configure nova-compute +---------------------- + +The ``nova.conf`` file must be placed in ``C:\etc\nova`` for running OpenStack +on Hyper-V. Below is a sample ``nova.conf`` for Windows: + +.. code-block:: ini + + [DEFAULT] + auth_strategy = keystone + image_service = nova.image.glance.GlanceImageService + compute_driver = nova.virt.hyperv.driver.HyperVDriver + volume_api_class = nova.volume.cinder.API + fake_network = true + instances_path = C:\Program Files (x86)\OpenStack\Instances + use_cow_images = true + force_config_drive = false + injected_network_template = C:\Program Files (x86)\OpenStack\Nova\etc\interfaces.template + policy_file = C:\Program Files (x86)\OpenStack\Nova\etc\policy.json + mkisofs_cmd = C:\Program Files (x86)\OpenStack\Nova\bin\mkisofs.exe + allow_resize_to_same_host = true + running_deleted_instance_action = reap + running_deleted_instance_poll_interval = 120 + resize_confirm_window = 5 + resume_guests_state_on_host_boot = true + rpc_response_timeout = 1800 + lock_path = C:\Program Files (x86)\OpenStack\Log\ + rpc_backend = nova.openstack.common.rpc.impl_kombu + rabbit_host = IP_ADDRESS + rabbit_port = 5672 + rabbit_userid = guest + rabbit_password = Passw0rd + logdir = C:\Program Files (x86)\OpenStack\Log\ + logfile = nova-compute.log + instance_usage_audit = true + instance_usage_audit_period = hour + use_neutron = True + [glance] + api_servers = http://IP_ADDRESS:9292 + [neutron] + url = http://IP_ADDRESS:9696 + auth_strategy = keystone + admin_tenant_name = service + admin_username = neutron + admin_password = Passw0rd + admin_auth_url = http://IP_ADDRESS:35357/v2.0 + [hyperv] + vswitch_name = newVSwitch0 + limit_cpu_features = false + config_drive_inject_password = false + qemu_img_cmd = C:\Program Files (x86)\OpenStack\Nova\bin\qemu-img.exe + config_drive_cdrom = true + dynamic_memory_ratio = 1 + enable_instance_metrics_collection = true + [rdp] + enabled = true + html5_proxy_base_url = https://IP_ADDRESS:4430 + +Prepare images for use with Hyper-V +----------------------------------- + +Hyper-V currently supports only the VHD and VHDX file format for virtual +machine instances. Detailed instructions for installing virtual machines on +Hyper-V can be found here: + +`Create Virtual Machines +`_ + +Once you have successfully created a virtual machine, you can then upload the +image to `glance` using the `openstack-client`: + +.. code-block:: none + + PS C:\> openstack image create --name "VM_IMAGE_NAME" --property hypervisor_type=hyperv --public \ + --container-format bare --disk-format vhd + +.. note:: + + VHD and VHDX files sizes can be bigger than their maximum internal size, + as such you need to boot instances using a flavor with a slightly bigger + disk size than the internal size of the disk file. + To create VHDs, use the following PowerShell cmdlet: + + .. code-block:: none + + PS C:\> New-VHD DISK_NAME.vhd -SizeBytes VHD_SIZE + +Inject interfaces and routes +---------------------------- + +The ``interfaces.template`` file describes the network interfaces and routes +available on your system and how to activate them. You can specify the location +of the file with the ``injected_network_template`` configuration option in +``/etc/nova/nova.conf``. + +.. code-block:: ini + + injected_network_template = PATH_TO_FILE + +A default template exists in ``nova/virt/interfaces.template``. + +Run Compute with Hyper-V +------------------------ + +To start the ``nova-compute`` service, run this command from a console in the +Windows server: + +.. code-block:: none + + PS C:\> C:\Python27\python.exe c:\Python27\Scripts\nova-compute --config-file c:\etc\nova\nova.conf + +Troubleshoot Hyper-V configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* I ran the :command:`nova-manage service list` command from my controller; + however, I'm not seeing smiley faces for Hyper-V compute nodes, what do I do? + + Verify that you are synchronized with a network time source. For + instructions about how to configure NTP on your Hyper-V compute node, see + :ref:`configure-ntp-windows`. + +* How do I restart the compute service? + + .. code-block:: none + + PS C:\> net stop nova-compute && net start nova-compute + +* How do I restart the iSCSI initiator service? + + .. code-block:: none + + PS C:\> net stop msiscsi && net start msiscsi diff --git a/doc/source/admin/configuration/hypervisor-kvm.rst b/doc/source/admin/configuration/hypervisor-kvm.rst new file mode 100644 index 00000000000..159d065a855 --- /dev/null +++ b/doc/source/admin/configuration/hypervisor-kvm.rst @@ -0,0 +1,374 @@ +=== +KVM +=== + +.. todo:: This is really installation guide material and should probably be + moved. + +KVM is configured as the default hypervisor for Compute. + +.. note:: + + This document contains several sections about hypervisor selection. If you + are reading this document linearly, you do not want to load the KVM module + before you install ``nova-compute``. The ``nova-compute`` service depends + on qemu-kvm, which installs ``/lib/udev/rules.d/45-qemu-kvm.rules``, which + sets the correct permissions on the ``/dev/kvm`` device node. + +To enable KVM explicitly, add the following configuration options to the +``/etc/nova/nova.conf`` file: + +.. code-block:: ini + + compute_driver = libvirt.LibvirtDriver + + [libvirt] + virt_type = kvm + +The KVM hypervisor supports the following virtual machine image formats: + +* Raw +* QEMU Copy-on-write (QCOW2) +* QED Qemu Enhanced Disk +* VMware virtual machine disk format (vmdk) + +This section describes how to enable KVM on your system. For more information, +see the following distribution-specific documentation: + +* `Fedora: Virtualization Getting Started Guide `_ + from the Fedora 22 documentation. +* `Ubuntu: KVM/Installation `_ from the Community Ubuntu documentation. +* `Debian: Virtualization with KVM `_ from the Debian handbook. +* `Red Hat Enterprise Linux: Installing virtualization packages on an existing + Red Hat Enterprise Linux system `_ from the ``Red Hat Enterprise Linux + Virtualization Host Configuration and Guest Installation Guide``. +* `openSUSE: Installing KVM `_ + from the openSUSE Virtualization with KVM manual. +* `SLES: Installing KVM `_ from the SUSE Linux Enterprise Server + ``Virtualization Guide``. + +.. _enable-kvm: + +Enable KVM +~~~~~~~~~~ + +The following sections outline how to enable KVM based hardware virtualization +on different architectures and platforms. To perform these steps, you must be +logged in as the ``root`` user. + +For x86 based systems +--------------------- + +#. To determine whether the ``svm`` or ``vmx`` CPU extensions are present, run + this command: + + .. code-block:: console + + # grep -E 'svm|vmx' /proc/cpuinfo + + This command generates output if the CPU is capable of + hardware-virtualization. Even if output is shown, you might still need to + enable virtualization in the system BIOS for full support. + + If no output appears, consult your system documentation to ensure that your + CPU and motherboard support hardware virtualization. Verify that any + relevant hardware virtualization options are enabled in the system BIOS. + + The BIOS for each manufacturer is different. If you must enable + virtualization in the BIOS, look for an option containing the words + ``virtualization``, ``VT``, ``VMX``, or ``SVM``. + +#. To list the loaded kernel modules and verify that the ``kvm`` modules are + loaded, run this command: + + .. code-block:: console + + # lsmod | grep kvm + + If the output includes ``kvm_intel`` or ``kvm_amd``, the ``kvm`` hardware + virtualization modules are loaded and your kernel meets the module + requirements for OpenStack Compute. + + If the output does not show that the ``kvm`` module is loaded, run this + command to load it: + + .. code-block:: console + + # modprobe -a kvm + + Run the command for your CPU. For Intel, run this command: + + .. code-block:: console + + # modprobe -a kvm-intel + + For AMD, run this command: + + .. code-block:: console + + # modprobe -a kvm-amd + + Because a KVM installation can change user group membership, you might need + to log in again for changes to take effect. + + If the kernel modules do not load automatically, use the procedures listed + in these subsections. + +If the checks indicate that required hardware virtualization support or kernel +modules are disabled or unavailable, you must either enable this support on the +system or find a system with this support. + +.. note:: + + Some systems require that you enable VT support in the system BIOS. If you + believe your processor supports hardware acceleration but the previous + command did not produce output, reboot your machine, enter the system BIOS, + and enable the VT option. + +If KVM acceleration is not supported, configure Compute to use a different +hypervisor, such as ``QEMU`` or ``Xen``. See :ref:`compute_qemu` or +:ref:`compute_xen_api` for details. + +These procedures help you load the kernel modules for Intel-based and AMD-based +processors if they do not load automatically during KVM installation. + +.. rubric:: Intel-based processors + +If your compute host is Intel-based, run these commands as root to load the +kernel modules: + +.. code-block:: console + + # modprobe kvm + # modprobe kvm-intel + +Add these lines to the ``/etc/modules`` file so that these modules load on +reboot: + +.. code-block:: console + + kvm + kvm-intel + +.. rubric:: AMD-based processors + +If your compute host is AMD-based, run these commands as root to load the +kernel modules: + +.. code-block:: console + + # modprobe kvm + # modprobe kvm-amd + +Add these lines to ``/etc/modules`` file so that these modules load on reboot: + +.. code-block:: console + + kvm + kvm-amd + +For POWER based systems +----------------------- + +KVM as a hypervisor is supported on POWER system's PowerNV platform. + +#. To determine if your POWER platform supports KVM based virtualization run + the following command: + + .. code-block:: console + + # cat /proc/cpuinfo | grep PowerNV + + If the previous command generates the following output, then CPU supports + KVM based virtualization. + + .. code-block:: console + + platform: PowerNV + + If no output is displayed, then your POWER platform does not support KVM + based hardware virtualization. + +#. To list the loaded kernel modules and verify that the ``kvm`` modules are + loaded, run the following command: + + .. code-block:: console + + # lsmod | grep kvm + + If the output includes ``kvm_hv``, the ``kvm`` hardware virtualization + modules are loaded and your kernel meets the module requirements for + OpenStack Compute. + + If the output does not show that the ``kvm`` module is loaded, run the + following command to load it: + + .. code-block:: console + + # modprobe -a kvm + + For PowerNV platform, run the following command: + + .. code-block:: console + + # modprobe -a kvm-hv + + Because a KVM installation can change user group membership, you might need + to log in again for changes to take effect. + +Configure Compute backing storage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Backing Storage is the storage used to provide the expanded operating system +image, and any ephemeral storage. Inside the virtual machine, this is normally +presented as two virtual hard disks (for example, ``/dev/vda`` and ``/dev/vdb`` +respectively). However, inside OpenStack, this can be derived from one of these +methods: ``lvm``, ``qcow``, ``rbd`` or ``flat``, chosen using the +``images_type`` option in ``nova.conf`` on the compute node. + +.. note:: + + The option ``raw`` is acceptable but deprecated in favor of ``flat``. The + Flat back end uses either raw or QCOW2 storage. It never uses a backing + store, so when using QCOW2 it copies an image rather than creating an + overlay. By default, it creates raw files but will use QCOW2 when creating a + disk from a QCOW2 if ``force_raw_images`` is not set in configuration. + +QCOW is the default backing store. It uses a copy-on-write philosophy to delay +allocation of storage until it is actually needed. This means that the space +required for the backing of an image can be significantly less on the real disk +than what seems available in the virtual machine operating system. + +Flat creates files without any sort of file formatting, effectively creating +files with the plain binary one would normally see on a real disk. This can +increase performance, but means that the entire size of the virtual disk is +reserved on the physical disk. + +Local `LVM volumes +`__ can also be +used. Set ``images_volume_group = nova_local`` where ``nova_local`` is the name +of the LVM group you have created. + +Specify the CPU model of KVM guests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Compute service enables you to control the guest CPU model that is exposed +to KVM virtual machines. Use cases include: + +* To maximize performance of virtual machines by exposing new host CPU features + to the guest + +* To ensure a consistent default CPU across all machines, removing reliance of + variable QEMU defaults + +In libvirt, the CPU is specified by providing a base CPU model name (which is a +shorthand for a set of feature flags), a set of additional feature flags, and +the topology (sockets/cores/threads). The libvirt KVM driver provides a number +of standard CPU model names. These models are defined in the +``/usr/share/libvirt/cpu_map.xml`` file. Check this file to determine which +models are supported by your local installation. + +Two Compute configuration options in the ``[libvirt]`` group of ``nova.conf`` +define which type of CPU model is exposed to the hypervisor when using KVM: +``cpu_mode`` and ``cpu_model``. + +The ``cpu_mode`` option can take one of the following values: ``none``, +``host-passthrough``, ``host-model``, and ``custom``. + +Host model (default for KVM & QEMU) +----------------------------------- + +If your ``nova.conf`` file contains ``cpu_mode=host-model``, libvirt identifies +the CPU model in ``/usr/share/libvirt/cpu_map.xml`` file that most closely +matches the host, and requests additional CPU flags to complete the match. This +configuration provides the maximum functionality and performance and maintains +good reliability and compatibility if the guest is migrated to another host +with slightly different host CPUs. + +Host pass through +----------------- + +If your ``nova.conf`` file contains ``cpu_mode=host-passthrough``, libvirt +tells KVM to pass through the host CPU with no modifications. The difference +to host-model, instead of just matching feature flags, every last detail of the +host CPU is matched. This gives the best performance, and can be important to +some apps which check low level CPU details, but it comes at a cost with +respect to migration. The guest can only be migrated to a matching host CPU. + +Custom +------ + +If your ``nova.conf`` file contains ``cpu_mode=custom``, you can explicitly +specify one of the supported named models using the cpu_model configuration +option. For example, to configure the KVM guests to expose Nehalem CPUs, your +``nova.conf`` file should contain: + +.. code-block:: ini + + [libvirt] + cpu_mode = custom + cpu_model = Nehalem + +None (default for all libvirt-driven hypervisors other than KVM & QEMU) +----------------------------------------------------------------------- + +If your ``nova.conf`` file contains ``cpu_mode=none``, libvirt does not specify +a CPU model. Instead, the hypervisor chooses the default model. + +Guest agent support +------------------- + +Use guest agents to enable optional access between compute nodes and guests +through a socket, using the QMP protocol. + +To enable this feature, you must set ``hw_qemu_guest_agent=yes`` as a metadata +parameter on the image you wish to use to create the guest-agent-capable +instances from. You can explicitly disable the feature by setting +``hw_qemu_guest_agent=no`` in the image metadata. + +KVM performance tweaks +~~~~~~~~~~~~~~~~~~~~~~ + +The `VHostNet `_ kernel module improves +network performance. To load the kernel module, run the following command as +root: + +.. code-block:: console + + # modprobe vhost_net + +Troubleshoot KVM +~~~~~~~~~~~~~~~~ + +Trying to launch a new virtual machine instance fails with the ``ERROR`` state, +and the following error appears in the ``/var/log/nova/nova-compute.log`` file: + +.. code-block:: console + + libvirtError: internal error no supported architecture for os type 'hvm' + +This message indicates that the KVM kernel modules were not loaded. + +If you cannot start VMs after installation without rebooting, the permissions +might not be set correctly. This can happen if you load the KVM module before +you install ``nova-compute``. To check whether the group is set to ``kvm``, +run: + +.. code-block:: console + + # ls -l /dev/kvm + +If it is not set to ``kvm``, run: + +.. code-block:: console + + # udevadm trigger diff --git a/doc/source/admin/configuration/hypervisor-lxc.rst b/doc/source/admin/configuration/hypervisor-lxc.rst new file mode 100644 index 00000000000..eb8d51f83ef --- /dev/null +++ b/doc/source/admin/configuration/hypervisor-lxc.rst @@ -0,0 +1,38 @@ +====================== +LXC (Linux containers) +====================== + +LXC (also known as Linux containers) is a virtualization technology that works +at the operating system level. This is different from hardware virtualization, +the approach used by other hypervisors such as KVM, Xen, and VMware. LXC (as +currently implemented using libvirt in the Compute service) is not a secure +virtualization technology for multi-tenant environments (specifically, +containers may affect resource quotas for other containers hosted on the same +machine). Additional containment technologies, such as AppArmor, may be used to +provide better isolation between containers, although this is not the case by +default. For all these reasons, the choice of this virtualization technology +is not recommended in production. + +If your compute hosts do not have hardware support for virtualization, LXC will +likely provide better performance than QEMU. In addition, if your guests must +access specialized hardware, such as GPUs, this might be easier to achieve with +LXC than other hypervisors. + +.. note:: + + Some OpenStack Compute features might be missing when running with LXC as + the hypervisor. See the `hypervisor support matrix + `_ for details. + +To enable LXC, ensure the following options are set in ``/etc/nova/nova.conf`` +on all hosts running the ``nova-compute`` service. + +.. code-block:: ini + + compute_driver = libvirt.LibvirtDriver + + [libvirt] + virt_type = lxc + +On Ubuntu, enable LXC support in OpenStack by installing the +``nova-compute-lxc`` package. diff --git a/doc/source/admin/configuration/hypervisor-qemu.rst b/doc/source/admin/configuration/hypervisor-qemu.rst new file mode 100644 index 00000000000..6849b89c280 --- /dev/null +++ b/doc/source/admin/configuration/hypervisor-qemu.rst @@ -0,0 +1,56 @@ +.. _compute_qemu: + +==== +QEMU +==== + +From the perspective of the Compute service, the QEMU hypervisor is +very similar to the KVM hypervisor. Both are controlled through libvirt, +both support the same feature set, and all virtual machine images that +are compatible with KVM are also compatible with QEMU. +The main difference is that QEMU does not support native virtualization. +Consequently, QEMU has worse performance than KVM and is a poor choice +for a production deployment. + +The typical uses cases for QEMU are + +* Running on older hardware that lacks virtualization support. +* Running the Compute service inside of a virtual machine for + development or testing purposes, where the hypervisor does not + support native virtualization for guests. + +To enable QEMU, add these settings to ``nova.conf``: + +.. code-block:: ini + + compute_driver = libvirt.LibvirtDriver + + [libvirt] + virt_type = qemu + +For some operations you may also have to install the +:command:`guestmount` utility: + +On Ubuntu: + +.. code-block:: console + + # apt-get install guestmount + +On Red Hat Enterprise Linux, Fedora, or CentOS: + +.. code-block:: console + + # yum install libguestfs-tools + +On openSUSE: + +.. code-block:: console + + # zypper install guestfs-tools + +The QEMU hypervisor supports the following virtual machine image formats: + +* Raw +* QEMU Copy-on-write (qcow2) +* VMware virtual machine disk format (vmdk) diff --git a/doc/source/admin/configuration/hypervisor-virtuozzo.rst b/doc/source/admin/configuration/hypervisor-virtuozzo.rst new file mode 100644 index 00000000000..13c63daba62 --- /dev/null +++ b/doc/source/admin/configuration/hypervisor-virtuozzo.rst @@ -0,0 +1,39 @@ +========= +Virtuozzo +========= + +Virtuozzo 7.0.0 (or newer), or its community edition OpenVZ, provides both +types of virtualization: Kernel Virtual Machines and OS Containers. The type +of instance to span is chosen depending on the ``hw_vm_type`` property of an +image. + +.. note:: + + Some OpenStack Compute features may be missing when running with Virtuozzo + as the hypervisor. See :doc:`/user/support-matrix` for details. + +To enable Virtuozzo Containers, set the following options in +``/etc/nova/nova.conf`` on all hosts running the ``nova-compute`` service. + +.. code-block:: ini + + compute_driver = libvirt.LibvirtDriver + force_raw_images = False + + [libvirt] + virt_type = parallels + images_type = ploop + connection_uri = parallels:///system + inject_partition = -2 + +To enable Virtuozzo Virtual Machines, set the following options in +``/etc/nova/nova.conf`` on all hosts running the ``nova-compute`` service. + +.. code-block:: ini + + compute_driver = libvirt.LibvirtDriver + + [libvirt] + virt_type = parallels + images_type = qcow2 + connection_uri = parallels:///system diff --git a/doc/source/admin/configuration/hypervisor-vmware.rst b/doc/source/admin/configuration/hypervisor-vmware.rst new file mode 100644 index 00000000000..d8d3ec40cad --- /dev/null +++ b/doc/source/admin/configuration/hypervisor-vmware.rst @@ -0,0 +1,1130 @@ +============== +VMware vSphere +============== + +Introduction +~~~~~~~~~~~~ + +OpenStack Compute supports the VMware vSphere product family and enables access +to advanced features such as vMotion, High Availability, and Dynamic Resource +Scheduling (DRS). + +This section describes how to configure VMware-based virtual machine images for +launch. The VMware driver supports vCenter version 5.5.0 and later. + +The VMware vCenter driver enables the ``nova-compute`` service to communicate +with a VMware vCenter server that manages one or more ESX host clusters. The +driver aggregates the ESX hosts in each cluster to present one large hypervisor +entity for each cluster to the Compute scheduler. Because individual ESX hosts +are not exposed to the scheduler, Compute schedules to the granularity of +clusters and vCenter uses DRS to select the actual ESX host within the cluster. +When a virtual machine makes its way into a vCenter cluster, it can use all +vSphere features. + +The following sections describe how to configure the VMware vCenter driver. + +High-level architecture +~~~~~~~~~~~~~~~~~~~~~~~ + +The following diagram shows a high-level view of the VMware driver +architecture: + +.. rubric:: VMware driver architecture + +.. figure:: /figures/vmware-nova-driver-architecture.jpg + :width: 100% + +As the figure shows, the OpenStack Compute Scheduler sees three hypervisors +that each correspond to a cluster in vCenter. ``nova-compute`` contains the +VMware driver. You can run with multiple ``nova-compute`` services. It is +recommended to run with one ``nova-compute`` service per ESX cluster thus +ensuring that while Compute schedules at the granularity of the +``nova-compute`` service it is also in effect able to schedule at the cluster +level. In turn the VMware driver inside ``nova-compute`` interacts with the +vCenter APIs to select an appropriate ESX host within the cluster. Internally, +vCenter uses DRS for placement. + +The VMware vCenter driver also interacts with the Image service to copy VMDK +images from the Image service back-end store. The dotted line in the figure +represents VMDK images being copied from the OpenStack Image service to the +vSphere data store. VMDK images are cached in the data store so the copy +operation is only required the first time that the VMDK image is used. + +After OpenStack boots a VM into a vSphere cluster, the VM becomes visible in +vCenter and can access vSphere advanced features. At the same time, the VM is +visible in the OpenStack dashboard and you can manage it as you would any other +OpenStack VM. You can perform advanced vSphere operations in vCenter while you +configure OpenStack resources such as VMs through the OpenStack dashboard. + +The figure does not show how networking fits into the architecture. Both +``nova-network`` and the OpenStack Networking Service are supported. For +details, see :ref:`vmware-networking`. + +Configuration overview +~~~~~~~~~~~~~~~~~~~~~~ + +To get started with the VMware vCenter driver, complete the following +high-level steps: + +#. Configure vCenter. See :ref:`vmware-prereqs`. + +#. Configure the VMware vCenter driver in the ``nova.conf`` file. + See :ref:`vmware-vcdriver`. + +#. Load desired VMDK images into the Image service. See :ref:`vmware-images`. + +#. Configure networking with either ``nova-network`` or + the Networking service. See :ref:`vmware-networking`. + +.. _vmware-prereqs: + +Prerequisites and limitations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the following list to prepare a vSphere environment that runs with the +VMware vCenter driver: + +Copying VMDK files + In vSphere 5.1, copying large image files (for example, 12 GB and greater) + from the Image service can take a long time. To improve performance, VMware + recommends that you upgrade to VMware vCenter Server 5.1 Update 1 or later. + For more information, see the `Release Notes + `_. + +DRS + For any cluster that contains multiple ESX hosts, enable DRS and enable fully + automated placement. + +Shared storage + Only shared storage is supported and data stores must be shared among all + hosts in a cluster. It is recommended to remove data stores not intended for + OpenStack from clusters being configured for OpenStack. + +Clusters and data stores + Do not use OpenStack clusters and data stores for other purposes. If you do, + OpenStack displays incorrect usage information. + +Networking + The networking configuration depends on the desired networking model. See + :ref:`vmware-networking`. + +Security groups + If you use the VMware driver with OpenStack Networking and the NSX plug-in, + security groups are supported. If you use ``nova-network``, security groups + are not supported. + + .. note:: + + The NSX plug-in is the only plug-in that is validated for vSphere. + +VNC + The port range 5900 - 6105 (inclusive) is automatically enabled for VNC + connections on every ESX host in all clusters under OpenStack control. + + .. note:: + + In addition to the default VNC port numbers (5900 to 6000) specified in + the above document, the following ports are also used: 6101, 6102, and + 6105. + + You must modify the ESXi firewall configuration to allow the VNC ports. + Additionally, for the firewall modifications to persist after a reboot, you + must create a custom vSphere Installation Bundle (VIB) which is then + installed onto the running ESXi host or added to a custom image profile used + to install ESXi hosts. For details about how to create a VIB for persisting + the firewall configuration modifications, see `Knowledge Base + `_. + + .. note:: + + The VIB can be downloaded from `openstack-vmwareapi-team/Tools + `_. + +To use multiple vCenter installations with OpenStack, each vCenter must be +assigned to a separate availability zone. This is required as the OpenStack +Block Storage VMDK driver does not currently work across multiple vCenter +installations. + +VMware vCenter service account +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +OpenStack integration requires a vCenter service account with the following +minimum permissions. Apply the permissions to the ``Datacenter`` root object, +and select the :guilabel:`Propagate to Child Objects` option. + +.. list-table:: vCenter permissions tree + :header-rows: 1 + :widths: 12, 12, 40, 36 + + * - All Privileges + - + - + - + * - + - Datastore + - + - + * - + - + - Allocate space + - + * - + - + - Browse datastore + - + * - + - + - Low level file operation + - + * - + - + - Remove file + - + * - + - Extension + - + - + * - + - + - Register extension + - + * - + - Folder + - + - + * - + - + - Create folder + - + * - + - Host + - + - + * - + - + - Configuration + - + * - + - + - + - Maintenance + * - + - + - + - Network configuration + * - + - + - + - Storage partition configuration + * - + - Network + - + - + * - + - + - Assign network + - + * - + - Resource + - + - + * - + - + - Assign virtual machine to resource pool + - + * - + - + - Migrate powered off virtual machine + - + * - + - + - Migrate powered on virtual machine + - + * - + - Virtual Machine + - + - + * - + - + - Configuration + - + * - + - + - + - Add existing disk + * - + - + - + - Add new disk + * - + - + - + - Add or remove device + * - + - + - + - Advanced + * - + - + - + - CPU count + * - + - + - + - Change resource + * - + - + - + - Disk change tracking + * - + - + - + - Host USB device + * - + - + - + - Memory + * - + - + - + - Modify device settings + * - + - + - + - Raw device + * - + - + - + - Remove disk + * - + - + - + - Rename + * - + - + - + - Set annotation + * - + - + - + - Swapfile placement + * - + - + - Interaction + - + * - + - + - + - Configure CD media + * - + - + - + - Power Off + * - + - + - + - Power On + * - + - + - + - Reset + * - + - + - + - Suspend + * - + - + - Inventory + - + * - + - + - + - Create from existing + * - + - + - + - Create new + * - + - + - + - Move + * - + - + - + - Remove + * - + - + - + - Unregister + * - + - + - Provisioning + - + * - + - + - + - Clone virtual machine + * - + - + - + - Customize + * - + - + - + - Create template from virtual machine + * - + - + - Snapshot management + - + * - + - + - + - Create snapshot + * - + - + - + - Remove snapshot + * - + - Sessions + - + - + * - + - + - + - Validate session + * - + - + - + - View and stop sessions + * - + - vApp + - + - + * - + - + - Export + - + * - + - + - Import + - + +.. _vmware-vcdriver: + +VMware vCenter driver +~~~~~~~~~~~~~~~~~~~~~ + +Use the VMware vCenter driver (VMwareVCDriver) to connect OpenStack Compute +with vCenter. This recommended configuration enables access through vCenter to +advanced vSphere features like vMotion, High Availability, and Dynamic Resource +Scheduling (DRS). + +VMwareVCDriver configuration options +------------------------------------ + +Add the following VMware-specific configuration options to the ``nova.conf`` +file: + +.. code-block:: ini + + [DEFAULT] + compute_driver = vmwareapi.VMwareVCDriver + + [vmware] + host_ip = + host_username = + host_password = + cluster_name = + datastore_regex = + +.. note:: + + * Clusters: The vCenter driver can support only a single cluster. Clusters + and data stores used by the vCenter driver should not contain any VMs + other than those created by the driver. + + * Data stores: The ``datastore_regex`` setting specifies the data stores to + use with Compute. For example, ``datastore_regex="nas.*"`` selects all + the data stores that have a name starting with "nas". If this line is + omitted, Compute uses the first data store returned by the vSphere API. It + is recommended not to use this field and instead remove data stores that + are not intended for OpenStack. + + * Reserved host memory: The ``reserved_host_memory_mb`` option value is 512 + MB by default. However, VMware recommends that you set this option to 0 MB + because the vCenter driver reports the effective memory available to the + virtual machines. + + * The vCenter driver generates instance name by instance ID. Instance name + template is ignored. + + * The minimum supported vCenter version is 5.5.0. Starting in the OpenStack + Ocata release any version lower than 5.5.0 will be logged as a warning. In + the OpenStack Pike release this will be enforced. + +A ``nova-compute`` service can control one or more clusters containing multiple +ESXi hosts, making ``nova-compute`` a critical service from a high availability +perspective. Because the host that runs ``nova-compute`` can fail while the +vCenter and ESX still run, you must protect the ``nova-compute`` service +against host failures. + +.. note:: + + Many ``nova.conf`` options are relevant to libvirt but do not apply to this + driver. + +.. _vmware-images: + +Images with VMware vSphere +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The vCenter driver supports images in the VMDK format. Disks in this format can +be obtained from VMware Fusion or from an ESX environment. It is also possible +to convert other formats, such as qcow2, to the VMDK format using the +``qemu-img`` utility. After a VMDK disk is available, load it into the Image +service. Then, you can use it with the VMware vCenter driver. The following +sections provide additional details on the supported disks and the commands +used for conversion and upload. + +Supported image types +--------------------- + +Upload images to the OpenStack Image service in VMDK format. The following +VMDK disk types are supported: + +* ``VMFS Flat Disks`` (includes thin, thick, zeroedthick, and + eagerzeroedthick). Note that once a VMFS thin disk is exported from VMFS to a + non-VMFS location, like the OpenStack Image service, it becomes a + preallocated flat disk. This impacts the transfer time from the Image service + to the data store when the full preallocated flat disk, rather than the thin + disk, must be transferred. + +* ``Monolithic Sparse disks``. Sparse disks get imported from the Image service + into ESXi as thin provisioned disks. Monolithic Sparse disks can be obtained + from VMware Fusion or can be created by converting from other virtual disk + formats using the ``qemu-img`` utility. + +* ``Stream-optimized disks``. Stream-optimized disks are compressed sparse + disks. They can be obtained from VMware vCenter/ESXi when exporting vm to + ovf/ova template. + +The following table shows the ``vmware_disktype`` property that applies to each +of the supported VMDK disk types: + +.. list-table:: OpenStack Image service disk type settings + :header-rows: 1 + + * - vmware_disktype property + - VMDK disk type + * - sparse + - Monolithic Sparse + * - thin + - VMFS flat, thin provisioned + * - preallocated (default) + - VMFS flat, thick/zeroedthick/eagerzeroedthick + * - streamOptimized + - Compressed Sparse + +The ``vmware_disktype`` property is set when an image is loaded into the Image +service. For example, the following command creates a Monolithic Sparse image +by setting ``vmware_disktype`` to ``sparse``: + +.. code-block:: console + + $ openstack image create \ + --disk-format vmdk \ + --container-format bare \ + --property vmware_disktype="sparse" \ + --property vmware_ostype="ubuntu64Guest" \ + ubuntu-sparse < ubuntuLTS-sparse.vmdk + +.. note:: + + Specifying ``thin`` does not provide any advantage over ``preallocated`` + with the current version of the driver. Future versions might restore the + thin properties of the disk after it is downloaded to a vSphere data store. + +The following table shows the ``vmware_ostype`` property that applies to each +of the supported guest OS: + +.. note:: + + If a glance image has a ``vmware_ostype`` property which does not correspond + to a valid VMware guestId, VM creation will fail, and a warning will be + logged. + +.. list-table:: OpenStack Image service OS type settings + :header-rows: 1 + + * - vmware_ostype property + - Retail Name + * - asianux3_64Guest + - Asianux Server 3 (64 bit) + * - asianux3Guest + - Asianux Server 3 + * - asianux4_64Guest + - Asianux Server 4 (64 bit) + * - asianux4Guest + - Asianux Server 4 + * - darwin64Guest + - Darwin 64 bit + * - darwinGuest + - Darwin + * - debian4_64Guest + - Debian GNU/Linux 4 (64 bit) + * - debian4Guest + - Debian GNU/Linux 4 + * - debian5_64Guest + - Debian GNU/Linux 5 (64 bit) + * - debian5Guest + - Debian GNU/Linux 5 + * - dosGuest + - MS-DOS + * - freebsd64Guest + - FreeBSD x64 + * - freebsdGuest + - FreeBSD + * - mandrivaGuest + - Mandriva Linux + * - netware4Guest + - Novell NetWare 4 + * - netware5Guest + - Novell NetWare 5.1 + * - netware6Guest + - Novell NetWare 6.x + * - nld9Guest + - Novell Linux Desktop 9 + * - oesGuest + - Open Enterprise Server + * - openServer5Guest + - SCO OpenServer 5 + * - openServer6Guest + - SCO OpenServer 6 + * - opensuse64Guest + - openSUSE (64 bit) + * - opensuseGuest + - openSUSE + * - os2Guest + - OS/2 + * - other24xLinux64Guest + - Linux 2.4x Kernel (64 bit) (experimental) + * - other24xLinuxGuest + - Linux 2.4x Kernel + * - other26xLinux64Guest + - Linux 2.6x Kernel (64 bit) (experimental) + * - other26xLinuxGuest + - Linux 2.6x Kernel (experimental) + * - otherGuest + - Other Operating System + * - otherGuest64 + - Other Operating System (64 bit) (experimental) + * - otherLinux64Guest + - Linux (64 bit) (experimental) + * - otherLinuxGuest + - Other Linux + * - redhatGuest + - Red Hat Linux 2.1 + * - rhel2Guest + - Red Hat Enterprise Linux 2 + * - rhel3_64Guest + - Red Hat Enterprise Linux 3 (64 bit) + * - rhel3Guest + - Red Hat Enterprise Linux 3 + * - rhel4_64Guest + - Red Hat Enterprise Linux 4 (64 bit) + * - rhel4Guest + - Red Hat Enterprise Linux 4 + * - rhel5_64Guest + - Red Hat Enterprise Linux 5 (64 bit) (experimental) + * - rhel5Guest + - Red Hat Enterprise Linux 5 + * - rhel6_64Guest + - Red Hat Enterprise Linux 6 (64 bit) + * - rhel6Guest + - Red Hat Enterprise Linux 6 + * - sjdsGuest + - Sun Java Desktop System + * - sles10_64Guest + - SUSE Linux Enterprise Server 10 (64 bit) (experimental) + * - sles10Guest + - SUSE Linux Enterprise Server 10 + * - sles11_64Guest + - SUSE Linux Enterprise Server 11 (64 bit) + * - sles11Guest + - SUSE Linux Enterprise Server 11 + * - sles64Guest + - SUSE Linux Enterprise Server 9 (64 bit) + * - slesGuest + - SUSE Linux Enterprise Server 9 + * - solaris10_64Guest + - Solaris 10 (64 bit) (experimental) + * - solaris10Guest + - Solaris 10 (32 bit) (experimental) + * - solaris6Guest + - Solaris 6 + * - solaris7Guest + - Solaris 7 + * - solaris8Guest + - Solaris 8 + * - solaris9Guest + - Solaris 9 + * - suse64Guest + - SUSE Linux (64 bit) + * - suseGuest + - SUSE Linux + * - turboLinux64Guest + - Turbolinux (64 bit) + * - turboLinuxGuest + - Turbolinux + * - ubuntu64Guest + - Ubuntu Linux (64 bit) + * - ubuntuGuest + - Ubuntu Linux + * - unixWare7Guest + - SCO UnixWare 7 + * - win2000AdvServGuest + - Windows 2000 Advanced Server + * - win2000ProGuest + - Windows 2000 Professional + * - win2000ServGuest + - Windows 2000 Server + * - win31Guest + - Windows 3.1 + * - win95Guest + - Windows 95 + * - win98Guest + - Windows 98 + * - windows7_64Guest + - Windows 7 (64 bit) + * - windows7Guest + - Windows 7 + * - windows7Server64Guest + - Windows Server 2008 R2 (64 bit) + * - winLonghorn64Guest + - Windows Longhorn (64 bit) (experimental) + * - winLonghornGuest + - Windows Longhorn (experimental) + * - winMeGuest + - Windows Millennium Edition + * - winNetBusinessGuest + - Windows Small Business Server 2003 + * - winNetDatacenter64Guest + - Windows Server 2003, Datacenter Edition (64 bit) (experimental) + * - winNetDatacenterGuest + - Windows Server 2003, Datacenter Edition + * - winNetEnterprise64Guest + - Windows Server 2003, Enterprise Edition (64 bit) + * - winNetEnterpriseGuest + - Windows Server 2003, Enterprise Edition + * - winNetStandard64Guest + - Windows Server 2003, Standard Edition (64 bit) + * - winNetEnterpriseGuest + - Windows Server 2003, Enterprise Edition + * - winNetStandard64Guest + - Windows Server 2003, Standard Edition (64 bit) + * - winNetStandardGuest + - Windows Server 2003, Standard Edition + * - winNetWebGuest + - Windows Server 2003, Web Edition + * - winNTGuest + - Windows NT 4 + * - winVista64Guest + - Windows Vista (64 bit) + * - winVistaGuest + - Windows Vista + * - winXPHomeGuest + - Windows XP Home Edition + * - winXPPro64Guest + - Windows XP Professional Edition (64 bit) + * - winXPProGuest + - Windows XP Professional + +Convert and load images +----------------------- + +Using the ``qemu-img`` utility, disk images in several formats (such as, +qcow2) can be converted to the VMDK format. + +For example, the following command can be used to convert a `qcow2 Ubuntu +Trusty cloud image `_: + +.. code-block:: console + + $ qemu-img convert -f qcow2 ~/Downloads/trusty-server-cloudimg-amd64-disk1.img \ + -O vmdk trusty-server-cloudimg-amd64-disk1.vmdk + +VMDK disks converted through ``qemu-img`` are ``always`` monolithic sparse VMDK +disks with an IDE adapter type. Using the previous example of the Ubuntu Trusty +image after the ``qemu-img`` conversion, the command to upload the VMDK disk +should be something like: + +.. code-block:: console + + $ openstack image create \ + --container-format bare --disk-format vmdk \ + --property vmware_disktype="sparse" \ + --property vmware_adaptertype="ide" \ + trusty-cloud < trusty-server-cloudimg-amd64-disk1.vmdk + +Note that the ``vmware_disktype`` is set to ``sparse`` and the +``vmware_adaptertype`` is set to ``ide`` in the previous command. + +If the image did not come from the ``qemu-img`` utility, the +``vmware_disktype`` and ``vmware_adaptertype`` might be different. To +determine the image adapter type from an image file, use the following command +and look for the ``ddb.adapterType=`` line: + +.. code-block:: console + + $ head -20 + +Assuming a preallocated disk type and an iSCSI lsiLogic adapter type, the +following command uploads the VMDK disk: + +.. code-block:: console + + $ openstack image create \ + --disk-format vmdk \ + --container-format bare \ + --property vmware_adaptertype="lsiLogic" \ + --property vmware_disktype="preallocated" \ + --property vmware_ostype="ubuntu64Guest" \ + ubuntu-thick-scsi < ubuntuLTS-flat.vmdk + +Currently, OS boot VMDK disks with an IDE adapter type cannot be attached to a +virtual SCSI controller and likewise disks with one of the SCSI adapter types +(such as, busLogic, lsiLogic, lsiLogicsas, paraVirtual) cannot be attached to +the IDE controller. Therefore, as the previous examples show, it is important +to set the ``vmware_adaptertype`` property correctly. The default adapter type +is lsiLogic, which is SCSI, so you can omit the ``vmware_adaptertype`` property +if you are certain that the image adapter type is lsiLogic. + +Tag VMware images +----------------- + +In a mixed hypervisor environment, OpenStack Compute uses the +``hypervisor_type`` tag to match images to the correct hypervisor type. For +VMware images, set the hypervisor type to ``vmware``. Other valid hypervisor +types include: ``hyperv``, ``ironic``, ``lxc``, ``qemu``, ``uml``, and ``xen``. +Note that ``qemu`` is used for both QEMU and KVM hypervisor types. + +.. code-block:: console + + $ openstack image create \ + --disk-format vmdk \ + --container-format bare \ + --property vmware_adaptertype="lsiLogic" \ + --property vmware_disktype="preallocated" \ + --property hypervisor_type="vmware" \ + --property vmware_ostype="ubuntu64Guest" \ + ubuntu-thick-scsi < ubuntuLTS-flat.vmdk + +Optimize images +--------------- + +Monolithic Sparse disks are considerably faster to download but have the +overhead of an additional conversion step. When imported into ESX, sparse disks +get converted to VMFS flat thin provisioned disks. The download and conversion +steps only affect the first launched instance that uses the sparse disk image. +The converted disk image is cached, so subsequent instances that use this disk +image can simply use the cached version. + +To avoid the conversion step (at the cost of longer download times) consider +converting sparse disks to thin provisioned or preallocated disks before +loading them into the Image service. + +Use one of the following tools to pre-convert sparse disks. + +vSphere CLI tools + Sometimes called the remote CLI or rCLI. + + Assuming that the sparse disk is made available on a data store accessible by + an ESX host, the following command converts it to preallocated format: + + .. code-block:: console + + vmkfstools --server=ip_of_some_ESX_host -i \ + /vmfs/volumes/datastore1/sparse.vmdk \ + /vmfs/volumes/datastore1/converted.vmdk + + Note that the vifs tool from the same CLI package can be used to upload the + disk to be converted. The vifs tool can also be used to download the + converted disk if necessary. + +``vmkfstools`` directly on the ESX host + If the SSH service is enabled on an ESX host, the sparse disk can be uploaded + to the ESX data store through scp and the vmkfstools local to the ESX host + can use used to perform the conversion. After you log in to the host through + ssh, run this command: + + .. code-block:: console + + vmkfstools -i /vmfs/volumes/datastore1/sparse.vmdk /vmfs/volumes/datastore1/converted.vmdk + +``vmware-vdiskmanager`` + ``vmware-vdiskmanager`` is a utility that comes bundled with VMware Fusion + and VMware Workstation. The following example converts a sparse disk to + preallocated format: + + .. code-block:: console + + '/Applications/VMware Fusion.app/Contents/Library/vmware-vdiskmanager' -r sparse.vmdk -t 4 converted.vmdk + +In the previous cases, the converted vmdk is actually a pair of files: + +* The descriptor file ``converted.vmdk``. + +* The actual virtual disk data file ``converted-flat.vmdk``. + +The file to be uploaded to the Image service is ``converted-flat.vmdk``. + +Image handling +-------------- + +The ESX hypervisor requires a copy of the VMDK file in order to boot up a +virtual machine. As a result, the vCenter OpenStack Compute driver must +download the VMDK via HTTP from the Image service to a data store that is +visible to the hypervisor. To optimize this process, the first time a VMDK file +is used, it gets cached in the data store. A cached image is stored in a +folder named after the image ID. Subsequent virtual machines that need the +VMDK use the cached version and don't have to copy the file again from the +Image service. + +Even with a cached VMDK, there is still a copy operation from the cache +location to the hypervisor file directory in the shared data store. To avoid +this copy, boot the image in linked_clone mode. To learn how to enable this +mode, see :ref:`vmware-config`. + +.. note:: + + You can also use the ``img_linked_clone`` property (or legacy property + ``vmware_linked_clone``) in the Image service to override the linked_clone + mode on a per-image basis. + + If spawning a virtual machine image from ISO with a VMDK disk, the image is + created and attached to the virtual machine as a blank disk. In that case + ``img_linked_clone`` property for the image is just ignored. + +If multiple compute nodes are running on the same host, or have a shared file +system, you can enable them to use the same cache folder on the back-end data +store. To configure this action, set the ``cache_prefix`` option in the +``nova.conf`` file. Its value stands for the name prefix of the folder where +cached images are stored. + +.. note:: + + This can take effect only if compute nodes are running on the same host, or + have a shared file system. + +You can automatically purge unused images after a specified period of time. To +configure this action, set these options in the ``DEFAULT`` section in the +``nova.conf`` file: + +``remove_unused_base_images`` + Set this option to ``True`` to specify that unused images should be removed + after the duration specified in the + ``remove_unused_original_minimum_age_seconds`` option. The default is + ``True``. + +``remove_unused_original_minimum_age_seconds`` + Specifies the duration in seconds after which an unused image is purged from + the cache. The default is ``86400`` (24 hours). + +.. _vmware-networking: + +Networking with VMware vSphere +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The VMware driver supports networking with the ``nova-network`` service or the +Networking Service. Depending on your installation, complete these +configuration steps before you provision VMs: + +#. **The nova-network service with the FlatManager or FlatDHCPManager**. + Create a port group with the same name as the ``flat_network_bridge`` value + in the ``nova.conf`` file. The default value is ``br100``. If you specify + another value, the new value must be a valid Linux bridge identifier that + adheres to Linux bridge naming conventions. + + All VM NICs are attached to this port group. + + Ensure that the flat interface of the node that runs the ``nova-network`` + service has a path to this network. + + .. note:: + + When configuring the port binding for this port group in vCenter, specify + ``ephemeral`` for the port binding type. For more information, see + `Choosing a port binding type in ESX/ESXi `_ in the VMware Knowledge Base. + +#. **The nova-network service with the VlanManager**. + Set the ``vlan_interface`` configuration option to match the ESX host + interface that handles VLAN-tagged VM traffic. + + OpenStack Compute automatically creates the corresponding port groups. + +#. If you are using the OpenStack Networking Service: + Before provisioning VMs, create a port group with the same name as the + ``vmware.integration_bridge`` value in ``nova.conf`` (default is + ``br-int``). All VM NICs are attached to this port group for management by + the OpenStack Networking plug-in. + +Volumes with VMware vSphere +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The VMware driver supports attaching volumes from the Block Storage service. +The VMware VMDK driver for OpenStack Block Storage is recommended and should be +used for managing volumes based on vSphere data stores. For more information +about the VMware VMDK driver, see Cinder's manual on the VMDK Driver (TODO: +this has not yet been imported and published). Also an +iSCSI volume driver provides limited support and can be used only for +attachments. + +.. _vmware-config: + +Configuration reference +~~~~~~~~~~~~~~~~~~~~~~~ + +To customize the VMware driver, use the configuration option settings below. + +.. TODO(sdague): for the import we just copied this in from the auto generated + file. We probably need a strategy for doing equivalent autogeneration, but + we don't as of yet. + + Warning: Do not edit this file. It is automatically generated from the + software project's code and your changes will be overwritten. + + The tool to generate this file lives in openstack-doc-tools repository. + + Please make any changes needed in the code, then run the + autogenerate-config-doc tool from the openstack-doc-tools repository, or + ask for help on the documentation mailing list, IRC channel or meeting. + +.. _nova-vmware: + +.. list-table:: Description of VMware configuration options + :header-rows: 1 + :class: config-ref-table + + * - Configuration option = Default value + - Description + * - **[vmware]** + - + * - ``api_retry_count`` = ``10`` + - (Integer) Number of times VMware vCenter server API must be retried on connection failures, e.g. socket error, etc. + * - ``ca_file`` = ``None`` + - (String) Specifies the CA bundle file to be used in verifying the vCenter server certificate. + * - ``cache_prefix`` = ``None`` + - (String) This option adds a prefix to the folder where cached images are stored + + This is not the full path - just a folder prefix. This should only be used when a datastore cache is shared between compute nodes. + + Note: This should only be used when the compute nodes are running on same host or they have a shared file system. + + Possible values: + + * Any string representing the cache prefix to the folder + * - ``cluster_name`` = ``None`` + - (String) Name of a VMware Cluster ComputeResource. + * - ``console_delay_seconds`` = ``None`` + - (Integer) Set this value if affected by an increased network latency causing repeated characters when typing in a remote console. + * - ``datastore_regex`` = ``None`` + - (String) Regular expression pattern to match the name of datastore. + + The datastore_regex setting specifies the datastores to use with Compute. For example, datastore_regex="nas.*" selects all the data stores that have a name starting with "nas". + + NOTE: If no regex is given, it just picks the datastore with the most freespace. + + Possible values: + + * Any matching regular expression to a datastore must be given + * - ``host_ip`` = ``None`` + - (String) Hostname or IP address for connection to VMware vCenter host. + * - ``host_password`` = ``None`` + - (String) Password for connection to VMware vCenter host. + * - ``host_port`` = ``443`` + - (Port number) Port for connection to VMware vCenter host. + * - ``host_username`` = ``None`` + - (String) Username for connection to VMware vCenter host. + * - ``insecure`` = ``False`` + - (Boolean) If true, the vCenter server certificate is not verified. If false, then the default CA truststore is used for verification. + + Related options: + + * ca_file: This option is ignored if "ca_file" is set. + * - ``integration_bridge`` = ``None`` + - (String) This option should be configured only when using the NSX-MH Neutron plugin. This is the name of the integration bridge on the ESXi server or host. This should not be set for any other Neutron plugin. Hence the default value is not set. + + Possible values: + + * Any valid string representing the name of the integration bridge + * - ``maximum_objects`` = ``100`` + - (Integer) This option specifies the limit on the maximum number of objects to return in a single result. + + A positive value will cause the operation to suspend the retrieval when the count of objects reaches the specified limit. The server may still limit the count to something less than the configured value. Any remaining objects may be retrieved with additional requests. + * - ``pbm_default_policy`` = ``None`` + - (String) This option specifies the default policy to be used. + + If pbm_enabled is set and there is no defined storage policy for the specific request, then this policy will be used. + + Possible values: + + * Any valid storage policy such as VSAN default storage policy + + Related options: + + * pbm_enabled + * - ``pbm_enabled`` = ``False`` + - (Boolean) This option enables or disables storage policy based placement of instances. + + Related options: + + * pbm_default_policy + * - ``pbm_wsdl_location`` = ``None`` + - (String) This option specifies the PBM service WSDL file location URL. + + Setting this will disable storage policy based placement of instances. + + Possible values: + + * Any valid file path e.g file:///opt/SDK/spbm/wsdl/pbmService.wsdl + * - ``serial_port_proxy_uri`` = ``None`` + - (String) Identifies a proxy service that provides network access to the serial_port_service_uri. + + Possible values: + + * Any valid URI + + Related options: This option is ignored if serial_port_service_uri is not specified. + + * serial_port_service_uri + * - ``serial_port_service_uri`` = ``None`` + - (String) Identifies the remote system where the serial port traffic will be sent. + + This option adds a virtual serial port which sends console output to a configurable service URI. At the service URI address there will be virtual serial port concentrator that will collect console logs. If this is not set, no serial ports will be added to the created VMs. + + Possible values: + + * Any valid URI + * - ``task_poll_interval`` = ``0.5`` + - (Floating point) Time interval in seconds to poll remote tasks invoked on VMware VC server. + * - ``use_linked_clone`` = ``True`` + - (Boolean) This option enables/disables the use of linked clone. + + The ESX hypervisor requires a copy of the VMDK file in order to boot up a virtual machine. The compute driver must download the VMDK via HTTP from the OpenStack Image service to a datastore that is visible to the hypervisor and cache it. Subsequent virtual machines that need the VMDK use the cached version and don't have to copy the file again from the OpenStack Image service. + + If set to false, even with a cached VMDK, there is still a copy operation from the cache location to the hypervisor file directory in the shared datastore. If set to true, the above copy operation is avoided as it creates copy of the virtual machine that shares virtual disks with its parent VM. + * - ``wsdl_location`` = ``None`` + - (String) This option specifies VIM Service WSDL Location + + If vSphere API versions 5.1 and later is being used, this section can be ignored. If version is less than 5.1, WSDL files must be hosted locally and their location must be specified in the above section. + + Optional over-ride to default location for bug work-arounds. + + Possible values: + + * http:///vimService.wsdl + + * file:///opt/stack/vmware/SDK/wsdl/vim25/vimService.wsdl diff --git a/doc/source/admin/configuration/hypervisor-xen-api.rst b/doc/source/admin/configuration/hypervisor-xen-api.rst new file mode 100644 index 00000000000..f3254da7b3b --- /dev/null +++ b/doc/source/admin/configuration/hypervisor-xen-api.rst @@ -0,0 +1,434 @@ +.. _compute_xen_api: + +============================================= +XenServer (and other XAPI based Xen variants) +============================================= + +This section describes XAPI managed hypervisors, and how to use them with +OpenStack. + +Terminology +~~~~~~~~~~~ + +Xen +--- + +A hypervisor that provides the fundamental isolation between virtual machines. +Xen is open source (GPLv2) and is managed by `XenProject.org +`_, a cross-industry organization and a Linux +Foundation Collaborative project. + +Xen is a component of many different products and projects. The hypervisor +itself is very similar across all these projects, but the way that it is +managed can be different, which can cause confusion if you're not clear which +toolstack you are using. Make sure you know what `toolstack +`_ you want before you get +started. If you want to use Xen with libvirt in OpenStack Compute refer to +:doc:`hypervisor-xen-libvirt`. + +XAPI +---- + +XAPI is one of the toolstacks that could control a Xen based hypervisor. +XAPI's role is similar to libvirt's in the KVM world. The API provided by XAPI +is called XenAPI. To learn more about the provided interface, look at `XenAPI +Object Model Overview `_ for definitions of XAPI +specific terms such as SR, VDI, VIF and PIF. + +OpenStack has a compute driver which talks to XAPI, therefore all XAPI managed +servers could be used with OpenStack. + +XenAPI +------ + +XenAPI is the API provided by XAPI. This name is also used by the python +library that is a client for XAPI. A set of packages to use XenAPI on existing +distributions can be built using the `xenserver/buildroot +`_ project. + +XenServer +--------- + +An Open Source virtualization platform that delivers all features needed for +any server and datacenter implementation including the Xen hypervisor and XAPI +for the management. For more information and product downloads, visit +`xenserver.org `_. + +XCP +--- + +XCP is not supported anymore. XCP project recommends all XCP users to upgrade +to the latest version of XenServer by visiting `xenserver.org +`_. + +Privileged and unprivileged domains +----------------------------------- + +A Xen host runs a number of virtual machines, VMs, or domains (the terms are +synonymous on Xen). One of these is in charge of running the rest of the +system, and is known as domain 0, or dom0. It is the first domain to boot after +Xen, and owns the storage and networking hardware, the device drivers, and the +primary control software. Any other VM is unprivileged, and is known as a domU +or guest. All customer VMs are unprivileged, but you should note that on +XenServer (and other XenAPI using hypervisors), the OpenStack Compute service +(``nova-compute``) also runs in a domU. This gives a level of security +isolation between the privileged system software and the OpenStack software +(much of which is customer-facing). This architecture is described in more +detail later. + +Paravirtualized versus hardware virtualized domains +--------------------------------------------------- + +A Xen virtual machine can be paravirtualized (PV) or hardware virtualized +(HVM). This refers to the interaction between Xen, domain 0, and the guest VM's +kernel. PV guests are aware of the fact that they are virtualized and will +co-operate with Xen and domain 0; this gives them better performance +characteristics. HVM guests are not aware of their environment, and the +hardware has to pretend that they are running on an unvirtualized machine. HVM +guests do not need to modify the guest operating system, which is essential +when running Windows. + +In OpenStack, customer VMs may run in either PV or HVM mode. However, the +OpenStack domU (that's the one running ``nova-compute``) must be running in PV +mode. + +XenAPI deployment architecture +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A basic OpenStack deployment on a XAPI-managed server, assuming that the +network provider is neutron network, looks like this: + +.. figure:: /figures/xenserver_architecture.png + :width: 100% + +Key things to note: + +* The hypervisor: Xen + +* Domain 0: runs XAPI and some small pieces from OpenStack, + the XAPI plug-ins. + +* OpenStack VM: The ``Compute`` service runs in a paravirtualized virtual + machine, on the host under management. Each host runs a local instance of + ``Compute``. It is also running neutron plugin-agent + (``neutron-openvswitch-agent``) to perform local vSwitch configuration. + +* OpenStack Compute uses the XenAPI Python library to talk to XAPI, and it uses + the Management Network to reach from the OpenStack VM to Domain 0. + +Some notes on the networking: + +* The above diagram assumes DHCP networking. + +* There are three main OpenStack networks: + + * Management network: RabbitMQ, MySQL, inter-host communication, and + compute-XAPI communication. Please note that the VM images are downloaded + by the XenAPI plug-ins, so make sure that the OpenStack Image service is + accessible through this network. It usually means binding those services to + the management interface. + + * Tenant network: controlled by neutron, this is used for tenant traffic. + + * Public network: floating IPs, public API endpoints. + +* The networks shown here must be connected to the corresponding physical + networks within the data center. In the simplest case, three individual + physical network cards could be used. It is also possible to use VLANs to + separate these networks. Please note, that the selected configuration must be + in line with the networking model selected for the cloud. (In case of VLAN + networking, the physical channels have to be able to forward the tagged + traffic.) + +* With the Networking service, you should enable Linux bridge in ``Dom0`` which + is used for Compute service. ``nova-compute`` will create Linux bridges for + security group and ``neutron-openvswitch-agent`` in Compute node will apply + security group rules on these Linux bridges. To implement this, you need to + remove ``/etc/modprobe.d/blacklist-bridge*`` in ``Dom0``. + +Further reading +~~~~~~~~~~~~~~~ + +Here are some of the resources available to learn more about Xen: + +* `Citrix XenServer official documentation + `_ +* `What is Xen? by XenProject.org + `_ +* `Xen Hypervisor project + `_ +* `Xapi project `_ +* `Further XenServer and OpenStack information + `_ + +Install XenServer +~~~~~~~~~~~~~~~~~ + +Before you can run OpenStack with XenServer, you must install the hypervisor on +`an appropriate server `_. + +.. note:: + + Xen is a type 1 hypervisor: When your server starts, Xen is the first + software that runs. Consequently, you must install XenServer before you + install the operating system where you want to run OpenStack code. You then + install ``nova-compute`` into a dedicated virtual machine on the host. + +Use the following link to download XenServer's installation media: + +* http://xenserver.org/open-source-virtualization-download.html + +When you install many servers, you might find it easier to perform `PXE boot +installations `_. You can also package any +post-installation changes that you want to make to your XenServer by following +the instructions of `creating your own XenServer supplemental pack +`_. + +.. important:: + + Make sure you use the EXT type of storage repository (SR). Features that + require access to VHD files (such as copy on write, snapshot and migration) + do not work when you use the LVM SR. Storage repository (SR) is a + XAPI-specific term relating to the physical storage where virtual disks are + stored. + + On the XenServer installation screen, choose the :guilabel:`XenDesktop + Optimized` option. If you use an answer file, make sure you use + ``srtype="ext"`` in the ``installation`` tag of the answer file. + +Post-installation steps +~~~~~~~~~~~~~~~~~~~~~~~ + +The following steps need to be completed after the hypervisor's installation: + +#. For resize and migrate functionality, enable password-less SSH + authentication and set up the ``/images`` directory on dom0. + +#. Install the XAPI plug-ins. + +#. To support AMI type images, you must set up ``/boot/guest`` + symlink/directory in dom0. + +#. Create a paravirtualized virtual machine that can run ``nova-compute``. + +#. Install and configure ``nova-compute`` in the above virtual machine. + +Install XAPI plug-ins +--------------------- + +When you use a XAPI managed hypervisor, you can install a Python script (or any +executable) on the host side, and execute that through XenAPI. These scripts +are called plug-ins. The OpenStack related XAPI plug-ins live in OpenStack +os-xenapi code repository. These plug-ins have to be copied to dom0's +filesystem, to the appropriate directory, where XAPI can find them. It is +important to ensure that the version of the plug-ins are in line with the +OpenStack Compute installation you are using. + +The plugins should typically be copied from the Nova installation running in +the Compute's DomU (``pip show os-xenapi`` to find its location), but if you +want to download the latest version the following procedure can be used. + +**Manually installing the plug-ins** + +#. Create temporary files/directories: + + .. code-block:: console + + $ OS_XENAPI_TARBALL=$(mktemp) + $ OS_XENAPI_SOURCES=$(mktemp -d) + +#. Get the source from the openstack.org archives. The example assumes the + latest release is used, and the XenServer host is accessible as xenserver. + Match those parameters to your setup. + + .. code-block:: console + + $ OS_XENAPI_URL=https://tarballs.openstack.org/os-xenapi/os-xenapi-0.1.1.tar.gz + $ wget -qO "$OS_XENAPI_TARBALL" "$OS_XENAPI_URL" + $ tar xvf "$OS_XENAPI_TARBALL" -d "$OS_XENAPI_SOURCES" + +#. Copy the plug-ins to the hypervisor: + + .. code-block:: console + + $ PLUGINPATH=$(find $OS_XENAPI_SOURCES -path '*/xapi.d/plugins' -type d -print) + $ tar -czf - -C "$PLUGINPATH" ./ | + > ssh root@xenserver tar -xozf - -C /etc/xapi.d/plugins + +#. Remove temporary files/directories: + + .. code-block:: console + + $ rm "$OS_XENAPI_TARBALL" + $ rm -rf "$OS_XENAPI_SOURCES" + +Prepare for AMI type images +--------------------------- + +To support AMI type images in your OpenStack installation, you must create the +``/boot/guest`` directory on dom0. One of the OpenStack XAPI plugins will +extract the kernel and ramdisk from AKI and ARI images and put them to that +directory. + +OpenStack maintains the contents of this directory and its size should not +increase during normal operation. However, in case of power failures or +accidental shutdowns, some files might be left over. To prevent these files +from filling up dom0's filesystem, set up this directory as a symlink that +points to a subdirectory of the local SR. + +Run these commands in dom0 to achieve this setup: + +.. code-block:: console + + # LOCAL_SR=$(xe sr-list name-label="Local storage" --minimal) + # LOCALPATH="/var/run/sr-mount/$LOCAL_SR/os-guest-kernels" + # mkdir -p "$LOCALPATH" + # ln -s "$LOCALPATH" /boot/guest + +Modify dom0 for resize/migration support +---------------------------------------- + +To resize servers with XenServer you must: + +* Establish a root trust between all hypervisor nodes of your deployment: + + To do so, generate an ssh key-pair with the :command:`ssh-keygen` command. + Ensure that each of your dom0's ``authorized_keys`` file (located in + ``/root/.ssh/authorized_keys``) contains the public key fingerprint (located + in ``/root/.ssh/id_rsa.pub``). + +* Provide a ``/images`` mount point to the dom0 for your hypervisor: + + dom0 space is at a premium so creating a directory in dom0 is potentially + dangerous and likely to fail especially when you resize large servers. The + least you can do is to symlink ``/images`` to your local storage SR. The + following instructions work for an English-based installation of XenServer + and in the case of ext3-based SR (with which the resize functionality is + known to work correctly). + + .. code-block:: console + + # LOCAL_SR=$(xe sr-list name-label="Local storage" --minimal) + # IMG_DIR="/var/run/sr-mount/$LOCAL_SR/images" + # mkdir -p "$IMG_DIR" + # ln -s "$IMG_DIR" /images + +XenAPI configuration reference +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following section discusses some commonly changed options when using the +XenAPI driver. The table below provides a complete reference of all +configuration options available for configuring XAPI with OpenStack. + +The recommended way to use XAPI with OpenStack is through the XenAPI driver. +To enable the XenAPI driver, add the following configuration options to +``/etc/nova/nova.conf`` and restart ``OpenStack Compute``: + +.. code-block:: ini + + compute_driver = xenapi.XenAPIDriver + [xenserver] + connection_url = http://your_xenapi_management_ip_address + connection_username = root + connection_password = your_password + ovs_integration_bridge = br-int + vif_driver = nova.virt.xenapi.vif.XenAPIOpenVswitchDriver + +These connection details are used by OpenStack Compute service to contact your +hypervisor and are the same details you use to connect XenCenter, the XenServer +management console, to your XenServer node. + +.. note:: + + The ``connection_url`` is generally the management network IP + address of the XenServer. + +Networking configuration +------------------------ + +The Networking service in the Compute node is running +``neutron-openvswitch-agent``, this manages dom0's OVS. You can refer +Networking `openvswitch_agent.ini sample`__ for details, +however there are several specific items to look out for. + +__ https://docs.openstack.org/neutron/latest/configuration/samples/openvswitch-agent.html + +.. code-block:: ini + + [agent] + minimize_polling = False + root_helper_daemon = xenapi_root_helper + + [ovs] + of_listen_address = management_ip_address + ovsdb_connection = tcp:your_xenapi_management_ip_address:6640 + bridge_mappings = :, ... + integration_bridge = br-int + + [xenapi] + connection_url = http://your_xenapi_management_ip_address + connection_username = root + connection_password = your_pass_word + +.. note:: + + The ``ovsdb_connection`` is the connection string for the native OVSDB + backend, you need to enable port 6640 in dom0. + +Agent +----- + +The agent is a piece of software that runs on the instances, and communicates +with OpenStack. In case of the XenAPI driver, the agent communicates with +OpenStack through XenStore (see `the Xen Project Wiki +`_ for more information on XenStore). + +If you don't have the guest agent on your VMs, it takes a long time for +OpenStack Compute to detect that the VM has successfully started. Generally a +large timeout is required for Windows instances, but you may want to adjust: +``agent_version_timeout`` within the ``[xenserver]`` section. + +VNC proxy address +----------------- + +Assuming you are talking to XAPI through a management network, and XenServer is +on the address: 10.10.1.34 specify the same address for the vnc proxy address: +``vncserver_proxyclient_address=10.10.1.34`` + +Storage +------- + +You can specify which Storage Repository to use with nova by editing the +following flag. To use the local-storage setup by the default installer: + +.. code-block:: ini + + sr_matching_filter = "other-config:i18n-key=local-storage" + +Another alternative is to use the "default" storage (for example if you have +attached NFS or any other shared storage): + +.. code-block:: ini + + sr_matching_filter = "default-sr:true" + +Image upload in TGZ compressed format +------------------------------------- + +To start uploading ``tgz`` compressed raw disk images to the Image service, +configure ``xenapi_image_upload_handler`` by replacing ``GlanceStore`` with +``VdiThroughDevStore``. + +.. code-block:: ini + + xenapi_image_upload_handler=nova.virt.xenapi.image.vdi_through_dev.VdiThroughDevStore + +As opposed to: + +.. code-block:: ini + + xenapi_image_upload_handler=nova.virt.xenapi.image.glance.GlanceStore diff --git a/doc/source/admin/configuration/hypervisor-xen-libvirt.rst b/doc/source/admin/configuration/hypervisor-xen-libvirt.rst new file mode 100644 index 00000000000..2c28cf03d40 --- /dev/null +++ b/doc/source/admin/configuration/hypervisor-xen-libvirt.rst @@ -0,0 +1,249 @@ +=============== +Xen via libvirt +=============== + +OpenStack Compute supports the Xen Project Hypervisor (or Xen). Xen can be +integrated with OpenStack Compute via the `libvirt `_ +`toolstack `_ or via the `XAPI +`_ `toolstack +`_. This section describes how +to set up OpenStack Compute with Xen and libvirt. For information on how to +set up Xen with XAPI refer to :doc:`hypervisor-xen-api`. + +Installing Xen with libvirt +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At this stage we recommend using the baseline that we use for the `Xen Project +OpenStack CI Loop +`_, which +contains the most recent stability fixes to both Xen and libvirt. + +`Xen 4.5.1 +`_ +(or newer) and `libvirt 1.2.15 `_ (or newer) +contain the minimum required OpenStack improvements for Xen. Although libvirt +1.2.15 works with Xen, libvirt 1.3.2 or newer is recommended. The necessary +Xen changes have also been backported to the Xen 4.4.3 stable branch. Please +check with the Linux and FreeBSD distros you are intending to use as `Dom 0 +`_, whether the relevant +version of Xen and libvirt are available as installable packages. + +The latest releases of Xen and libvirt packages that fulfil the above minimum +requirements for the various openSUSE distributions can always be found and +installed from the `Open Build Service +`_ Virtualization +project. To install these latest packages, add the Virtualization repository +to your software management stack and get the newest packages from there. More +information about the latest Xen and libvirt packages are available `here +`__ and `here +`__. + +Alternatively, it is possible to use the Ubuntu LTS 14.04 Xen Package +**4.4.1-0ubuntu0.14.04.4** (Xen 4.4.1) and apply the patches outlined `here +`__. +You can also use the Ubuntu LTS 14.04 libvirt package **1.2.2 +libvirt_1.2.2-0ubuntu13.1.7** as baseline and update it to libvirt version +1.2.15, or 1.2.14 with the patches outlined `here +`__ +applied. Note that this will require rebuilding these packages partly from +source. + +For further information and latest developments, you may want to consult the +Xen Project's `mailing lists for OpenStack related issues and questions +`_. + +Configuring Xen with libvirt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To enable Xen via libvirt, ensure the following options are set in +``/etc/nova/nova.conf`` on all hosts running the ``nova-compute`` service. + +.. code-block:: ini + + compute_driver = libvirt.LibvirtDriver + + [libvirt] + virt_type = xen + +Additional configuration options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the following as a guideline for configuring Xen for use in OpenStack: + +#. **Dom0 memory**: Set it between 1GB and 4GB by adding the following + parameter to the Xen Boot Options in the `grub.conf `_ file. + + .. code-block:: ini + + dom0_mem=1024M + + .. note:: + + The above memory limits are suggestions and should be based on the + available compute host resources. For large hosts that will run many + hundreds of instances, the suggested values may need to be higher. + + .. note:: + + The location of the grub.conf file depends on the host Linux distribution + that you are using. Please refer to the distro documentation for more + details (see `Dom 0 `_ for more resources). + +#. **Dom0 vcpus**: Set the virtual CPUs to 4 and employ CPU pinning by adding + the following parameters to the Xen Boot Options in the `grub.conf + `_ file. + + .. code-block:: ini + + dom0_max_vcpus=4 dom0_vcpus_pin + + .. note:: + + Note that the above virtual CPU limits are suggestions and should be + based on the available compute host resources. For large hosts, that will + run many hundred of instances, the suggested values may need to be + higher. + +#. **PV vs HVM guests**: A Xen virtual machine can be paravirtualized (PV) or + hardware virtualized (HVM). The virtualization mode determines the + interaction between Xen, Dom 0, and the guest VM's kernel. PV guests are + aware of the fact that they are virtualized and will co-operate with Xen and + Dom 0. The choice of virtualization mode determines performance + characteristics. For an overview of Xen virtualization modes, see `Xen Guest + Types `_. + + In OpenStack, customer VMs may run in either PV or HVM mode. The mode is a + property of the operating system image used by the VM, and is changed by + adjusting the image metadata stored in the Image service. The image + metadata can be changed using the :command:`openstack` commands. + + To choose one of the HVM modes (HVM, HVM with PV Drivers or PVHVM), use + :command:`openstack` to set the ``vm_mode`` property to ``hvm``. + + To choose one of the HVM modes (HVM, HVM with PV Drivers or PVHVM), use one + of the following two commands: + + .. code-block:: console + + $ openstack image set --property vm_mode=hvm IMAGE + + To chose PV mode, which is supported by NetBSD, FreeBSD and Linux, use one + of the following two commands + + .. code-block:: console + + $ openstack image set --property vm_mode=xen IMAGE + + .. note:: + + The default for virtualization mode in nova is PV mode. + +#. **Image formats**: Xen supports raw, qcow2 and vhd image formats. For more + information on image formats, refer to the `OpenStack Virtual Image Guide + `__ and the + `Storage Options Guide on the Xen Project Wiki + `_. + +#. **Image metadata**: In addition to the ``vm_mode`` property discussed above, + the ``hypervisor_type`` property is another important component of the image + metadata, especially if your cloud contains mixed hypervisor compute nodes. + Setting the ``hypervisor_type`` property allows the nova scheduler to select + a compute node running the specified hypervisor when launching instances of + the image. Image metadata such as ``vm_mode``, ``hypervisor_type``, + architecture, and others can be set when importing the image to the Image + service. The metadata can also be changed using the :command:`openstack` + commands: + + .. code-block:: console + + $ openstack image set --property hypervisor_type=xen vm_mode=hvm IMAGE + + For more more information on image metadata, refer to the `OpenStack Virtual + Image Guide `__. + +#. **Libguestfs file injection**: OpenStack compute nodes can use `libguestfs + `_ to inject files into an instance's image prior to + launching the instance. libguestfs uses libvirt's QEMU driver to start a + qemu process, which is then used to inject files into the image. When using + libguestfs for file injection, the compute node must have the libvirt qemu + driver installed, in addition to the Xen driver. In RPM based distributions, + the qemu driver is provided by the ``libvirt-daemon-qemu`` package. In + Debian and Ubuntu, the qemu driver is provided by the ``libvirt-bin`` + package. + +Troubleshoot Xen with libvirt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Important log files**: When an instance fails to start, or when you come +across other issues, you should first consult the following log files: + +* ``/var/log/nova/nova-compute.log`` + +* ``/var/log/libvirt/libxl/libxl-driver.log``, + +* ``/var/log/xen/qemu-dm-${instancename}.log``, + +* ``/var/log/xen/xen-hotplug.log``, + +* ``/var/log/xen/console/guest-${instancename}`` (to enable see `Enabling Guest + Console Logs + `_) + +* Host Console Logs (read `Enabling and Retrieving Host Console Logs + `_). + +If you need further help you can ask questions on the mailing lists `xen-users@ +`_, +`wg-openstack@ `_ or `raise a bug `_ against Xen. + +Known issues +~~~~~~~~~~~~ + +* **Networking**: Xen via libvirt is currently only supported with + nova-network. Fixes for a number of bugs are currently being worked on to + make sure that Xen via libvirt will also work with OpenStack Networking + (neutron). + + .. todo:: Is this still true? + +* **Live migration**: Live migration is supported in the libvirt libxl driver + since version 1.2.5. However, there were a number of issues when used with + OpenStack, in particular with libvirt migration protocol compatibility. It is + worth mentioning that libvirt 1.3.0 addresses most of these issues. We do + however recommend using libvirt 1.3.2, which is fully supported and tested as + part of the Xen Project CI loop. It addresses live migration monitoring + related issues and adds support for peer-to-peer migration mode, which nova + relies on. + +* **Live migration monitoring**: On compute nodes running Kilo or later, live + migration monitoring relies on libvirt APIs that are only implemented from + libvirt version 1.3.1 onwards. When attempting to live migrate, the migration + monitoring thread would crash and leave the instance state as "MIGRATING". If + you experience such an issue and you are running on a version released before + libvirt 1.3.1, make sure you backport libvirt commits ad71665 and b7b4391 + from upstream. + +Additional information and resources +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following section contains links to other useful resources. + +* `wiki.xenproject.org/wiki/OpenStack `_ - OpenStack Documentation on the Xen Project wiki + +* `wiki.xenproject.org/wiki/OpenStack_CI_Loop_for_Xen-Libvirt + `_ - + Information about the Xen Project OpenStack CI Loop + +* `wiki.xenproject.org/wiki/OpenStack_via_DevStack + `_ - How to set up + OpenStack via DevStack + +* `Mailing lists for OpenStack related issues and questions + `_ - This + list is dedicated to coordinating bug fixes and issues across Xen, libvirt + and OpenStack and the CI loop. diff --git a/doc/source/admin/configuration/hypervisors.rst b/doc/source/admin/configuration/hypervisors.rst new file mode 100644 index 00000000000..79b7e3a5df6 --- /dev/null +++ b/doc/source/admin/configuration/hypervisors.rst @@ -0,0 +1,63 @@ +=========== +Hypervisors +=========== + +.. toctree:: + :maxdepth: 1 + + hypervisor-basics.rst + hypervisor-kvm.rst + hypervisor-qemu.rst + hypervisor-xen-api.rst + hypervisor-xen-libvirt.rst + hypervisor-lxc.rst + hypervisor-vmware.rst + hypervisor-hyper-v.rst + hypervisor-virtuozzo.rst + +OpenStack Compute supports many hypervisors, which might make it difficult for +you to choose one. Most installations use only one hypervisor. However, you +can use :ref:`ComputeFilter` and :ref:`ImagePropertiesFilter` to schedule +different hypervisors within the same installation. The following links help +you choose a hypervisor. See :doc:`/user/support-matrix` for a detailed list +of features and support across the hypervisors. + +The following hypervisors are supported: + +* `KVM`_ - Kernel-based Virtual Machine. The virtual disk formats that it + supports is inherited from QEMU since it uses a modified QEMU program to + launch the virtual machine. The supported formats include raw images, the + qcow2, and VMware formats. + +* `LXC`_ - Linux Containers (through libvirt), used to run Linux-based virtual + machines. + +* `QEMU`_ - Quick EMUlator, generally only used for development purposes. + +* `VMware vSphere`_ 5.1.0 and newer - Runs VMware-based Linux and Windows + images through a connection with a vCenter server. + +* `Xen (using libvirt) `_ - Xen Project Hypervisor using libvirt as + management interface into ``nova-compute`` to run Linux, Windows, FreeBSD and + NetBSD virtual machines. + +* `XenServer`_ - XenServer, Xen Cloud Platform (XCP) and other XAPI based Xen + variants runs Linux or Windows virtual machines. You must install the + ``nova-compute`` service in a para-virtualized VM. + +* `Hyper-V`_ - Server virtualization with Microsoft Hyper-V, use to run + Windows, Linux, and FreeBSD virtual machines. Runs ``nova-compute`` natively + on the Windows virtualization platform. + +* `Virtuozzo`_ 7.0.0 and newer - OS Containers and Kernel-based Virtual + Machines supported via libvirt virt_type=parallels. The supported formats + include ploop and qcow2 images. + +.. _KVM: http://www.linux-kvm.org/page/Main_Page +.. _LXC: https://linuxcontainers.org/ +.. _QEMU: http://wiki.qemu.org/Manual +.. _VMware vSphere: https://www.vmware.com/support/vsphere-hypervisor +.. _Xen: (using libvirt) ,,, + iser iser,,,, + bnx2i.00:05:b5:d2:a0:c2 bnx2i,00:05:b5:d2:a0:c2,5.10.10.20,, + + The output is in the format:: + + iface_name transport_name,hwaddress,ipaddress,net_ifacename,initiatorname + +* Individual iface configuration can be viewed via + + .. code-block:: console + + # iscsiadm -m iface -I IFACE_NAME + # BEGIN RECORD 2.0-873 + iface.iscsi_ifacename = cxgb4i.00:07:43:28:b2:58 + iface.net_ifacename = + iface.ipaddress = 102.50.50.80 + iface.hwaddress = 00:07:43:28:b2:58 + iface.transport_name = cxgb4i + iface.initiatorname = + # END RECORD + + Configuration can be updated as desired via + + .. code-block:: console + + # iscsiadm -m iface-I IFACE_NAME--op=update -n iface.SETTING -v VALUE + +* All iface configurations need a minimum of ``iface.iface_name``, + ``iface.transport_name`` and ``iface.hwaddress`` to be correctly configured + to work. Some transports may require ``iface.ipaddress`` and + ``iface.net_ifacename`` as well to bind correctly. + + Detailed configuration instructions can be found at + http://www.open-iscsi.org/docs/README. diff --git a/doc/source/admin/configuration/logs.rst b/doc/source/admin/configuration/logs.rst new file mode 100644 index 00000000000..396eab9b88c --- /dev/null +++ b/doc/source/admin/configuration/logs.rst @@ -0,0 +1,47 @@ +================= +Compute log files +================= + +The corresponding log file of each Compute service is stored in the +``/var/log/nova/`` directory of the host on which each service runs. + +.. list-table:: Log files used by Compute services + :widths: 35 35 30 + :header-rows: 1 + + * - Log file + - Service name (CentOS/Fedora/openSUSE/Red Hat Enterprise + Linux/SUSE Linux Enterprise) + - Service name (Ubuntu/Debian) + * - ``nova-api.log`` + - ``openstack-nova-api`` + - ``nova-api`` + * - ``nova-cert.log`` [#a]_ + - ``openstack-nova-cert`` + - ``nova-cert`` + * - ``nova-compute.log`` + - ``openstack-nova-compute`` + - ``nova-compute`` + * - ``nova-conductor.log`` + - ``openstack-nova-conductor`` + - ``nova-conductor`` + * - ``nova-consoleauth.log`` + - ``openstack-nova-consoleauth`` + - ``nova-consoleauth`` + * - ``nova-network.log`` [#b]_ + - ``openstack-nova-network`` + - ``nova-network`` + * - ``nova-manage.log`` + - ``nova-manage`` + - ``nova-manage`` + * - ``nova-scheduler.log`` + - ``openstack-nova-scheduler`` + - ``nova-scheduler`` + +.. rubric:: Footnotes + +.. [#a] The X509 certificate service (``openstack-nova-cert``/``nova-cert``) + is only required by the EC2 API to the Compute service. +.. [#b] The ``nova`` network service (``openstack-nova-network``/ + ``nova-network``) only runs in deployments that are not configured + to use the Networking service (``neutron``). diff --git a/doc/source/admin/configuration/resize.rst b/doc/source/admin/configuration/resize.rst new file mode 100644 index 00000000000..fd787af6491 --- /dev/null +++ b/doc/source/admin/configuration/resize.rst @@ -0,0 +1,26 @@ +================ +Configure resize +================ + +Resize (or Server resize) is the ability to change the flavor of a server, thus +allowing it to upscale or downscale according to user needs. For this feature +to work properly, you might need to configure some underlying virt layers. + +KVM +~~~ + +Resize on KVM is implemented currently by transferring the images between +compute nodes over ssh. For KVM you need hostnames to resolve properly and +passwordless ssh access between your compute hosts. Direct access from one +compute host to another is needed to copy the VM file across. + +Cloud end users can find out how to resize a server by reading the `OpenStack +End User Guide `_. + +XenServer +~~~~~~~~~ + +To get resize to work with XenServer (and XCP), you need to establish a root +trust between all hypervisor nodes and provide an ``/image`` mount point to +your hypervisors dom0. diff --git a/doc/source/admin/configuration/samples/api-paste.ini.rst b/doc/source/admin/configuration/samples/api-paste.ini.rst new file mode 100644 index 00000000000..b702904eef1 --- /dev/null +++ b/doc/source/admin/configuration/samples/api-paste.ini.rst @@ -0,0 +1,8 @@ +============= +api-paste.ini +============= + +The Compute service stores its API configuration settings in the +``api-paste.ini`` file. + +.. literalinclude:: /../../etc/nova/api-paste.ini diff --git a/doc/source/admin/configuration/samples/index.rst b/doc/source/admin/configuration/samples/index.rst new file mode 100644 index 00000000000..6db5a16a488 --- /dev/null +++ b/doc/source/admin/configuration/samples/index.rst @@ -0,0 +1,12 @@ +========================================== +Compute service sample configuration files +========================================== + +Files in this section can be found in ``/etc/nova``. + +.. toctree:: + :maxdepth: 2 + + api-paste.ini.rst + policy.yaml.rst + rootwrap.conf.rst diff --git a/doc/source/admin/configuration/samples/policy.yaml.rst b/doc/source/admin/configuration/samples/policy.yaml.rst new file mode 100644 index 00000000000..7c195a9bb3d --- /dev/null +++ b/doc/source/admin/configuration/samples/policy.yaml.rst @@ -0,0 +1,9 @@ +=========== +policy.yaml +=========== + +The ``policy.yaml`` file defines additional access controls +that apply to the Compute service. + +.. literalinclude:: /_static/nova.policy.yaml.sample + :language: yaml diff --git a/doc/source/admin/configuration/samples/rootwrap.conf.rst b/doc/source/admin/configuration/samples/rootwrap.conf.rst new file mode 100644 index 00000000000..82975b82491 --- /dev/null +++ b/doc/source/admin/configuration/samples/rootwrap.conf.rst @@ -0,0 +1,13 @@ +============= +rootwrap.conf +============= + +The ``rootwrap.conf`` file defines configuration values +used by the rootwrap script when the Compute service needs +to escalate its privileges to those of the root user. + +It is also possible to disable the root wrapper, and default +to sudo only. Configure the ``disable_rootwrap`` option in the +``[workaround]`` section of the ``nova.conf`` configuration file. + +.. literalinclude:: /../../etc/nova/rootwrap.conf diff --git a/doc/source/admin/configuration/schedulers.rst b/doc/source/admin/configuration/schedulers.rst new file mode 100644 index 00000000000..d8d42306004 --- /dev/null +++ b/doc/source/admin/configuration/schedulers.rst @@ -0,0 +1,1199 @@ +================== +Compute schedulers +================== + +Compute uses the ``nova-scheduler`` service to determine how to dispatch +compute requests. For example, the ``nova-scheduler`` service determines on +which host a VM should launch. In the context of filters, the term ``host`` +means a physical node that has a ``nova-compute`` service running on it. You +can configure the scheduler through a variety of options. + +Compute is configured with the following default scheduler options in the +``/etc/nova/nova.conf`` file: + +.. code-block:: ini + + scheduler_driver_task_period = 60 + scheduler_driver = nova.scheduler.filter_scheduler.FilterScheduler + scheduler_available_filters = nova.scheduler.filters.all_filters + scheduler_default_filters = RetryFilter, AvailabilityZoneFilter, RamFilter, DiskFilter, ComputeFilter, ComputeCapabilitiesFilter, ImagePropertiesFilter, ServerGroupAntiAffinityFilter, ServerGroupAffinityFilter + +By default, the ``scheduler_driver`` is configured as a filter scheduler, as +described in the next section. In the default configuration, this scheduler +considers hosts that meet all the following criteria: + +* Have not been attempted for scheduling purposes (``RetryFilter``). + +* Are in the requested availability zone (``AvailabilityZoneFilter``). + +* Have sufficient RAM available (``RamFilter``). + +* Have sufficient disk space available for root and ephemeral storage + (``DiskFilter``). + +* Can service the request (``ComputeFilter``). + +* Satisfy the extra specs associated with the instance type + (``ComputeCapabilitiesFilter``). + +* Satisfy any architecture, hypervisor type, or virtual machine mode properties + specified on the instance's image properties (``ImagePropertiesFilter``). + +* Are on a different host than other instances of a group (if requested) + (``ServerGroupAntiAffinityFilter``). + +* Are in a set of group hosts (if requested) (``ServerGroupAffinityFilter``). + +The scheduler caches its list of available hosts; use the +``scheduler_driver_task_period`` option to specify how often the list is +updated. + +.. note:: + + Do not configure ``service_down_time`` to be much smaller than + ``scheduler_driver_task_period``; otherwise, hosts appear to be dead while + the host list is being cached. + +For information about the volume scheduler, see the `Block Storage section +`_ of +OpenStack Administrator Guide. + +The scheduler chooses a new host when an instance is migrated. + +When evacuating instances from a host, the scheduler service honors the target +host defined by the administrator on the :command:`nova evacuate` command. If +a target is not defined by the administrator, the scheduler determines the +target host. For information about instance evacuation, see `Evacuate instances +`_ section of the OpenStack +Administrator Guide. + +.. _compute-scheduler-filters: + +Filter scheduler +~~~~~~~~~~~~~~~~ + +The filter scheduler (``nova.scheduler.filter_scheduler.FilterScheduler``) is +the default scheduler for scheduling virtual machine instances. It supports +filtering and weighting to make informed decisions on where a new instance +should be created. + +When the filter scheduler receives a request for a resource, it first applies +filters to determine which hosts are eligible for consideration when +dispatching a resource. Filters are binary: either a host is accepted by the +filter, or it is rejected. Hosts that are accepted by the filter are then +processed by a different algorithm to decide which hosts to use for that +request, described in the :ref:`weights` section. + +**Filtering** + +.. figure:: /figures/filteringWorkflow1.png + +The ``scheduler_available_filters`` configuration option in ``nova.conf`` +provides the Compute service with the list of the filters that are used by the +scheduler. The default setting specifies all of the filter that are included +with the Compute service: + +.. code-block:: ini + + scheduler_available_filters = nova.scheduler.filters.all_filters + +This configuration option can be specified multiple times. For example, if you +implemented your own custom filter in Python called ``myfilter.MyFilter`` and +you wanted to use both the built-in filters and your custom filter, your +``nova.conf`` file would contain: + +.. code-block:: ini + + scheduler_available_filters = nova.scheduler.filters.all_filters + scheduler_available_filters = myfilter.MyFilter + +The ``scheduler_default_filters`` configuration option in ``nova.conf`` defines +the list of filters that are applied by the ``nova-scheduler`` service. The +default filters are: + +.. code-block:: ini + + scheduler_default_filters = RetryFilter, AvailabilityZoneFilter, RamFilter, ComputeFilter, ComputeCapabilitiesFilter, ImagePropertiesFilter, ServerGroupAntiAffinityFilter, ServerGroupAffinityFilter + +Compute filters +~~~~~~~~~~~~~~~ + +The following sections describe the available compute filters. + +AggregateCoreFilter +------------------- + +Filters host by CPU core numbers with a per-aggregate ``cpu_allocation_ratio`` +value. If the per-aggregate value is not found, the value falls back to the +global setting. If the host is in more than one aggregate and more than one +value is found, the minimum value will be used. For information about how to +use this filter, see :ref:`host-aggregates`. See also :ref:`CoreFilter`. + +AggregateDiskFilter +------------------- + +Filters host by disk allocation with a per-aggregate ``disk_allocation_ratio`` +value. If the per-aggregate value is not found, the value falls back to the +global setting. If the host is in more than one aggregate and more than one +value is found, the minimum value will be used. For information about how to +use this filter, see :ref:`host-aggregates`. See also :ref:`DiskFilter`. + +AggregateImagePropertiesIsolation +--------------------------------- + +Matches properties defined in an image's metadata against those of aggregates +to determine host matches: + +* If a host belongs to an aggregate and the aggregate defines one or more + metadata that matches an image's properties, that host is a candidate to boot + the image's instance. + +* If a host does not belong to any aggregate, it can boot instances from all + images. + +For example, the following aggregate ``myWinAgg`` has the Windows operating +system as metadata (named 'windows'): + +.. code-block:: console + + $ openstack aggregate show MyWinAgg + +-------------------+----------------------------+ + | Field | Value | + +-------------------+----------------------------+ + | availability_zone | zone1 | + | created_at | 2017-01-01T15:36:44.000000 | + | deleted | False | + | deleted_at | None | + | hosts | [u'sf-devel'] | + | id | 1 | + | name | test | + | properties | | + | updated_at | None | + +-------------------+----------------------------+ + +In this example, because the following Win-2012 image has the ``windows`` +property, it boots on the ``sf-devel`` host (all other filters being equal): + +.. code-block:: console + + $ openstack image show Win-2012 + +------------------+------------------------------------------------------+ + | Field | Value | + +------------------+------------------------------------------------------+ + | checksum | ee1eca47dc88f4879d8a229cc70a07c6 | + | container_format | bare | + | created_at | 2016-12-13T09:30:30Z | + | disk_format | qcow2 | + | ... + +You can configure the ``AggregateImagePropertiesIsolation`` filter by using the +following options in the ``nova.conf`` file: + +.. code-block:: ini + + # Considers only keys matching the given namespace (string). + # Multiple values can be given, as a comma-separated list. + aggregate_image_properties_isolation_namespace = + + # Separator used between the namespace and keys (string). + aggregate_image_properties_isolation_separator = . + +.. _AggregateInstanceExtraSpecsFilter: + +AggregateInstanceExtraSpecsFilter +--------------------------------- + +Matches properties defined in extra specs for an instance type against +admin-defined properties on a host aggregate. Works with specifications that +are scoped with ``aggregate_instance_extra_specs``. Multiple values can be +given, as a comma-separated list. For backward compatibility, also works with +non-scoped specifications; this action is highly discouraged because it +conflicts with :ref:`ComputeCapabilitiesFilter` filter when you enable both +filters. For information about how to use this filter, see the +:ref:`host-aggregates` section. + +AggregateIoOpsFilter +-------------------- + +Filters host by disk allocation with a per-aggregate ``max_io_ops_per_host`` +value. If the per-aggregate value is not found, the value falls back to the +global setting. If the host is in more than one aggregate and more than one +value is found, the minimum value will be used. For information about how to +use this filter, see :ref:`host-aggregates`. See also :ref:`IoOpsFilter`. + +AggregateMultiTenancyIsolation +------------------------------ + +Ensures that the tenant (or list of tenants) creates all instances only on +specific :ref:`host-aggregates`. If a host is in an aggregate that has the +``filter_tenant_id`` metadata key, the host creates instances from only that +tenant or list of tenants. A host can be in different aggregates. If a host +does not belong to an aggregate with the metadata key, the host can create +instances from all tenants. This setting does not isolate the aggregate from +other tenants. Any other tenant can continue to build instances on the +specified aggregate. + +AggregateNumInstancesFilter +--------------------------- + +Filters host by number of instances with a per-aggregate +``max_instances_per_host`` value. If the per-aggregate value is not found, the +value falls back to the global setting. If the host is in more than one +aggregate and thus more than one value is found, the minimum value will be +used. For information about how to use this filter, see +:ref:`host-aggregates`. See also :ref:`NumInstancesFilter`. + +AggregateRamFilter +------------------ + +Filters host by RAM allocation of instances with a per-aggregate +``ram_allocation_ratio`` value. If the per-aggregate value is not found, the +value falls back to the global setting. If the host is in more than one +aggregate and thus more than one value is found, the minimum value will be +used. For information about how to use this filter, see +:ref:`host-aggregates`. See also :ref:`ramfilter`. + +AggregateTypeAffinityFilter +--------------------------- + +This filter passes hosts if no ``instance_type`` key is set or the +``instance_type`` aggregate metadata value contains the name of the +``instance_type`` requested. The value of the ``instance_type`` metadata entry +is a string that may contain either a single ``instance_type`` name or a +comma-separated list of ``instance_type`` names, such as ``m1.nano`` or +``m1.nano,m1.small``. For information about how to use this filter, see +:ref:`host-aggregates`. See also :ref:`TypeAffinityFilter`. + +AllHostsFilter +-------------- + +This is a no-op filter. It does not eliminate any of the available hosts. + +AvailabilityZoneFilter +---------------------- + +Filters hosts by availability zone. You must enable this filter for the +scheduler to respect availability zones in requests. + +.. _ComputeCapabilitiesFilter: + +ComputeCapabilitiesFilter +------------------------- + +Matches properties defined in extra specs for an instance type against compute +capabilities. If an extra specs key contains a colon (``:``), anything before +the colon is treated as a namespace and anything after the colon is treated as +the key to be matched. If a namespace is present and is not ``capabilities``, +the filter ignores the namespace. For backward compatibility, also treats the +extra specs key as the key to be matched if no namespace is present; this +action is highly discouraged because it conflicts with +:ref:`AggregateInstanceExtraSpecsFilter` filter when you enable both filters. + +.. _ComputeFilter: + +ComputeFilter +------------- + +Passes all hosts that are operational and enabled. + +In general, you should always enable this filter. + +.. _CoreFilter: + +CoreFilter +---------- + +Only schedules instances on hosts if sufficient CPU cores are available. If +this filter is not set, the scheduler might over-provision a host based on +cores. For example, the virtual cores running on an instance may exceed the +physical cores. + +You can configure this filter to enable a fixed amount of vCPU overcommitment +by using the ``cpu_allocation_ratio`` configuration option in ``nova.conf``. +The default setting is: + +.. code-block:: ini + + cpu_allocation_ratio = 16.0 + +With this setting, if 8 vCPUs are on a node, the scheduler allows instances up +to 128 vCPU to be run on that node. + +To disallow vCPU overcommitment set: + +.. code-block:: ini + + cpu_allocation_ratio = 1.0 + +.. note:: + + The Compute API always returns the actual number of CPU cores available on a + compute node regardless of the value of the ``cpu_allocation_ratio`` + configuration key. As a result changes to the ``cpu_allocation_ratio`` are + not reflected via the command line clients or the dashboard. Changes to + this configuration key are only taken into account internally in the + scheduler. + +DifferentHostFilter +------------------- + +Schedules the instance on a different host from a set of instances. To take +advantage of this filter, the requester must pass a scheduler hint, using +``different_host`` as the key and a list of instance UUIDs as the value. This +filter is the opposite of the ``SameHostFilter``. Using the +:command:`openstack server create` command, use the ``--hint`` flag. For +example: + +.. code-block:: console + + $ openstack server create --image cedef40a-ed67-4d10-800e-17455edce175 \ + --flavor 1 --hint different_host=a0cf03a5-d921-4877-bb5c-86d26cf818e1 \ + --hint different_host=8c19174f-4220-44f0-824a-cd1eeef10287 server-1 + +With the API, use the ``os:scheduler_hints`` key. For example: + +.. code-block:: json + + { + "server": { + "name": "server-1", + "imageRef": "cedef40a-ed67-4d10-800e-17455edce175", + "flavorRef": "1" + }, + "os:scheduler_hints": { + "different_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ] + } + } + +.. _DiskFilter: + +DiskFilter +---------- + +Only schedules instances on hosts if there is sufficient disk space available +for root and ephemeral storage. + +You can configure this filter to enable a fixed amount of disk overcommitment +by using the ``disk_allocation_ratio`` configuration option in the +``nova.conf`` configuration file. The default setting disables the possibility +of the overcommitment and allows launching a VM only if there is a sufficient +amount of disk space available on a host: + +.. code-block:: ini + + disk_allocation_ratio = 1.0 + +DiskFilter always considers the value of the ``disk_available_least`` property +and not the one of the ``free_disk_gb`` property of a hypervisor's statistics: + +.. code-block:: console + + $ openstack hypervisor stats show + +----------------------+-------+ + | Field | Value | + +----------------------+-------+ + | count | 1 | + | current_workload | 0 | + | disk_available_least | 14 | + | free_disk_gb | 27 | + | free_ram_mb | 15374 | + | local_gb | 27 | + | local_gb_used | 0 | + | memory_mb | 15886 | + | memory_mb_used | 512 | + | running_vms | 0 | + | vcpus | 8 | + | vcpus_used | 0 | + +----------------------+-------+ + +As it can be viewed from the command output above, the amount of the available +disk space can be less than the amount of the free disk space. It happens +because the ``disk_available_least`` property accounts for the virtual size +rather than the actual size of images. If you use an image format that is +sparse or copy on write so that each virtual instance does not require a 1:1 +allocation of a virtual disk to a physical storage, it may be useful to allow +the overcommitment of disk space. + +To enable scheduling instances while overcommitting disk resources on the node, +adjust the value of the ``disk_allocation_ratio`` configuration option to +greater than ``1.0``: + +.. code-block:: none + + disk_allocation_ratio > 1.0 + +.. note:: + + If the value is set to ``>1``, we recommend keeping track of the free disk + space, as the value approaching ``0`` may result in the incorrect + functioning of instances using it at the moment. + +ExactCoreFilter +--------------- + +Only schedules instances on hosts if host has the exact number of CPU cores. + +ExactDiskFilter +--------------- + +Only schedules instances on hosts if host has the exact amount of disk +available. + +ExactRamFilter +-------------- + +Only schedules instances on hosts if host has the exact number of RAM +available. + +.. _ImagePropertiesFilter: + +ImagePropertiesFilter +--------------------- + +Filters hosts based on properties defined on the instance's image. It passes +hosts that can support the specified image properties contained in the +instance. Properties include the architecture, hypervisor type, hypervisor +version (for Xen hypervisor type only), and virtual machine mode. + +For example, an instance might require a host that runs an ARM-based processor, +and QEMU as the hypervisor. You can decorate an image with these properties by +using: + +.. code-block:: console + + $ openstack image set --architecture arm --property hypervisor_type=qemu \ + img-uuid + +The image properties that the filter checks for are: + +``architecture`` + describes the machine architecture required by the image. Examples are + ``i686``, ``x86_64``, ``arm``, and ``ppc64``. + +``hypervisor_type`` + describes the hypervisor required by the image. Examples are ``xen``, + ``qemu``, and ``xenapi``. + + .. note:: + + ``qemu`` is used for both QEMU and KVM hypervisor types. + +``hypervisor_version_requires`` + describes the hypervisor version required by the image. The property is + supported for Xen hypervisor type only. It can be used to enable support for + multiple hypervisor versions, and to prevent instances with newer Xen tools + from being provisioned on an older version of a hypervisor. If available, the + property value is compared to the hypervisor version of the compute host. + + To filter the hosts by the hypervisor version, add the + ``hypervisor_version_requires`` property on the image as metadata and pass an + operator and a required hypervisor version as its value: + + .. code-block:: console + + $ openstack image set --property hypervisor_type=xen --property \ + hypervisor_version_requires=">=4.3" img-uuid + +``vm_mode`` + describes the hypervisor application binary interface (ABI) required by the + image. Examples are ``xen`` for Xen 3.0 paravirtual ABI, ``hvm`` for native + ABI, ``uml`` for User Mode Linux paravirtual ABI, ``exe`` for container virt + executable ABI. + +IsolatedHostsFilter +------------------- + +Allows the admin to define a special (isolated) set of images and a special +(isolated) set of hosts, such that the isolated images can only run on the +isolated hosts, and the isolated hosts can only run isolated images. The flag +``restrict_isolated_hosts_to_isolated_images`` can be used to force isolated +hosts to only run isolated images. + +The admin must specify the isolated set of images and hosts in the +``nova.conf`` file using the ``isolated_hosts`` and ``isolated_images`` +configuration options. For example: + +.. code-block:: ini + + [filter_scheduler] + isolated_hosts = server1, server2 + isolated_images = 342b492c-128f-4a42-8d3a-c5088cf27d13, ebd267a6-ca86-4d6c-9a0e-bd132d6b7d09 + +.. _IoOpsFilter: + +IoOpsFilter +----------- + +The IoOpsFilter filters hosts by concurrent I/O operations on it. Hosts with +too many concurrent I/O operations will be filtered out. The +``max_io_ops_per_host`` option specifies the maximum number of I/O intensive +instances allowed to run on a host. A host will be ignored by the scheduler if +more than ``max_io_ops_per_host`` instances in build, resize, snapshot, +migrate, rescue or unshelve task states are running on it. + +JsonFilter +---------- + +The JsonFilter allows a user to construct a custom filter by passing a +scheduler hint in JSON format. The following operators are supported: + +* = +* < +* > +* in +* <= +* >= +* not +* or +* and + +The filter supports the following variables: + +* ``$free_ram_mb`` +* ``$free_disk_mb`` +* ``$total_usable_ram_mb`` +* ``$vcpus_total`` +* ``$vcpus_used`` + +Using the :command:`openstack server create` command, use the ``--hint`` flag: + +.. code-block:: console + + $ openstack server create --image 827d564a-e636-4fc4-a376-d36f7ebe1747 \ + --flavor 1 --hint query='[">=","$free_ram_mb",1024]' server1 + +With the API, use the ``os:scheduler_hints`` key: + +.. code-block:: json + + { + "server": { + "name": "server-1", + "imageRef": "cedef40a-ed67-4d10-800e-17455edce175", + "flavorRef": "1" + }, + "os:scheduler_hints": { + "query": "[>=,$free_ram_mb,1024]" + } + } + +MetricsFilter +------------- + +Filters hosts based on meters ``weight_setting``. Only hosts with the +available meters are passed so that the metrics weigher will not fail due to +these hosts. + +NUMATopologyFilter +------------------ + +Filters hosts based on the NUMA topology that was specified for the instance +through the use of flavor ``extra_specs`` in combination with the image +properties, as described in detail in the `related nova-spec document +`_. Filter +will try to match the exact NUMA cells of the instance to those of the host. It +will consider the standard over-subscription limits each cell, and provide +limits to the compute host accordingly. + +.. note:: + + If instance has no topology defined, it will be considered for any host. If + instance has a topology defined, it will be considered only for NUMA capable + hosts. + +.. _NumInstancesFilter: + +NumInstancesFilter +------------------ + +Hosts that have more instances running than specified by the +``max_instances_per_host`` option are filtered out when this filter is in +place. + +PciPassthroughFilter +-------------------- + +The filter schedules instances on a host if the host has devices that meet the +device requests in the ``extra_specs`` attribute for the flavor. + +.. _RamFilter: + +RamFilter +--------- + +Only schedules instances on hosts that have sufficient RAM available. If this +filter is not set, the scheduler may over provision a host based on RAM (for +example, the RAM allocated by virtual machine instances may exceed the physical +RAM). + +You can configure this filter to enable a fixed amount of RAM overcommitment by +using the ``ram_allocation_ratio`` configuration option in ``nova.conf``. The +default setting is: + +.. code-block:: ini + + ram_allocation_ratio = 1.5 + +This setting enables 1.5 GB instances to run on any compute node with 1 GB of +free RAM. + +RetryFilter +----------- + +Filters out hosts that have already been attempted for scheduling purposes. If +the scheduler selects a host to respond to a service request, and the host +fails to respond to the request, this filter prevents the scheduler from +retrying that host for the service request. + +This filter is only useful if the ``scheduler_max_attempts`` configuration +option is set to a value greater than zero. + +SameHostFilter +-------------- + +Schedules the instance on the same host as another instance in a set of +instances. To take advantage of this filter, the requester must pass a +scheduler hint, using ``same_host`` as the key and a list of instance UUIDs as +the value. This filter is the opposite of the ``DifferentHostFilter``. Using +the :command:`openstack server create` command, use the ``--hint`` flag: + +.. code-block:: console + + $ openstack server create --image cedef40a-ed67-4d10-800e-17455edce175 \ + --flavor 1 --hint same_host=a0cf03a5-d921-4877-bb5c-86d26cf818e1 \ + --hint same_host=8c19174f-4220-44f0-824a-cd1eeef10287 server-1 + +With the API, use the ``os:scheduler_hints`` key: + +.. code-block:: json + + { + "server": { + "name": "server-1", + "imageRef": "cedef40a-ed67-4d10-800e-17455edce175", + "flavorRef": "1" + }, + "os:scheduler_hints": { + "same_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ] + } + } + +.. _ServerGroupAffinityFilter: + +ServerGroupAffinityFilter +------------------------- + +The ServerGroupAffinityFilter ensures that an instance is scheduled on to a +host from a set of group hosts. To take advantage of this filter, the requester +must create a server group with an ``affinity`` policy, and pass a scheduler +hint, using ``group`` as the key and the server group UUID as the value. Using +the :command:`openstack server create` command, use the ``--hint`` flag. For +example: + +.. code-block:: console + + $ openstack server group create --policy affinity group-1 + $ openstack server create --image IMAGE_ID --flavor 1 \ + --hint group=SERVER_GROUP_UUID server-1 + +.. _ServerGroupAntiAffinityFilter: + +ServerGroupAntiAffinityFilter +----------------------------- + +The ServerGroupAntiAffinityFilter ensures that each instance in a group is on a +different host. To take advantage of this filter, the requester must create a +server group with an ``anti-affinity`` policy, and pass a scheduler hint, using +``group`` as the key and the server group UUID as the value. Using the +:command:`openstack server create` command, use the ``--hint`` flag. For +example: + +.. code-block:: console + + $ openstack server group create --policy anti-affinity group-1 + $ openstack server create --image IMAGE_ID --flavor 1 \ + --hint group=SERVER_GROUP_UUID server-1 + +SimpleCIDRAffinityFilter +------------------------ + +Schedules the instance based on host IP subnet range. To take advantage of +this filter, the requester must specify a range of valid IP address in CIDR +format, by passing two scheduler hints: + +``build_near_host_ip`` + The first IP address in the subnet (for example, ``192.168.1.1``) + +``cidr`` + The CIDR that corresponds to the subnet (for example, ``/24``) + +Using the :command:`openstack server create` command, use the ``--hint`` flag. +For example, to specify the IP subnet ``192.168.1.1/24``: + +.. code-block:: console + + $ openstack server create --image cedef40a-ed67-4d10-800e-17455edce175 \ + --flavor 1 --hint build_near_host_ip=192.168.1.1 --hint cidr=/24 server-1 + +With the API, use the ``os:scheduler_hints`` key: + +.. code-block:: json + + { + "server": { + "name": "server-1", + "imageRef": "cedef40a-ed67-4d10-800e-17455edce175", + "flavorRef": "1" + }, + "os:scheduler_hints": { + "build_near_host_ip": "192.168.1.1", + "cidr": "24" + } + } + +TrustedFilter +------------- + +Filters hosts based on their trust. Only passes hosts that meet the trust +requirements specified in the instance properties. + +.. _TypeAffinityFilter: + +TypeAffinityFilter +------------------ + +Dynamically limits hosts to one instance type. An instance can only be launched +on a host, if no instance with different instances types are running on it, or +if the host has no running instances at all. + +Cell filters +~~~~~~~~~~~~ + +The following sections describe the available cell filters. + +DifferentCellFilter +------------------- + +Schedules the instance on a different cell from a set of instances. To take +advantage of this filter, the requester must pass a scheduler hint, using +``different_cell`` as the key and a list of instance UUIDs as the value. + +ImagePropertiesFilter +--------------------- + +Filters cells based on properties defined on the instance’s image. This +filter works specifying the hypervisor required in the image metadata and the +supported hypervisor version in cell capabilities. + +TargetCellFilter +---------------- + +Filters target cells. This filter works by specifying a scheduler hint of +``target_cell``. The value should be the full cell path. + +.. _weights: + +Weights +~~~~~~~ + +When resourcing instances, the filter scheduler filters and weights each host +in the list of acceptable hosts. Each time the scheduler selects a host, it +virtually consumes resources on it, and subsequent selections are adjusted +accordingly. This process is useful when the customer asks for the same large +amount of instances, because weight is computed for each requested instance. + +All weights are normalized before being summed up; the host with the largest +weight is given the highest priority. + +**Weighting hosts** + +.. figure:: /figures/nova-weighting-hosts.png + +If cells are used, cells are weighted by the scheduler in the same manner as +hosts. + +Hosts and cells are weighted based on the following options in the +``/etc/nova/nova.conf`` file: + +.. list-table:: Host weighting options + :header-rows: 1 + :widths: 10, 25, 60 + + * - Section + - Option + - Description + * - [DEFAULT] + - ``ram_weight_multiplier`` + - By default, the scheduler spreads instances across all hosts evenly. + Set the ``ram_weight_multiplier`` option to a negative number if you + prefer stacking instead of spreading. Use a floating-point value. + * - [DEFAULT] + - ``scheduler_host_subset_size`` + - New instances are scheduled on a host that is chosen randomly from a + subset of the N best hosts. This property defines the subset size from + which a host is chosen. A value of 1 chooses the first host returned by + the weighting functions. This value must be at least 1. A value less + than 1 is ignored, and 1 is used instead. Use an integer value. + * - [DEFAULT] + - ``scheduler_weight_classes`` + - Defaults to ``nova.scheduler.weights.all_weighers``. Hosts are then + weighted and sorted with the largest weight winning. + * - [DEFAULT] + - ``io_ops_weight_multiplier`` + - Multiplier used for weighing host I/O operations. A negative value means + a preference to choose light workload compute hosts. + * - [DEFAULT] + - ``soft_affinity_weight_multiplier`` + - Multiplier used for weighing hosts for group soft-affinity. Only a + positive value is meaningful. Negative means that the behavior will + change to the opposite, which is soft-anti-affinity. + * - [DEFAULT] + - ``soft_anti_affinity_weight_multiplier`` + - Multiplier used for weighing hosts for group soft-anti-affinity. Only a + positive value is meaningful. Negative means that the behavior will + change to the opposite, which is soft-affinity. + * - [metrics] + - ``weight_multiplier`` + - Multiplier for weighting meters. Use a floating-point value. + * - [metrics] + - ``weight_setting`` + - Determines how meters are weighted. Use a comma-separated list of + metricName=ratio. For example: ``name1=1.0, name2=-1.0`` results in: + ``name1.value * 1.0 + name2.value * -1.0`` + * - [metrics] + - ``required`` + - Specifies how to treat unavailable meters: + + * True - Raises an exception. To avoid the raised exception, you should + use the scheduler filter ``MetricFilter`` to filter out hosts with + unavailable meters. + * False - Treated as a negative factor in the weighting process (uses + the ``weight_of_unavailable`` option). + * - [metrics] + - ``weight_of_unavailable`` + - If ``required`` is set to False, and any one of the meters set by + ``weight_setting`` is unavailable, the ``weight_of_unavailable`` value + is returned to the scheduler. + +For example: + +.. code-block:: ini + + [DEFAULT] + scheduler_host_subset_size = 1 + scheduler_weight_classes = nova.scheduler.weights.all_weighers + ram_weight_multiplier = 1.0 + io_ops_weight_multiplier = 2.0 + soft_affinity_weight_multiplier = 1.0 + soft_anti_affinity_weight_multiplier = 1.0 + [metrics] + weight_multiplier = 1.0 + weight_setting = name1=1.0, name2=-1.0 + required = false + weight_of_unavailable = -10000.0 + +.. list-table:: Cell weighting options + :header-rows: 1 + :widths: 10, 25, 60 + + * - Section + - Option + - Description + * - [cells] + - ``mute_weight_multiplier`` + - Multiplier to weight mute children (hosts which have not sent + capacity or capacity updates for some time). + Use a negative, floating-point value. + * - [cells] + - ``offset_weight_multiplier`` + - Multiplier to weight cells, so you can specify a preferred cell. + Use a floating point value. + * - [cells] + - ``ram_weight_multiplier`` + - By default, the scheduler spreads instances across all cells evenly. + Set the ``ram_weight_multiplier`` option to a negative number if you + prefer stacking instead of spreading. Use a floating-point value. + * - [cells] + - ``scheduler_weight_classes`` + - Defaults to ``nova.cells.weights.all_weighers``, which maps to all + cell weighers included with Compute. Cells are then weighted and + sorted with the largest weight winning. + +For example: + +.. code-block:: ini + + [cells] + scheduler_weight_classes = nova.cells.weights.all_weighers + mute_weight_multiplier = -10.0 + ram_weight_multiplier = 1.0 + offset_weight_multiplier = 1.0 + +Chance scheduler +~~~~~~~~~~~~~~~~ + +As an administrator, you work with the filter scheduler. However, the Compute +service also uses the Chance Scheduler, +``nova.scheduler.chance.ChanceScheduler``, which randomly selects from lists of +filtered hosts. + +Utilization aware scheduling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to schedule VMs using advanced scheduling decisions. These +decisions are made based on enhanced usage statistics encompassing data like +memory cache utilization, memory bandwidth utilization, or network bandwidth +utilization. This is disabled by default. The administrator can configure how +the metrics are weighted in the configuration file by using the +``weight_setting`` configuration option in the ``nova.conf`` configuration +file. For example to configure metric1 with ratio1 and metric2 with ratio2: + +.. code-block:: ini + + weight_setting = "metric1=ratio1, metric2=ratio2" + +.. _host-aggregates: + +Host aggregates and availability zones +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Host aggregates are a mechanism for partitioning hosts in an OpenStack cloud, +or a region of an OpenStack cloud, based on arbitrary characteristics. +Examples where an administrator may want to do this include where a group of +hosts have additional hardware or performance characteristics. + +Host aggregates are not explicitly exposed to users. Instead administrators +map flavors to host aggregates. Administrators do this by setting metadata on +a host aggregate, and matching flavor extra specifications. The scheduler then +endeavors to match user requests for instance of the given flavor to a host +aggregate with the same key-value pair in its metadata. Compute nodes can be +in more than one host aggregate. + +Administrators are able to optionally expose a host aggregate as an +availability zone. Availability zones are different from host aggregates in +that they are explicitly exposed to the user, and hosts can only be in a single +availability zone. Administrators can configure a default availability zone +where instances will be scheduled when the user fails to specify one. + +Command-line interface +---------------------- + +The :command:`nova` command-line client supports the following +aggregate-related commands. + +nova aggregate-list + Print a list of all aggregates. + +nova aggregate-create [availability-zone] + Create a new aggregate named ````, and optionally in availability zone + ``[availability-zone]`` if specified. The command returns the ID of the newly + created aggregate. Hosts can be made available to multiple host aggregates. + Be careful when adding a host to an additional host aggregate when the host + is also in an availability zone. Pay attention when using the :command:`nova + aggregate-set-metadata` and :command:`nova aggregate-update` commands to + avoid user confusion when they boot instances in different availability + zones. An error occurs if you cannot add a particular host to an aggregate + zone for which it is not intended. + +nova aggregate-delete + Delete an aggregate with its ```` or ````. + +nova aggregate-show + Show details of the aggregate with its ```` or ````. + +nova aggregate-add-host + Add host with name ```` to aggregate with its ```` or ````. + +nova aggregate-remove-host + Remove the host with name ```` from the aggregate with its ```` + or ````. + +nova aggregate-set-metadata [ ...] + Add or update metadata (key-value pairs) associated with the aggregate with + its ```` or ````. + +nova aggregate-update [] + Update the name and availability zone (optional) for the aggregate. + +nova host-list + List all hosts by service. + +nova host-update --maintenance [enable | disable] + Put/resume host into/from maintenance. + +.. note:: + + Only administrators can access these commands. If you try to use these + commands and the user name and tenant that you use to access the Compute + service do not have the ``admin`` role or the appropriate privileges, these + errors occur: + + .. code-block:: console + + ERROR: Policy doesn't allow compute_extension:aggregates to be performed. (HTTP 403) (Request-ID: req-299fbff6-6729-4cef-93b2-e7e1f96b4864) + + .. code-block:: console + + ERROR: Policy doesn't allow compute_extension:hosts to be performed. (HTTP 403) (Request-ID: req-ef2400f6-6776-4ea3-b6f1-7704085c27d1) + +Configure scheduler to support host aggregates +---------------------------------------------- + +One common use case for host aggregates is when you want to support scheduling +instances to a subset of compute hosts because they have a specific capability. +For example, you may want to allow users to request compute hosts that have SSD +drives if they need access to faster disk I/O, or access to compute hosts that +have GPU cards to take advantage of GPU-accelerated code. + +To configure the scheduler to support host aggregates, the +``scheduler_default_filters`` configuration option must contain the +``AggregateInstanceExtraSpecsFilter`` in addition to the other filters used by +the scheduler. Add the following line to ``/etc/nova/nova.conf`` on the host +that runs the ``nova-scheduler`` service to enable host aggregates filtering, +as well as the other filters that are typically enabled: + +.. code-block:: ini + + scheduler_default_filters=AggregateInstanceExtraSpecsFilter,RetryFilter,AvailabilityZoneFilter,RamFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,ServerGroupAntiAffinityFilter,ServerGroupAffinityFilter + +Example: Specify compute hosts with SSDs +---------------------------------------- + +This example configures the Compute service to enable users to request nodes +that have solid-state drives (SSDs). You create a ``fast-io`` host aggregate in +the ``nova`` availability zone and you add the ``ssd=true`` key-value pair to +the aggregate. Then, you add the ``node1``, and ``node2`` compute nodes to it. + +.. code-block:: console + + $ openstack aggregate create --zone nova fast-io + +-------------------+----------------------------+ + | Field | Value | + +-------------------+----------------------------+ + | availability_zone | nova | + | created_at | 2016-12-22T07:31:13.013466 | + | deleted | False | + | deleted_at | None | + | id | 1 | + | name | fast-io | + | updated_at | None | + +-------------------+----------------------------+ + + $ openstack aggregate set --property ssd=true 1 + +-------------------+----------------------------+ + | Field | Value | + +-------------------+----------------------------+ + | availability_zone | nova | + | created_at | 2016-12-22T07:31:13.000000 | + | deleted | False | + | deleted_at | None | + | hosts | [] | + | id | 1 | + | name | fast-io | + | properties | ssd='true' | + | updated_at | None | + +-------------------+----------------------------+ + + $ openstack aggregate add host 1 node1 + +-------------------+--------------------------------------------------+ + | Field | Value | + +-------------------+--------------------------------------------------+ + | availability_zone | nova | + | created_at | 2016-12-22T07:31:13.000000 | + | deleted | False | + | deleted_at | None | + | hosts | [u'node1'] | + | id | 1 | + | metadata | {u'ssd': u'true', u'availability_zone': u'nova'} | + | name | fast-io | + | updated_at | None | + +-------------------+--------------------------------------------------+ + + $ openstack aggregate add host 1 node2 + +-------------------+--------------------------------------------------+ + | Field | Value | + +-------------------+--------------------------------------------------+ + | availability_zone | nova | + | created_at | 2016-12-22T07:31:13.000000 | + | deleted | False | + | deleted_at | None | + | hosts | [u'node2'] | + | id | 1 | + | metadata | {u'ssd': u'true', u'availability_zone': u'nova'} | + | name | fast-io | + | updated_at | None | + +-------------------+--------------------------------------------------+ + +Use the :command:`openstack flavor create` command to create the ``ssd.large`` +flavor called with an ID of 6, 8 GB of RAM, 80 GB root disk, and 4 vCPUs. + +.. code-block:: console + + $ openstack flavor create --id 6 --ram 8192 --disk 80 --vcpus 4 ssd.large + +----------------------------+-----------+ + | Field | Value | + +----------------------------+-----------+ + | OS-FLV-DISABLED:disabled | False | + | OS-FLV-EXT-DATA:ephemeral | 0 | + | disk | 80 | + | id | 6 | + | name | ssd.large | + | os-flavor-access:is_public | True | + | ram | 8192 | + | rxtx_factor | 1.0 | + | swap | | + | vcpus | 4 | + +----------------------------+-----------+ + +Once the flavor is created, specify one or more key-value pairs that match the +key-value pairs on the host aggregates with scope +``aggregate_instance_extra_specs``. In this case, that is the +``aggregate_instance_extra_specs:ssd=true`` key-value pair. Setting a +key-value pair on a flavor is done using the :command:`openstack flavor set` +command. + +.. code-block:: console + + $ openstack flavor set --property aggregate_instance_extra_specs:ssd=true ssd.large + +Once it is set, you should see the ``extra_specs`` property of the +``ssd.large`` flavor populated with a key of ``ssd`` and a corresponding value +of ``true``. + +.. code-block:: console + + $ openstack flavor show ssd.large + +----------------------------+-------------------------------------------+ + | Field | Value | + +----------------------------+-------------------------------------------+ + | OS-FLV-DISABLED:disabled | False | + | OS-FLV-EXT-DATA:ephemeral | 0 | + | disk | 80 | + | id | 6 | + | name | ssd.large | + | os-flavor-access:is_public | True | + | properties | aggregate_instance_extra_specs:ssd='true' | + | ram | 8192 | + | rxtx_factor | 1.0 | + | swap | | + | vcpus | 4 | + +----------------------------+-------------------------------------------+ + +Now, when a user requests an instance with the ``ssd.large`` flavor, +the scheduler only considers hosts with the ``ssd=true`` key-value pair. +In this example, these are ``node1`` and ``node2``. + +XenServer hypervisor pools to support live migration +---------------------------------------------------- + +When using the XenAPI-based hypervisor, the Compute service uses host +aggregates to manage XenServer Resource pools, which are used in supporting +live migration. diff --git a/doc/source/admin/configuring-migrations.rst b/doc/source/admin/configuring-migrations.rst new file mode 100644 index 00000000000..226700ac102 --- /dev/null +++ b/doc/source/admin/configuring-migrations.rst @@ -0,0 +1,421 @@ +.. _section_configuring-compute-migrations: + +========================= +Configure live migrations +========================= + +Migration enables an administrator to move a virtual machine instance from one +compute host to another. A typical scenario is planned maintenance on the +source host, but migration can also be useful to redistribute the load when +many VM instances are running on a specific physical machine. + +This document covers live migrations using the +:ref:`configuring-migrations-kvm-libvirt` and +:ref:`configuring-migrations-xenserver` hypervisors. + +.. :ref:`_configuring-migrations-kvm-libvirt` +.. :ref:`_configuring-migrations-xenserver` + +.. note:: + + Not all Compute service hypervisor drivers support live-migration, or + support all live-migration features. + + Consult the `Hypervisor Support Matrix + `_ to + determine which hypervisors support live-migration. + + See the `Hypervisor configuration pages + `_ + for details on hypervisor-specific configuration settings. + +The migration types are: + +- **Non-live migration**, also known as cold migration or simply migration. + + The instance is shut down, then moved to another hypervisor and restarted. + The instance recognizes that it was rebooted, and the application running on + the instance is disrupted. + + This section does not cover cold migration. + +- **Live migration** + + The instance keeps running throughout the migration. This is useful when it + is not possible or desirable to stop the application running on the instance. + + Live migrations can be classified further by the way they treat instance + storage: + + - **Shared storage-based live migration**. The instance has ephemeral disks + that are located on storage shared between the source and destination + hosts. + + - **Block live migration**, or simply block migration. The instance has + ephemeral disks that are not shared between the source and destination + hosts. Block migration is incompatible with read-only devices such as + CD-ROMs and Configuration Drive (config\_drive). + + - **Volume-backed live migration**. Instances use volumes rather than + ephemeral disks. + + Block live migration requires copying disks from the source to the + destination host. It takes more time and puts more load on the network. + Shared-storage and volume-backed live migration does not copy disks. + +.. note:: + + In a multi-cell cloud, instances can be live migrated to a + different host in the same cell, but not across cells. + +The following sections describe how to configure your hosts for live migrations +using the KVM and XenServer hypervisors. + +.. _configuring-migrations-kvm-libvirt: + +KVM-libvirt +~~~~~~~~~~~ + +.. :ref:`_configuring-migrations-kvm-general` +.. :ref:`_configuring-migrations-kvm-block-and-volume-migration` +.. :ref:`_configuring-migrations-kvm-shared-storage` + +.. _configuring-migrations-kvm-general: + +General configuration +--------------------- + +To enable any type of live migration, configure the compute hosts according to +the instructions below: + +#. Set the following parameters in ``nova.conf`` on all compute hosts: + + - ``vncserver_listen=0.0.0.0`` + + You must not make the VNC server listen to the IP address of its compute + host, since that addresses changes when the instance is migrated. + + .. important:: + + Since this setting allows VNC clients from any IP address to connect to + instance consoles, you must take additional measures like secure + networks or firewalls to prevent potential attackers from gaining + access to instances. + + - ``instances_path`` must have the same value for all compute hosts. In + this guide, the value ``/var/lib/nova/instances`` is assumed. + +#. Ensure that name resolution on all compute hosts is identical, so that they + can connect each other through their hostnames. + + If you use ``/etc/hosts`` for name resolution and enable SELinux, ensure + that ``/etc/hosts`` has the correct SELinux context: + + .. code-block:: console + + # restorecon /etc/hosts + +#. Enable password-less SSH so that root on one compute host can log on to any + other compute host without providing a password. The ``libvirtd`` daemon, + which runs as root, uses the SSH protocol to copy the instance to the + destination and can't know the passwords of all compute hosts. + + You may, for example, compile root's public SSH keys on all compute hosts + into an ``authorized_keys`` file and deploy that file to the compute hosts. + +#. Configure the firewalls to allow libvirt to communicate between compute + hosts. + + By default, libvirt uses the TCP port range from 49152 to 49261 for copying + memory and disk contents. Compute hosts must accept connections in this + range. + + For information about ports used by libvirt, see the `libvirt documentation + `_. + + .. important:: + + Be mindful of the security risks introduced by opening ports. + +.. _configuring-migrations-kvm-block-and-volume-migration: + +Block migration, volume-based live migration +-------------------------------------------- + +No additional configuration is required for block migration and volume-backed +live migration. + +Be aware that block migration adds load to the network and storage subsystems. + +.. _configuring-migrations-kvm-shared-storage: + +Shared storage +-------------- + +Compute hosts have many options for sharing storage, for example NFS, shared +disk array LUNs, Ceph or GlusterFS. + +The next steps show how a regular Linux system might be configured as an NFS v4 +server for live migration. For detailed information and alternative ways to +configure NFS on Linux, see instructions for `Ubuntu +`_, `RHEL and derivatives +`_ +or `SLES and OpenSUSE +`_. + +#. Ensure that UID and GID of the nova user are identical on the compute hosts + and the NFS server. + +#. Create a directory with enough disk space for all instances in the cloud, + owned by user nova. In this guide, we assume ``/var/lib/nova/instances``. + +#. Set the execute/search bit on the ``instances`` directory: + + .. code-block:: console + + $ chmod o+x /var/lib/nova/instances + + This allows qemu to access the ``instances`` directory tree. + +#. Export ``/var/lib/nova/instances`` to the compute hosts. For example, add + the following line to ``/etc/exports``: + + .. code-block:: ini + + /var/lib/nova/instances *(rw,sync,fsid=0,no_root_squash) + + The asterisk permits access to any NFS client. The option ``fsid=0`` exports + the instances directory as the NFS root. + +After setting up the NFS server, mount the remote filesystem on all compute +hosts. + +#. Assuming the NFS server's hostname is ``nfs-server``, add this line to + ``/etc/fstab`` to mount the NFS root: + + .. code-block:: console + + nfs-server:/ /var/lib/nova/instances nfs4 defaults 0 0 + +#. Test NFS by mounting the instances directory and check access permissions + for the nova user: + + .. code-block:: console + + $ sudo mount -a -v + $ ls -ld /var/lib/nova/instances/ + drwxr-xr-x. 2 nova nova 6 Mar 14 21:30 /var/lib/nova/instances/ + +.. _configuring-migrations-kvm-advanced: + +Advanced configuration for KVM and QEMU +--------------------------------------- + +Live migration copies the instance's memory from the source to the destination +compute host. After a memory page has been copied, the instance may write to it +again, so that it has to be copied again. Instances that frequently write to +different memory pages can overwhelm the memory copy process and prevent the +live migration from completing. + +This section covers configuration settings that can help live migration of +memory-intensive instances succeed. + +#. **Live migration completion timeout** + + The Compute service aborts a migration when it has been running for too + long. The timeout is calculated based on the instance size, which is the + instance's memory size in GiB. In the case of block migration, the size of + ephemeral storage in GiB is added. + + The timeout in seconds is the instance size multiplied by the configurable + parameter ``live_migration_completion_timeout``, whose default is 800. For + example, shared-storage live migration of an instance with 8GiB memory will + time out after 6400 seconds. + +#. **Live migration progress timeout** + + The Compute service also aborts a live migration when it detects that memory + copy is not making progress for a certain time. You can set this time, in + seconds, through the configurable parameter + ``live_migration_progress_timeout``. + + In Ocata, the default value of ``live_migration_progress_timeout`` is 0, + which disables progress timeouts. You should not change this value, since + the algorithm that detects memory copy progress has been determined to be + unreliable. It may be re-enabled in future releases. + +#. **Instance downtime** + + Near the end of the memory copy, the instance is paused for a short time so + that the remaining few pages can be copied without interference from + instance memory writes. The Compute service initializes this time to a small + value that depends on the instance size, typically around 50 milliseconds. + When it notices that the memory copy does not make sufficient progress, it + increases the time gradually. + + You can influence the instance downtime algorithm with the help of three + configuration variables on the compute hosts: + + .. code-block:: ini + + live_migration_downtime = 500 + live_migration_downtime_steps = 10 + live_migration_downtime_delay = 75 + + ``live_migration_downtime`` sets the maximum permitted downtime for a live + migration, in *milliseconds*. The default is 500. + + ``live_migration_downtime_steps`` sets the total number of adjustment steps + until ``live_migration_downtime`` is reached. The default is 10 steps. + + ``live_migration_downtime_delay`` sets the time interval between two + adjustment steps in *seconds*. The default is 75. + +#. **Auto-convergence** + + One strategy for a successful live migration of a memory-intensive instance + is slowing the instance down. This is called auto-convergence. Both libvirt + and QEMU implement this feature by automatically throttling the instance's + CPU when memory copy delays are detected. + + Auto-convergence is disabled by default. You can enable it by setting + ``live_migration_permit_auto_convergence=true``. + + .. caution:: + + Before enabling auto-convergence, make sure that the instance's + application tolerates a slow-down. + + Be aware that auto-convergence does not guarantee live migration success. + +#. **Post-copy** + + Live migration of a memory-intensive instance is certain to succeed when you + enable post-copy. This feature, implemented by libvirt and QEMU, activates + the virtual machine on the destination host before all of its memory has + been copied. When the virtual machine accesses a page that is missing on + the destination host, the resulting page fault is resolved by copying the + page from the source host. + + Post-copy is disabled by default. You can enable it by setting + ``live_migration_permit_post_copy=true``. + + When you enable both auto-convergence and post-copy, auto-convergence + remains disabled. + + .. caution:: + + The page faults introduced by post-copy can slow the instance down. + + When the network connection between source and destination host is + interrupted, page faults cannot be resolved anymore and the instance is + rebooted. + +.. TODO Bernd: I *believe* that it is certain to succeed, +.. but perhaps I am missing something. + +The full list of live migration configuration parameters is documented in the +:doc:`Nova Configuration Options ` + +.. _configuring-migrations-xenserver: + +XenServer +~~~~~~~~~ + +.. :ref:Shared Storage +.. :ref:Block migration + +.. _configuring-migrations-xenserver-shared-storage: + +Shared storage +-------------- + +**Prerequisites** + +- **Compatible XenServer hypervisors**. + + For more information, see the `Requirements for Creating Resource Pools + `_ + section of the XenServer Administrator's Guide. + +- **Shared storage**. + + An NFS export, visible to all XenServer hosts. + + .. note:: + + For the supported NFS versions, see the `NFS VHD + `_ + section of the XenServer Administrator's Guide. + +To use shared storage live migration with XenServer hypervisors, the hosts must +be joined to a XenServer pool. To create that pool, a host aggregate must be +created with specific metadata. This metadata is used by the XAPI plug-ins to +establish the pool. + +.. rubric:: Using shared storage live migrations with XenServer Hypervisors + +#. Add an NFS VHD storage to your master XenServer, and set it as the default + storage repository. For more information, see NFS VHD in the XenServer + Administrator's Guide. + +#. Configure all compute nodes to use the default storage repository (``sr``) + for pool operations. Add this line to your ``nova.conf`` configuration files + on all compute nodes: + + .. code-block:: ini + + sr_matching_filter=default-sr:true + +#. Create a host aggregate. This command creates the aggregate, and then + displays a table that contains the ID of the new aggregate + + .. code-block:: console + + $ openstack aggregate create --zone AVAILABILITY_ZONE POOL_NAME + + Add metadata to the aggregate, to mark it as a hypervisor pool + + .. code-block:: console + + $ openstack aggregate set --property hypervisor_pool=true AGGREGATE_ID + + $ openstack aggregate set --property operational_state=created AGGREGATE_ID + + Make the first compute node part of that aggregate + + .. code-block:: console + + $ openstack aggregate add host AGGREGATE_ID MASTER_COMPUTE_NAME + + The host is now part of a XenServer pool. + +#. Add hosts to the pool + + .. code-block:: console + + $ openstack aggregate add host AGGREGATE_ID COMPUTE_HOST_NAME + + .. note:: + + The added compute node and the host will shut down to join the host to + the XenServer pool. The operation will fail if any server other than the + compute node is running or suspended on the host. + +.. _configuring-migrations-xenserver-block-migration: + +Block migration +--------------- + +- **Compatible XenServer hypervisors**. + + The hypervisors must support the Storage XenMotion feature. See your + XenServer manual to make sure your edition has this feature. + + .. note:: + + - To use block migration, you must use the ``--block-migrate`` parameter + with the live migration command. + + - Block migration works only with EXT local storage storage repositories, + and the server must not have any volumes attached. diff --git a/doc/source/admin/cpu-topologies.rst b/doc/source/admin/cpu-topologies.rst new file mode 100644 index 00000000000..e795a0fb8e2 --- /dev/null +++ b/doc/source/admin/cpu-topologies.rst @@ -0,0 +1,365 @@ +============== +CPU topologies +============== + +The NUMA topology and CPU pinning features in OpenStack provide high-level +control over how instances run on hypervisor CPUs and the topology of virtual +CPUs available to instances. These features help minimize latency and maximize +performance. + +SMP, NUMA, and SMT +~~~~~~~~~~~~~~~~~~ + +Symmetric multiprocessing (SMP) + SMP is a design found in many modern multi-core systems. In an SMP system, + there are two or more CPUs and these CPUs are connected by some interconnect. + This provides CPUs with equal access to system resources like memory and + input/output ports. + +Non-uniform memory access (NUMA) + NUMA is a derivative of the SMP design that is found in many multi-socket + systems. In a NUMA system, system memory is divided into cells or nodes that + are associated with particular CPUs. Requests for memory on other nodes are + possible through an interconnect bus. However, bandwidth across this shared + bus is limited. As a result, competition for this resource can incur + performance penalties. + +Simultaneous Multi-Threading (SMT) + SMT is a design complementary to SMP. Whereas CPUs in SMP systems share a bus + and some memory, CPUs in SMT systems share many more components. CPUs that + share components are known as thread siblings. All CPUs appear as usable + CPUs on the system and can execute workloads in parallel. However, as with + NUMA, threads compete for shared resources. + +In OpenStack, SMP CPUs are known as *cores*, NUMA cells or nodes are known as +*sockets*, and SMT CPUs are known as *threads*. For example, a quad-socket, +eight core system with Hyper-Threading would have four sockets, eight cores per +socket and two threads per core, for a total of 64 CPUs. + +Configuring compute nodes for instances with NUMA placement policies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Hyper-V is configured by default to allow instances to span multiple NUMA +nodes, regardless if the instances have been configured to only span N NUMA +nodes. This behaviour allows Hyper-V instances to have up to 64 vCPUs and 1 TB +of memory. + +Checking NUMA spanning can easily be done by running this following powershell +command: + +.. code-block:: console + + (Get-VMHost).NumaSpanningEnabled + +In order to disable this behaviour, the host will have to be configured to +disable NUMA spanning. This can be done by executing these following +powershell commands: + +.. code-block:: console + + Set-VMHost -NumaSpanningEnabled $false + Restart-Service vmms + +In order to restore this behaviour, execute these powershell commands: + +.. code-block:: console + + Set-VMHost -NumaSpanningEnabled $true + Restart-Service vmms + +The ``vmms`` service (Virtual Machine Management Service) is responsible for +managing the Hyper-V VMs. The VMs will still run while the service is down +or restarting, but they will not be manageable by the ``nova-compute`` +service. In order for the effects of the Host NUMA spanning configuration +to take effect, the VMs will have to be restarted. + +Hyper-V does not allow instances with a NUMA topology to have dynamic +memory allocation turned on. The Hyper-V driver will ignore the configured +``dynamic_memory_ratio`` from the given ``nova.conf`` file when spawning +instances with a NUMA topology. + +Customizing instance NUMA placement policies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. important:: + + The functionality described below is currently only supported by the + libvirt/KVM and Hyper-V driver. + +When running workloads on NUMA hosts, it is important that the vCPUs executing +processes are on the same NUMA node as the memory used by these processes. +This ensures all memory accesses are local to the node and thus do not consume +the limited cross-node memory bandwidth, adding latency to memory accesses. +Similarly, large pages are assigned from memory and benefit from the same +performance improvements as memory allocated using standard pages. Thus, they +also should be local. Finally, PCI devices are directly associated with +specific NUMA nodes for the purposes of DMA. Instances that use PCI or SR-IOV +devices should be placed on the NUMA node associated with these devices. + +By default, an instance floats across all NUMA nodes on a host. NUMA awareness +can be enabled implicitly through the use of huge pages or pinned CPUs or +explicitly through the use of flavor extra specs or image metadata. In all +cases, the ``NUMATopologyFilter`` filter must be enabled. Details on this +filter are provided in :doc:`/admin/configuration/schedulers` in Nova +configuration guide. + +.. caution:: + + The NUMA node(s) used are normally chosen at random. However, if a PCI + passthrough or SR-IOV device is attached to the instance, then the NUMA + node that the device is associated with will be used. This can provide + important performance improvements. However, booting a large number of + similar instances can result in unbalanced NUMA node usage. Care should + be taken to mitigate this issue. See this `discussion`_ for more details. + +.. caution:: + + Inadequate per-node resources will result in scheduling failures. Resources + that are specific to a node include not only CPUs and memory, but also PCI + and SR-IOV resources. It is not possible to use multiple resources from + different nodes without requesting a multi-node layout. As such, it may be + necessary to ensure PCI or SR-IOV resources are associated with the same + NUMA node or force a multi-node layout. + +When used, NUMA awareness allows the operating system of the instance to +intelligently schedule the workloads that it runs and minimize cross-node +memory bandwidth. To restrict an instance's vCPUs to a single host NUMA node, +run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:numa_nodes=1 + +Some workloads have very demanding requirements for memory access latency or +bandwidth that exceed the memory bandwidth available from a single NUMA node. +For such workloads, it is beneficial to spread the instance across multiple +host NUMA nodes, even if the instance's RAM/vCPUs could theoretically fit on a +single NUMA node. To force an instance's vCPUs to spread across two host NUMA +nodes, run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:numa_nodes=2 + +The allocation of instances vCPUs and memory from different host NUMA nodes can +be configured. This allows for asymmetric allocation of vCPUs and memory, which +can be important for some workloads. To spread the 6 vCPUs and 6 GB of memory +of an instance across two NUMA nodes and create an asymmetric 1:2 vCPU and +memory mapping between the two nodes, run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:numa_nodes=2 + $ openstack flavor set m1.large \ # configure guest node 0 + --property hw:numa_cpus.0=0,1 \ + --property hw:numa_mem.0=2048 + $ openstack flavor set m1.large \ # configure guest node 1 + --property hw:numa_cpus.1=2,3,4,5 \ + --property hw:numa_mem.1=4096 + +.. note:: + + Hyper-V does not support asymmetric NUMA topologies, and the Hyper-V + driver will not spawn instances with such topologies. + +For more information about the syntax for ``hw:numa_nodes``, ``hw:numa_cpus.N`` +and ``hw:num_mem.N``, refer to the :ref:`NUMA +topology ` guide. + +Customizing instance CPU pinning policies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. important:: + + The functionality described below is currently only supported by the + libvirt/KVM driver. Hyper-V does not support CPU pinning. + +By default, instance vCPU processes are not assigned to any particular host +CPU, instead, they float across host CPUs like any other process. This allows +for features like overcommitting of CPUs. In heavily contended systems, this +provides optimal system performance at the expense of performance and latency +for individual instances. + +Some workloads require real-time or near real-time behavior, which is not +possible with the latency introduced by the default CPU policy. For such +workloads, it is beneficial to control which host CPUs are bound to an +instance's vCPUs. This process is known as pinning. No instance with pinned +CPUs can use the CPUs of another pinned instance, thus preventing resource +contention between instances. To configure a flavor to use pinned vCPUs, a +use a dedicated CPU policy. To force this, run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:cpu_policy=dedicated + +.. caution:: + + Host aggregates should be used to separate pinned instances from unpinned + instances as the latter will not respect the resourcing requirements of + the former. + +When running workloads on SMT hosts, it is important to be aware of the impact +that thread siblings can have. Thread siblings share a number of components +and contention on these components can impact performance. To configure how +to use threads, a CPU thread policy should be specified. For workloads where +sharing benefits performance, use thread siblings. To force this, run: + +.. code-block:: console + + $ openstack flavor set m1.large \ + --property hw:cpu_policy=dedicated \ + --property hw:cpu_thread_policy=require + +For other workloads where performance is impacted by contention for resources, +use non-thread siblings or non-SMT hosts. To force this, run: + +.. code-block:: console + + $ openstack flavor set m1.large \ + --property hw:cpu_policy=dedicated \ + --property hw:cpu_thread_policy=isolate + +Finally, for workloads where performance is minimally impacted, use thread +siblings if available. This is the default, but it can be set explicitly: + +.. code-block:: console + + $ openstack flavor set m1.large \ + --property hw:cpu_policy=dedicated \ + --property hw:cpu_thread_policy=prefer + +For more information about the syntax for ``hw:cpu_policy`` and +``hw:cpu_thread_policy``, refer to the :doc:`/admin/flavors` guide. + +Applications are frequently packaged as images. For applications that require +real-time or near real-time behavior, configure image metadata to ensure +created instances are always pinned regardless of flavor. To configure an +image to use pinned vCPUs and avoid thread siblings, run: + +.. code-block:: console + + $ openstack image set [IMAGE_ID] \ + --property hw_cpu_policy=dedicated \ + --property hw_cpu_thread_policy=isolate + +If the flavor specifies a CPU policy of ``dedicated`` then that policy will be +used. If the flavor explicitly specifies a CPU policy of ``shared`` and the +image specifies no policy or a policy of ``shared`` then the ``shared`` policy +will be used, but if the image specifies a policy of ``dedicated`` an exception +will be raised. By setting a ``shared`` policy through flavor extra-specs, +administrators can prevent users configuring CPU policies in images and +impacting resource utilization. To configure this policy, run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:cpu_policy=shared + +If the flavor does not specify a CPU thread policy then the CPU thread policy +specified by the image (if any) will be used. If both the flavor and image +specify a CPU thread policy then they must specify the same policy, otherwise +an exception will be raised. + +.. note:: + + There is no correlation required between the NUMA topology exposed in the + instance and how the instance is actually pinned on the host. This is by + design. See this `invalid bug + `_ for more information. + +For more information about image metadata, refer to the `Image metadata`_ +guide. + +Customizing instance CPU topologies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. important:: + + The functionality described below is currently only supported by the + libvirt/KVM driver. + +In addition to configuring how an instance is scheduled on host CPUs, it is +possible to configure how CPUs are represented in the instance itself. By +default, when instance NUMA placement is not specified, a topology of N +sockets, each with one core and one thread, is used for an instance, where N +corresponds to the number of instance vCPUs requested. When instance NUMA +placement is specified, the number of sockets is fixed to the number of host +NUMA nodes to use and the total number of instance CPUs is split over these +sockets. + +Some workloads benefit from a custom topology. For example, in some operating +systems, a different license may be needed depending on the number of CPU +sockets. To configure a flavor to use a maximum of two sockets, run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:cpu_sockets=2 + +Similarly, to configure a flavor to use one core and one thread, run: + +.. code-block:: console + + $ openstack flavor set m1.large \ + --property hw:cpu_cores=1 \ + --property hw:cpu_threads=1 + +.. caution:: + + If specifying all values, the product of sockets multiplied by cores + multiplied by threads must equal the number of instance vCPUs. If specifying + any one of these values or the multiple of two values, the values must be a + factor of the number of instance vCPUs to prevent an exception. For example, + specifying ``hw:cpu_sockets=2`` on a host with an odd number of cores fails. + Similarly, specifying ``hw:cpu_cores=2`` and ``hw:cpu_threads=4`` on a host + with ten cores fails. + +For more information about the syntax for ``hw:cpu_sockets``, ``hw:cpu_cores`` +and ``hw:cpu_threads``, refer to the :doc:`/admin/flavors` guide. + +It is also possible to set upper limits on the number of sockets, cores, and +threads used. Unlike the hard values above, it is not necessary for this exact +number to used because it only provides a limit. This can be used to provide +some flexibility in scheduling, while ensuring certains limits are not +exceeded. For example, to ensure no more than two sockets are defined in the +instance topology, run: + +.. code-block:: console + + $ openstack flavor set m1.large --property=hw:cpu_max_sockets=2 + +For more information about the syntax for ``hw:cpu_max_sockets``, +``hw:cpu_max_cores``, and ``hw:cpu_max_threads``, refer to the +:doc:`/admin/flavors` guide. + +Applications are frequently packaged as images. For applications that prefer +certain CPU topologies, configure image metadata to hint that created instances +should have a given topology regardless of flavor. To configure an image to +request a two-socket, four-core per socket topology, run: + +.. code-block:: console + + $ openstack image set [IMAGE_ID] \ + --property hw_cpu_sockets=2 \ + --property hw_cpu_cores=4 + +To constrain instances to a given limit of sockets, cores or threads, use the +``max_`` variants. To configure an image to have a maximum of two sockets and a +maximum of one thread, run: + +.. code-block:: console + + $ openstack image set [IMAGE_ID] \ + --property hw_cpu_max_sockets=2 \ + --property hw_cpu_max_threads=1 + +The value specified in the flavor is treated as the abolute limit. The image +limits are not permitted to exceed the flavor limits, they can only be equal +to or lower than what the flavor defines. By setting a ``max`` value for +sockets, cores, or threads, administrators can prevent users configuring +topologies that might, for example, incur an additional licensing fees. + +For more information about image metadata, refer to the `Image metadata`_ +guide. + +.. Links +.. _`Image metadata`: https://docs.openstack.org/image-guide/image-metadata.html +.. _`discussion`: http://lists.openstack.org/pipermail/openstack-dev/2016-March/090367.html diff --git a/doc/source/admin/default-ports.rst b/doc/source/admin/default-ports.rst new file mode 100644 index 00000000000..d17bcaa29ad --- /dev/null +++ b/doc/source/admin/default-ports.rst @@ -0,0 +1,31 @@ +========================================== +Compute service node firewall requirements +========================================== + +Console connections for virtual machines, whether direct or through a +proxy, are received on ports ``5900`` to ``5999``. The firewall on each +Compute service node must allow network traffic on these ports. + +This procedure modifies the iptables firewall to allow incoming +connections to the Compute services. + +**Configuring the service-node firewall** + +#. Log in to the server that hosts the Compute service, as root. + +#. Edit the ``/etc/sysconfig/iptables`` file, to add an INPUT rule that + allows TCP traffic on ports from ``5900`` to ``5999``. Make sure the new + rule appears before any INPUT rules that REJECT traffic: + + .. code-block:: console + + -A INPUT -p tcp -m multiport --dports 5900:5999 -j ACCEPT + +#. Save the changes to the ``/etc/sysconfig/iptables`` file, and restart the + ``iptables`` service to pick up the changes: + + .. code-block:: console + + $ service iptables restart + +#. Repeat this process for each Compute service node. diff --git a/doc/source/admin/euca2ools.rst b/doc/source/admin/euca2ools.rst new file mode 100644 index 00000000000..3895ac6567c --- /dev/null +++ b/doc/source/admin/euca2ools.rst @@ -0,0 +1,7 @@ +================================= +Managing the cloud with euca2ools +================================= + +The ``euca2ools`` command-line tool provides a command line interface to EC2 +API calls. For more information, see the `Official Eucalyptus Documentation +`_. diff --git a/doc/source/admin/evacuate.rst b/doc/source/admin/evacuate.rst new file mode 100644 index 00000000000..ef3b2256da3 --- /dev/null +++ b/doc/source/admin/evacuate.rst @@ -0,0 +1,49 @@ +================== +Evacuate instances +================== + +If a hardware malfunction or other error causes a cloud compute node to fail, +you can evacuate instances to make them available again. You can optionally +include the target host on the :command:`nova evacuate` command. If you omit +the host, the scheduler chooses the target host. + +To preserve user data on the server disk, configure shared storage on the +target host. When you evacuate the instance, Compute detects whether shared +storage is available on the target host. Also, you must validate that the +current VM host is not operational. Otherwise, the evacuation fails. + +#. To find a host for the evacuated instance, list all hosts: + + .. code-block:: console + + $ openstack host list + +#. Evacuate the instance. You can use the ``--password PWD`` option to pass the + instance password to the command. If you do not specify a password, the + command generates and prints one after it finishes successfully. The + following command evacuates a server from a failed host to ``HOST_B``. + + .. code-block:: console + + $ nova evacuate EVACUATED_SERVER_NAME HOST_B + + The command rebuilds the instance from the original image or volume and + returns a password. The command preserves the original configuration, which + includes the instance ID, name, uid, IP address, and so on. + + .. code-block:: console + + +-----------+--------------+ + | Property | Value | + +-----------+--------------+ + | adminPass | kRAJpErnT4xZ | + +-----------+--------------+ + +#. To preserve the user disk data on the evacuated server, deploy Compute with + a shared file system. To configure your system, see + :ref:`section_configuring-compute-migrations`. The following example does + not change the password. + + .. code-block:: console + + $ nova evacuate EVACUATED_SERVER_NAME HOST_B --on-shared-storage diff --git a/doc/source/admin/figures/OpenStackTrustedComputePool1.png b/doc/source/admin/figures/OpenStackTrustedComputePool1.png new file mode 100644 index 00000000000..b87df06e173 Binary files /dev/null and b/doc/source/admin/figures/OpenStackTrustedComputePool1.png differ diff --git a/doc/source/admin/figures/OpenStackTrustedComputePool2.png b/doc/source/admin/figures/OpenStackTrustedComputePool2.png new file mode 100644 index 00000000000..16b078de83f Binary files /dev/null and b/doc/source/admin/figures/OpenStackTrustedComputePool2.png differ diff --git a/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-Flat-DHCP-manager.jpg b/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-Flat-DHCP-manager.jpg new file mode 100644 index 00000000000..847b06be67e Binary files /dev/null and b/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-Flat-DHCP-manager.jpg differ diff --git a/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-Flat-manager.jpg b/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-Flat-manager.jpg new file mode 100644 index 00000000000..8533d248e1e Binary files /dev/null and b/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-Flat-manager.jpg differ diff --git a/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-VLAN-manager.jpg b/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-VLAN-manager.jpg new file mode 100644 index 00000000000..6d03278c99b Binary files /dev/null and b/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-VLAN-manager.jpg differ diff --git a/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack.vsd b/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack.vsd new file mode 100644 index 00000000000..cf0236baab5 Binary files /dev/null and b/doc/source/admin/figures/SCH_5007_V00_NUAC-multi_nic_OpenStack.vsd differ diff --git a/doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.png b/doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.png new file mode 100644 index 00000000000..3d0e51d4c3b Binary files /dev/null and b/doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.png differ diff --git a/doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.svg b/doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.svg new file mode 100644 index 00000000000..563dea780b0 --- /dev/null +++ b/doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.svg @@ -0,0 +1,824 @@ + + + + + Schéma Réseau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VBackground-1 + + + Solid + + + + + + + + + + None + + + + + + + + + + + + + + Page-1 + + + + + + + + + Sheet.58 + + + + User.50 + + + + + + + + + + + + + + + + + + Add list item permissions + + + Sheet.3 + + Network.80 + + + + + + + + Sheet.2 + Nova-api + + + + Nova-api + + + Sheet.4 + + Network.80 + + + + + + + + Sheet.12 + Compute node + + + + Compute node + + + Sheet.13 + + + + Sheet.14 + The api sends a «get_vnc_console» message + + + + The api sends a «get_vnc_console» message + + Sheet.19 + + + Sheet.21 + + + + Sheet.18 + + Sheet.20 + Generates a token + + + + Generates a token + + Sheet.22 + + + + + Sheet.23 + + + + Sheet.24 + + + + + Sheet.25 + + + + Sheet.26 + + + + Sheet.27 + + + + + Sheet.34 + + Configure + + + + + + + + Sheet.33 + Libvirt driver + + + + Libvirt driver + + + Sheet.35 + + Configure + + + + + + + + Sheet.39 + Nova-consoleauth + + + + Nova-consoleauth + + + Sheet.41 + + + + Sheet.46 + + + + + Sheet.42 + + Sheet.44 + Sends «authorize_console » message + + + + Sends «authorize_console » message + + Sheet.55 + + Sheet.56 + + + + Document + + + + + + + + + + Sheet.5 + + + + Sheet.6 + + Sheet.49 + Caches the connection informations and token + + + + Caches the connection informations and token + + New + + + + + + + + + Sheet.47 + + + + Sheet.53 + Browses the url returned Http://novncip:port/?token=xyz + + + + Browses the url returnedHttp://novncip:port/?token=xyz + + Sheet.28 + + Sheet.29 + Sends a «get_vnc_connection» message + + + + Sends a «get_vnc_connection» message + + Sheet.30 + + Sheet.31 + + + + Document + + + + + + + + + + Sheet.51 + + + + + + + + + + + + Dialog form + + + + + + + + Sheet.96 + + + Sheet.97 + + + + Sheet.98 + + + Sheet.100 + + + + + + + + + + + Sheet.50 + Browser + + + + Browser + + Sheet.57 + noVNC + + + + noVNC + + + Sheet.70 + + Sheet.67 + Sends « check_token » message + + + + Sends « check_token » message + + + + + + + + + + + + + + + + Key + + Sheet.61 + + + + + Sheet.62 + + + + Sheet.63 + + + + + Sheet.64 + + + + Sheet.65 + + + + Sheet.66 + + + + + + Internet + + + + + + + + Sheet.72 + + + + Sheet.83 + + Sheet.74 + Proxy starts + + + + Proxy starts + + Power + + + + + + + + + Sheet.102 + + + + Sheet.106 + 2 + + + + 2 + + Sheet.107 + 3 + + + + 3 + + Sheet.108 + 4 + + + + 4 + + Sheet.105 + + Sheet.104 + Returns a url with a token + + + + Returns a url with a token + + Link.84 + + + + + + + + + Sheet.109 + + Sheet.43 + The user requests an access URL + + + + The user requests an access URL + + Internet.103 + + + + + + + + + Sheet.101 + 1 + + + + 1 + + Sheet.110 + 5 + + + + 5 + + Sheet.111 + 6 + + + + 6 + + Sheet.112 + 7 + + + + 7 + + Sheet.113 + 8 + + + + 8 + + diff --git a/doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.vsd b/doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.vsd new file mode 100644 index 00000000000..ad075eb3261 Binary files /dev/null and b/doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.vsd differ diff --git a/doc/source/admin/flavors.rst b/doc/source/admin/flavors.rst new file mode 100644 index 00000000000..ccce05dfad0 --- /dev/null +++ b/doc/source/admin/flavors.rst @@ -0,0 +1,527 @@ +======= +Flavors +======= + +Admin users can use the :command:`openstack flavor` command to customize and +manage flavors. To see information for this command, run: + +.. code-block:: console + + $ openstack flavor --help + Command "flavor" matches: + flavor create + flavor delete + flavor list + flavor set + flavor show + flavor unset + +.. note:: + + Configuration rights can be delegated to additional users by redefining + the access controls for ``os_compute_api:os-flavor-manage`` in + ``/etc/nova/policy.json`` on the ``nova-api`` server. + +Flavors define these elements: + +.. todo:: This would be much easier to read as a list-table or similar + ++-------------+---------------------------------------------------------------+ +| Element | Description | ++=============+===============================================================+ +| Name | A descriptive name. XX.SIZE_NAME is typically not required, | +| | though some third party tools may rely on it. | ++-------------+---------------------------------------------------------------+ +| Memory MB | Instance memory in megabytes. | ++-------------+---------------------------------------------------------------+ +| Disk | Virtual root disk size in gigabytes. This is an ephemeral di\ | +| | sk that the base image is copied into. When booting from a p\ | +| | ersistent volume it is not used. The "0" size is a special c\ | +| | ase which uses the native base image size as the size of the | +| | ephemeral root volume. However, in this case the filter | +| | scheduler cannot select the compute host based on the virtual | +| | image size. Therefore 0 should only be used for volume booted | +| | instances or for testing purposes. | ++-------------+---------------------------------------------------------------+ +| Ephemeral | Specifies the size of a secondary ephemeral data disk. This | +| | is an empty, unformatted disk and exists only for the life o\ | +| | f the instance. Default value is ``0``. | ++-------------+---------------------------------------------------------------+ +| Swap | Optional swap space allocation for the instance. Default | +| | value is ``0``. | ++-------------+---------------------------------------------------------------+ +| VCPUs | Number of virtual CPUs presented to the instance. | ++-------------+---------------------------------------------------------------+ +| RXTX Factor | Optional property allows created servers to have a different | +| | bandwidth cap than that defined in the network they are att\ | +| | ached to. This factor is multiplied by the rxtx_base propert\ | +| | y of the network. Default value is ``1.0``. That is, the same | +| | as attached network. This parameter is only available for Xen | +| | or NSX based systems. | ++-------------+---------------------------------------------------------------+ +| Is Public | Boolean value, whether flavor is available to all users or p\ | +| | rivate to the project it was created in. Defaults to ``True``.| ++-------------+---------------------------------------------------------------+ +| Extra Specs | Key and value pairs that define on which compute nodes a fla\ | +| | vor can run. These pairs must match corresponding pairs on t\ | +| | he compute nodes. Use to implement special resources, such a\ | +| | s flavors that run on only compute nodes with GPU hardware. | ++-------------+---------------------------------------------------------------+ + +.. note:: + + Flavor customization can be limited by the hypervisor in use. For example + the libvirt driver enables quotas on CPUs available to a VM, disk tuning, + bandwidth I/O, watchdog behavior, random number generator device control, + and instance VIF traffic control. + +Is Public +~~~~~~~~~ + +Flavors can be assigned to particular projects. By default, a flavor is public +and available to all projects. Private flavors are only accessible to those on +the access list and are invisible to other projects. To create and assign a +private flavor to a project, run this command: + +.. code-block:: console + + $ openstack flavor create --private p1.medium --id auto --ram 512 --disk 40 --vcpus 4 + +Extra Specs +~~~~~~~~~~~ + +CPU limits + You can configure the CPU limits with control parameters with the ``nova`` + client. For example, to configure the I/O limit, use: + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME \ + --property quota:read_bytes_sec=10240000 \ + --property quota:write_bytes_sec=10240000 + + Use these optional parameters to control weight shares, enforcement intervals + for runtime quotas, and a quota for maximum allowed bandwidth: + + - ``cpu_shares``: Specifies the proportional weighted share for the domain. + If this element is omitted, the service defaults to the OS provided + defaults. There is no unit for the value; it is a relative measure based on + the setting of other VMs. For example, a VM configured with value 2048 gets + twice as much CPU time as a VM configured with value 1024. + + - ``cpu_shares_level``: On VMware, specifies the allocation level. Can be + ``custom``, ``high``, ``normal``, or ``low``. If you choose ``custom``, set + the number of shares using ``cpu_shares_share``. + + - ``cpu_period``: Specifies the enforcement interval (unit: microseconds) + for QEMU and LXC hypervisors. Within a period, each VCPU of the domain is + not allowed to consume more than the quota worth of runtime. The value + should be in range ``[1000, 1000000]``. A period with value 0 means no + value. + + - ``cpu_limit``: Specifies the upper limit for VMware machine CPU allocation + in MHz. This parameter ensures that a machine never uses more than the + defined amount of CPU time. It can be used to enforce a limit on the + machine's CPU performance. + + - ``cpu_reservation``: Specifies the guaranteed minimum CPU reservation in + MHz for VMware. This means that if needed, the machine will definitely get + allocated the reserved amount of CPU cycles. + + - ``cpu_quota``: Specifies the maximum allowed bandwidth (unit: + microseconds). A domain with a negative-value quota indicates that the + domain has infinite bandwidth, which means that it is not bandwidth + controlled. The value should be in range ``[1000, 18446744073709551]`` or + less than 0. A quota with value 0 means no value. You can use this feature + to ensure that all vCPUs run at the same speed. For example: + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME \ + --property quota:cpu_quota=10000 \ + --property quota:cpu_period=20000 + + In this example, an instance of ``FLAVOR-NAME`` can only consume a maximum + of 50% CPU of a physical CPU computing capability. + +Memory limits + For VMware, you can configure the memory limits with control parameters. + + Use these optional parameters to limit the memory allocation, guarantee + minimum memory reservation, and to specify shares used in case of resource + contention: + + - ``memory_limit``: Specifies the upper limit for VMware machine memory + allocation in MB. The utilization of a virtual machine will not exceed this + limit, even if there are available resources. This is typically used to + ensure a consistent performance of virtual machines independent of + available resources. + + - ``memory_reservation``: Specifies the guaranteed minimum memory reservation + in MB for VMware. This means the specified amount of memory will definitely + be allocated to the machine. + + - ``memory_shares_level``: On VMware, specifies the allocation level. This + can be ``custom``, ``high``, ``normal`` or ``low``. If you choose + ``custom``, set the number of shares using ``memory_shares_share``. + + - ``memory_shares_share``: Specifies the number of shares allocated in the + event that ``custom`` is used. There is no unit for this value. It is a + relative measure based on the settings for other VMs. For example: + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME \ + --property quota:memory_shares_level=custom \ + --property quota:memory_shares_share=15 + +Disk I/O limits + For VMware, you can configure the resource limits for disk with control + parameters. + + Use these optional parameters to limit the disk utilization, guarantee disk + allocation, and to specify shares used in case of resource contention. This + allows the VMware driver to enable disk allocations for the running instance. + + - ``disk_io_limit``: Specifies the upper limit for disk utilization in I/O + per second. The utilization of a virtual machine will not exceed this + limit, even if there are available resources. The default value is -1 which + indicates unlimited usage. + + - ``disk_io_reservation``: Specifies the guaranteed minimum disk allocation + in terms of Input/output Operations Per Second (IOPS). + + - ``disk_io_shares_level``: Specifies the allocation level. This can be + ``custom``, ``high``, ``normal`` or ``low``. If you choose custom, set the + number of shares using ``disk_io_shares_share``. + + - ``disk_io_shares_share``: Specifies the number of shares allocated in the + event that ``custom`` is used. When there is resource contention, this + value is used to determine the resource allocation. + + The example below sets the ``disk_io_reservation`` to 2000 IOPS. + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME \ + --property quota:disk_io_reservation=2000 + +Disk tuning + Using disk I/O quotas, you can set maximum disk write to 10 MB per second for + a VM user. For example: + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME \ + --property quota:disk_write_bytes_sec=10485760 + + The disk I/O options are: + + - ``disk_read_bytes_sec`` + - ``disk_read_iops_sec`` + - ``disk_write_bytes_sec`` + - ``disk_write_iops_sec`` + - ``disk_total_bytes_sec`` + - ``disk_total_iops_sec`` + +Bandwidth I/O + The vif I/O options are: + + - ``vif_inbound_average`` + - ``vif_inbound_burst`` + - ``vif_inbound_peak`` + - ``vif_outbound_average`` + - ``vif_outbound_burst`` + - ``vif_outbound_peak`` + + Incoming and outgoing traffic can be shaped independently. The bandwidth + element can have at most, one inbound and at most, one outbound child + element. If you leave any of these child elements out, no quality of service + (QoS) is applied on that traffic direction. So, if you want to shape only the + network's incoming traffic, use inbound only (and vice versa). Each element + has one mandatory attribute average, which specifies the average bit rate on + the interface being shaped. + + There are also two optional attributes (integer): ``peak``, which specifies + the maximum rate at which a bridge can send data (kilobytes/second), and + ``burst``, the amount of bytes that can be burst at peak speed (kilobytes). + The rate is shared equally within domains connected to the network. + + The example below sets network traffic bandwidth limits for existing flavor + as follows: + + - Outbound traffic: + + - average: 262 Mbps (32768 kilobytes/second) + + - peak: 524 Mbps (65536 kilobytes/second) + + - burst: 65536 kilobytes + + - Inbound traffic: + + - average: 262 Mbps (32768 kilobytes/second) + + - peak: 524 Mbps (65536 kilobytes/second) + + - burst: 65536 kilobytes + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME \ + --property quota:vif_outbound_average=32768 \ + --property quota:vif_outbound_peak=65536 \ + --property quota:vif_outbound_burst=65536 \ + --property quota:vif_inbound_average=32768 \ + --property quota:vif_inbound_peak=65536 \ + --property quota:vif_inbound_burst=65536 + + .. note:: + + All the speed limit values in above example are specified in + kilobytes/second. And burst values are in kilobytes. Values were converted + using 'Data rate units on Wikipedia + `_. + +Watchdog behavior + For the libvirt driver, you can enable and set the behavior of a virtual + hardware watchdog device for each flavor. Watchdog devices keep an eye on the + guest server, and carry out the configured action, if the server hangs. The + watchdog uses the i6300esb device (emulating a PCI Intel 6300ESB). If + ``hw:watchdog_action`` is not specified, the watchdog is disabled. + + To set the behavior, use: + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME --property hw:watchdog_action=ACTION + + Valid ACTION values are: + + - ``disabled``: (default) The device is not attached. + - ``reset``: Forcefully reset the guest. + - ``poweroff``: Forcefully power off the guest. + - ``pause``: Pause the guest. + - ``none``: Only enable the watchdog; do nothing if the server hangs. + + .. note:: + + Watchdog behavior set using a specific image's properties will override + behavior set using flavors. + +Random-number generator + If a random-number generator device has been added to the instance through + its image properties, the device can be enabled and configured using: + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME \ + --property hw_rng:allowed=True \ + --property hw_rng:rate_bytes=RATE-BYTES \ + --property hw_rng:rate_period=RATE-PERIOD + + Where: + + - RATE-BYTES: (integer) Allowed amount of bytes that the guest can read from + the host's entropy per period. + - RATE-PERIOD: (integer) Duration of the read period in seconds. + +CPU topology + For the libvirt driver, you can define the topology of the processors in the + virtual machine using properties. The properties with ``max`` limit the + number that can be selected by the user with image properties. + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME \ + --property hw:cpu_sockets=FLAVOR-SOCKETS \ + --property hw:cpu_cores=FLAVOR-CORES \ + --property hw:cpu_threads=FLAVOR-THREADS \ + --property hw:cpu_max_sockets=FLAVOR-SOCKETS \ + --property hw:cpu_max_cores=FLAVOR-CORES \ + --property hw:cpu_max_threads=FLAVOR-THREADS + + Where: + + - FLAVOR-SOCKETS: (integer) The number of sockets for the guest VM. By + default, this is set to the number of vCPUs requested. + - FLAVOR-CORES: (integer) The number of cores per socket for the guest VM. By + default, this is set to ``1``. + - FLAVOR-THREADS: (integer) The number of threads per core for the guest VM. + By default, this is set to ``1``. + +CPU pinning policy + For the libvirt driver, you can pin the virtual CPUs (vCPUs) of instances to + the host's physical CPU cores (pCPUs) using properties. You can further + refine this by stating how hardware CPU threads in a simultaneous + multithreading-based (SMT) architecture be used. These configurations will + result in improved per-instance determinism and performance. + + .. note:: + + SMT-based architectures include Intel processors with Hyper-Threading + technology. In these architectures, processor cores share a number of + components with one or more other cores. Cores in such architectures are + commonly referred to as hardware threads, while the cores that a given + core share components with are known as thread siblings. + + .. note:: + + Host aggregates should be used to separate these pinned instances from + unpinned instances as the latter will not respect the resourcing + requirements of the former. + + .. code:: console + + $ openstack flavor set FLAVOR-NAME \ + --property hw:cpu_policy=CPU-POLICY \ + --property hw:cpu_thread_policy=CPU-THREAD-POLICY + + Valid CPU-POLICY values are: + + - ``shared``: (default) The guest vCPUs will be allowed to freely float + across host pCPUs, albeit potentially constrained by NUMA policy. + - ``dedicated``: The guest vCPUs will be strictly pinned to a set of host + pCPUs. In the absence of an explicit vCPU topology request, the drivers + typically expose all vCPUs as sockets with one core and one thread. When + strict CPU pinning is in effect the guest CPU topology will be setup to + match the topology of the CPUs to which it is pinned. This option implies + an overcommit ratio of 1.0. For example, if a two vCPU guest is pinned to a + single host core with two threads, then the guest will get a topology of + one socket, one core, two threads. + + Valid CPU-THREAD-POLICY values are: + + - ``prefer``: (default) The host may or may not have an SMT architecture. + Where an SMT architecture is present, thread siblings are preferred. + - ``isolate``: The host must not have an SMT architecture or must emulate a + non-SMT architecture. If the host does not have an SMT architecture, each + vCPU is placed on a different core as expected. If the host does have an + SMT architecture - that is, one or more cores have thread siblings - then + each vCPU is placed on a different physical core. No vCPUs from other + guests are placed on the same core. All but one thread sibling on each + utilized core is therefore guaranteed to be unusable. + - ``require``: The host must have an SMT architecture. Each vCPU is allocated + on thread siblings. If the host does not have an SMT architecture, then it + is not used. If the host has an SMT architecture, but not enough cores with + free thread siblings are available, then scheduling fails. + + .. note:: + + The ``hw:cpu_thread_policy`` option is only valid if ``hw:cpu_policy`` is + set to ``dedicated``. + +.. _extra-specs-numa-topology: + +NUMA topology + For the libvirt driver, you can define the host NUMA placement for the + instance vCPU threads as well as the allocation of instance vCPUs and memory + from the host NUMA nodes. For flavors whose memory and vCPU allocations are + larger than the size of NUMA nodes in the compute hosts, the definition of a + NUMA topology allows hosts to better utilize NUMA and improve performance of + the instance OS. + + .. code-block:: console + + $ openstack flavor set FLAVOR-NAME \ + --property hw:numa_nodes=FLAVOR-NODES \ + --property hw:numa_cpus.N=FLAVOR-CORES \ + --property hw:numa_mem.N=FLAVOR-MEMORY + + Where: + + - FLAVOR-NODES: (integer) The number of host NUMA nodes to restrict execution + of instance vCPU threads to. If not specified, the vCPU threads can run on + any number of the host NUMA nodes available. + - N: (integer) The instance NUMA node to apply a given CPU or memory + configuration to, where N is in the range ``0`` to ``FLAVOR-NODES - 1``. + - FLAVOR-CORES: (comma-separated list of integers) A list of instance vCPUs + to map to instance NUMA node N. If not specified, vCPUs are evenly divided + among available NUMA nodes. + - FLAVOR-MEMORY: (integer) The number of MB of instance memory to map to + instance NUMA node N. If not specified, memory is evenly divided among + available NUMA nodes. + + .. note:: + + ``hw:numa_cpus.N`` and ``hw:numa_mem.N`` are only valid if + ``hw:numa_nodes`` is set. Additionally, they are only required if the + instance's NUMA nodes have an asymmetrical allocation of CPUs and RAM + (important for some NFV workloads). + + .. note:: + + The ``N`` parameter is an index of *guest* NUMA nodes and may not + correspond to *host* NUMA nodes. For example, on a platform with two NUMA + nodes, the scheduler may opt to place guest NUMA node 0, as referenced in + ``hw:numa_mem.0`` on host NUMA node 1 and vice versa. Similarly, the + integers used for ``FLAVOR-CORES`` are indexes of *guest* vCPUs and may + not correspond to *host* CPUs. As such, this feature cannot be used to + constrain instances to specific host CPUs or NUMA nodes. + + .. warning:: + + If the combined values of ``hw:numa_cpus.N`` or ``hw:numa_mem.N`` are + greater than the available number of CPUs or memory respectively, an + exception is raised. + +Large pages allocation + You can configure the size of large pages used to back the VMs. + + .. code:: console + + $ openstack flavor set FLAVOR-NAME \ + --property hw:mem_page_size=PAGE_SIZE + + Valid ``PAGE_SIZE`` values are: + + - ``small``: (default) The smallest page size is used. Example: 4 KB on x86. + - ``large``: Only use larger page sizes for guest RAM. Example: either 2 MB + or 1 GB on x86. + - ``any``: It is left up to the compute driver to decide. In this case, the + libvirt driver might try to find large pages, but fall back to small pages. + Other drivers may choose alternate policies for ``any``. + - pagesize: (string) An explicit page size can be set if the workload has + specific requirements. This value can be an integer value for the page size + in KB, or can use any standard suffix. Example: ``4KB``, ``2MB``, + ``2048``, ``1GB``. + + .. note:: + + Large pages can be enabled for guest RAM without any regard to whether the + guest OS will use them or not. If the guest OS chooses not to use huge + pages, it will merely see small pages as before. Conversely, if a guest OS + does intend to use huge pages, it is very important that the guest RAM be + backed by huge pages. Otherwise, the guest OS will not be getting the + performance benefit it is expecting. + +PCI passthrough + You can assign PCI devices to a guest by specifying them in the flavor. + + .. code:: console + + $ openstack flavor set FLAVOR-NAME \ + --property pci_passthrough:alias=ALIAS:COUNT + + Where: + + - ALIAS: (string) The alias which correspond to a particular PCI device class + as configured in the nova configuration file (see + :doc:`/configuration/config`). + - COUNT: (integer) The amount of PCI devices of type ALIAS to be assigned to + a guest. + +Secure Boot + When your Compute services use the Hyper-V hypervisor, you can enable secure + boot for Windows and Linux instances. + + .. code:: console + + $ openstack flavor set FLAVOR-NAME \ + --property os:secure_boot=SECURE_BOOT_OPTION + + Valid ``SECURE_BOOT_OPTION`` values are: + + - ``required``: Enable Secure Boot for instances running with this flavor. + - ``disabled`` or ``optional``: (default) Disable Secure Boot for instances + running with this flavor. diff --git a/doc/source/admin/flavors2.rst b/doc/source/admin/flavors2.rst new file mode 100644 index 00000000000..6bc48ad2fab --- /dev/null +++ b/doc/source/admin/flavors2.rst @@ -0,0 +1,155 @@ +============== +Manage flavors +============== + +.. todo:: Merge this into 'flavors' + +In OpenStack, flavors define the compute, memory, and storage capacity of nova +computing instances. To put it simply, a flavor is an available hardware +configuration for a server. It defines the *size* of a virtual server that can +be launched. + +.. note:: + + Flavors can also determine on which compute host a flavor can be used to + launch an instance. For information about customizing flavors, refer to + :doc:`flavors`. + +A flavor consists of the following parameters: + +Flavor ID + Unique ID (integer or UUID) for the new flavor. If specifying 'auto', a UUID + will be automatically generated. + +Name + Name for the new flavor. + +VCPUs + Number of virtual CPUs to use. + +Memory MB + Amount of RAM to use (in megabytes). + +Root Disk GB + Amount of disk space (in gigabytes) to use for the root (``/``) partition. + +Ephemeral Disk GB + Amount of disk space (in gigabytes) to use for the ephemeral partition. If + unspecified, the value is ``0`` by default. Ephemeral disks offer machine + local disk storage linked to the lifecycle of a VM instance. When a VM is + terminated, all data on the ephemeral disk is lost. Ephemeral disks are not + included in any snapshots. + +Swap + Amount of swap space (in megabytes) to use. If unspecified, the value is + ``0`` by default. + +RXTX Factor + Optional property that allows servers with a different bandwidth be created + with the RXTX Factor. The default value is ``1.0``. That is, the new + bandwidth is the same as that of the attached network. The RXTX Factor is + available only for Xen or NSX based systems. + +Is Public + Boolean value defines whether the flavor is available to all users. Defaults + to ``True``. + +Extra Specs + Key and value pairs that define on which compute nodes a flavor can run. + These pairs must match corresponding pairs on the compute nodes. It can be + used to implement special resources, such as flavors that run on only compute + nodes with GPU hardware. + +As of Newton, there are no default flavors. The following table lists the +default flavors for Mitaka and earlier. + +============ ========= =============== =============== + Flavor VCPUs Disk (in GB) RAM (in MB) +============ ========= =============== =============== + m1.tiny 1 1 512 + m1.small 1 20 2048 + m1.medium 2 40 4096 + m1.large 4 80 8192 + m1.xlarge 8 160 16384 +============ ========= =============== =============== + +You can create and manage flavors with the :command:`openstack flavor` commands +provided by the ``python-openstackclient`` package. + +Create a flavor +~~~~~~~~~~~~~~~ + +#. List flavors to show the ID and name, the amount of memory, the amount of + disk space for the root partition and for the ephemeral partition, the swap, + and the number of virtual CPUs for each flavor: + + .. code-block:: console + + $ openstack flavor list + +#. To create a flavor, specify a name, ID, RAM size, disk size, and the number + of VCPUs for the flavor, as follows: + + .. code-block:: console + + $ openstack flavor create FLAVOR_NAME --id FLAVOR_ID \ + --ram RAM_IN_MB --disk ROOT_DISK_IN_GB --vcpus NUMBER_OF_VCPUS + + .. note:: + + Unique ID (integer or UUID) for the new flavor. If specifying 'auto', a + UUID will be automatically generated. + + Here is an example with additional optional parameters filled in that + creates a public ``extra_tiny`` flavor that automatically gets an ID + assigned, with 256 MB memory, no disk space, and one VCPU. The rxtx-factor + indicates the slice of bandwidth that the instances with this flavor can use + (through the Virtual Interface (vif) creation in the hypervisor): + + .. code-block:: console + + $ openstack flavor create --public m1.extra_tiny --id auto \ + --ram 256 --disk 0 --vcpus 1 --rxtx-factor 1 + +#. If an individual user or group of users needs a custom flavor that you do + not want other projects to have access to, you can change the flavor's + access to make it a private flavor. See `Private Flavors in the OpenStack + Operations Guide + `_. + + For a list of optional parameters, run this command: + + .. code-block:: console + + $ openstack help flavor create + +#. After you create a flavor, assign it to a project by specifying the flavor + name or ID and the project ID: + + .. code-block:: console + + $ nova flavor-access-add FLAVOR TENANT_ID + +#. In addition, you can set or unset ``extra_spec`` for the existing flavor. + The ``extra_spec`` metadata keys can influence the instance directly when it + is launched. If a flavor sets the ``extra_spec key/value + quota:vif_outbound_peak=65536``, the instance's outbound peak bandwidth I/O + should be less than or equal to 512 Mbps. There are several aspects that can + work for an instance including *CPU limits*, *Disk tuning*, *Bandwidth I/O*, + *Watchdog behavior*, and *Random-number generator*. For information about + supporting metadata keys, see :doc:`flavors`. + + For a list of optional parameters, run this command: + + .. code-block:: console + + $ nova help flavor-key + +Delete a flavor +~~~~~~~~~~~~~~~ + +Delete a specified flavor, as follows: + +.. code-block:: console + + $ openstack flavor delete FLAVOR_ID diff --git a/doc/source/admin/huge-pages.rst b/doc/source/admin/huge-pages.rst new file mode 100644 index 00000000000..cf7719926ae --- /dev/null +++ b/doc/source/admin/huge-pages.rst @@ -0,0 +1,239 @@ +========== +Huge pages +========== + +The huge page feature in OpenStack provides important performance improvements +for applications that are highly memory IO-bound. + +.. note:: + + Huge pages may also be referred to hugepages or large pages, depending on + the source. These terms are synonyms. + +Pages, the TLB and huge pages +----------------------------- + +Pages + Physical memory is segmented into a series of contiguous regions called + pages. Each page contains a number of bytes, referred to as the page size. + The system retrieves memory by accessing entire pages, rather than byte by + byte. + +Translation Lookaside Buffer (TLB) + A TLB is used to map the virtual addresses of pages to the physical addresses + in actual memory. The TLB is a cache and is not limitless, storing only the + most recent or frequently accessed pages. During normal operation, processes + will sometimes attempt to retrieve pages that are not stored in the cache. + This is known as a TLB miss and results in a delay as the processor iterates + through the pages themselves to find the missing address mapping. + +Huge Pages + The standard page size in x86 systems is 4 kB. This is optimal for general + purpose computing but larger page sizes - 2 MB and 1 GB - are also available. + These larger page sizes are known as huge pages. Huge pages result in less + efficient memory usage as a process will not generally use all memory + available in each page. However, use of huge pages will result in fewer + overall pages and a reduced risk of TLB misses. For processes that have + significant memory requirements or are memory intensive, the benefits of huge + pages frequently outweigh the drawbacks. + +Persistent Huge Pages + On Linux hosts, persistent huge pages are huge pages that are reserved + upfront. The HugeTLB provides for the mechanism for this upfront + configuration of huge pages. The HugeTLB allows for the allocation of varying + quantities of different huge page sizes. Allocation can be made at boot time + or run time. Refer to the `Linux hugetlbfs guide`_ for more information. + +Transparent Huge Pages (THP) + On Linux hosts, transparent huge pages are huge pages that are automatically + provisioned based on process requests. Transparent huge pages are provisioned + on a best effort basis, attempting to provision 2 MB huge pages if available + but falling back to 4 kB small pages if not. However, no upfront + configuration is necessary. Refer to the `Linux THP guide`_ for more + information. + +Enabling huge pages on the host +------------------------------- + +Persistent huge pages are required owing to their guaranteed availability. +However, persistent huge pages are not enabled by default in most environments. +The steps for enabling huge pages differ from platform to platform and only the +steps for Linux hosts are described here. On Linux hosts, the number of +persistent huge pages on the host can be queried by checking ``/proc/meminfo``: + +.. code-block:: console + + $ grep Huge /proc/meminfo + AnonHugePages: 0 kB + ShmemHugePages: 0 kB + HugePages_Total: 0 + HugePages_Free: 0 + HugePages_Rsvd: 0 + HugePages_Surp: 0 + Hugepagesize: 2048 kB + +In this instance, there are 0 persistent huge pages (``HugePages_Total``) and 0 +transparent huge pages (``AnonHugePages``) allocated. Huge pages can be +allocated at boot time or run time. Huge pages require a contiguous area of +memory - memory that gets increasingly fragmented the long a host is running. +Identifying contiguous areas of memory is a issue for all huge page sizes, but +it is particularly problematic for larger huge page sizes such as 1 GB huge +pages. Allocating huge pages at boot time will ensure the correct number of huge +pages is always available, while allocating them at run time can fail if memory +has become too fragmented. + +To allocate huge pages at run time, the kernel boot parameters must be extended +to include some huge page-specific parameters. This can be achieved by +modifying ``/etc/default/grub`` and appending the ``hugepagesz``, +``hugepages``, and ``transparent_hugepages=never`` arguments to +``GRUB_CMDLINE_LINUX``. To allocate, for example, 2048 persistent 2 MB huge +pages at boot time, run: + +.. code-block:: console + + # echo 'GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX hugepagesz=2M hugepages=2048 transparent_hugepage=never"' > /etc/default/grub + $ grep GRUB_CMDLINE_LINUX /etc/default/grub + GRUB_CMDLINE_LINUX="..." + GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX hugepagesz=2M hugepages=2048 transparent_hugepage=never" + +.. important:: + + Persistent huge pages are not usable by standard host OS processes. Ensure + enough free, non-huge page memory is reserved for these processes. + +Reboot the host, then validate that huge pages are now available: + +.. code-block:: console + + $ grep "Huge" /proc/meminfo + AnonHugePages: 0 kB + ShmemHugePages: 0 kB + HugePages_Total: 2048 + HugePages_Free: 2048 + HugePages_Rsvd: 0 + HugePages_Surp: 0 + Hugepagesize: 2048 kB + +There are now 2048 2 MB huge pages totalling 4 GB of huge pages. These huge +pages must be mounted. On most platforms, this happens automatically. To verify +that the huge pages are mounted, run: + +.. code-block:: console + + # mount | grep huge + hugetlbfs on /dev/hugepages type hugetlbfs (rw) + +In this instance, the huge pages are mounted at ``/dev/hugepages``. This mount +point varies from platform to platform. If the above command did not return +anything, the hugepages must be mounted manually. To mount the huge pages at +``/dev/hugepages``, run: + +.. code-block:: console + + # mkdir -p /dev/hugepages + # mount -t hugetlbfs hugetlbfs /dev/hugepages + +There are many more ways to configure huge pages, including allocating huge +pages at run time, specifying varying allocations for different huge page +sizes, or allocating huge pages from memory affinitized to different NUMA +nodes. For more information on configuring huge pages on Linux hosts, refer to +the `Linux hugetlbfs guide`_. + +Customizing instance huge pages allocations +------------------------------------------- + +.. important:: + + The functionality described below is currently only supported by the + libvirt/KVM driver. + +.. important:: + + For performance reasons, configuring huge pages for an instance will + implicitly result in a NUMA topology being configured for the instance. + Configuring a NUMA topology for an instance requires enablement of + ``NUMATopologyFilter``. Refer to :doc:`cpu-topologies` for more + information. + +By default, an instance does not use huge pages for its underlying memory. +However, huge pages can bring important or required performance improvements +for some workloads. Huge pages must be requested explicitly through the use of +flavor extra specs or image metadata. To request an instance use huge pages, +run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:mem_page_size=large + +Different platforms offer different huge page sizes. For example: x86-based +platforms offer 2 MB and 1 GB huge page sizes. Specific huge page sizes can be +also be requested, with or without a unit suffix. The unit suffix must be one +of: Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it), KB, +KiB, MB, MiB, GB, GiB, TB, TiB. Where a unit suffix is not provided, Kilobytes +are assumed. To request an instance to use 2 MB huge pages, run one of: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:mem_page_size=2Mb + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:mem_page_size=2048 + +Enabling huge pages for an instance can have negative consequences for other +instances by consuming limited huge pages resources. To explicitly request +an instance use small pages, run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:mem_page_size=small + +.. note:: + + Explicitly requesting any page size will still result in a NUMA topology + being applied to the instance, as described earlier in this document. + +Finally, to leave the decision of huge or small pages to the compute driver, +run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:mem_page_size=any + +For more information about the syntax for ``hw:mem_page_size``, refer to the +`Flavors`_ guide. + +Applications are frequently packaged as images. For applications that require +the IO performance improvements that huge pages provides, configure image +metadata to ensure instances always request the specific page size regardless +of flavor. To configure an image to use 1 GB huge pages, run: + +.. code-block:: console + + $ openstack image set [IMAGE_ID] --property hw_mem_page_size=1GB + +If the flavor specifies a numerical page size or a page size of "small" the +image is not allowed to specify a page size and if it does an exception will +be raised. If the flavor specifies a page size of ``any`` or ``large`` then +any page size specified in the image will be used. By setting a ``small`` +page size in the flavor, administrators can prevent users requesting huge +pages in flavors and impacting resource utilization. To configure this page +size, run: + +.. code-block:: console + + $ openstack flavor set m1.large --property hw:mem_page_size=small + +.. note:: + + Explicitly requesting any page size will still result in a NUMA topology + being applied to the instance, as described earlier in this document. + +For more information about image metadata, refer to the `Image metadata`_ +guide. + +.. Links +.. _`Linux THP guide`: https://www.kernel.org/doc/Documentation/vm/transhuge.txt +.. _`Linux hugetlbfs guide`: https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt +.. _`Flavors`: https://docs.openstack.org/admin-guide/compute-flavors.html +.. _`Image metadata`: https://docs.openstack.org/image-guide/image-metadata.html diff --git a/doc/source/admin/index.rst b/doc/source/admin/index.rst new file mode 100644 index 00000000000..008c86f67c2 --- /dev/null +++ b/doc/source/admin/index.rst @@ -0,0 +1,49 @@ +======= +Compute +======= + +The OpenStack Compute service allows you to control an +Infrastructure-as-a-Service (IaaS) cloud computing platform. It gives you +control over instances and networks, and allows you to manage access to the +cloud through users and projects. + +Compute does not include virtualization software. Instead, it defines drivers +that interact with underlying virtualization mechanisms that run on your host +operating system, and exposes functionality over a web-based API. + +.. toctree:: + :maxdepth: 2 + + admin-password-injection.rst + adv-config.rst + arch.rst + availability-zones.rst + configuring-migrations.rst + cpu-topologies.rst + default-ports.rst + euca2ools.rst + evacuate.rst + flavors2.rst + flavors.rst + huge-pages.rst + live-migration-usage.rst + manage-logs.rst + manage-the-cloud.rst + manage-users.rst + manage-volumes.rst + migration.rst + networking-nova.rst + node-down.rst + numa.rst + pci-passthrough.rst + quotas2.rst + quotas.rst + remote-console-access.rst + root-wrap-reference.rst + security-groups.rst + security.rst + service-groups.rst + services.rst + ssh-configuration.rst + support-compute.rst + system-admin.rst diff --git a/doc/source/admin/live-migration-usage.rst b/doc/source/admin/live-migration-usage.rst new file mode 100644 index 00000000000..7ff74a68a3a --- /dev/null +++ b/doc/source/admin/live-migration-usage.rst @@ -0,0 +1,314 @@ +====================== +Live-migrate instances +====================== + +Live-migrating an instance means moving its virtual machine to a different +OpenStack Compute server while the instance continues running. Before starting +a live-migration, review the chapter +:ref:`section_configuring-compute-migrations`. It covers the configuration +settings required to enable live-migration, but also reasons for migrations and +non-live-migration options. + +The instructions below cover shared-storage and volume-backed migration. To +block-migrate instances, add the command-line option +``-block-migrate`` to the :command:`nova live-migration` command, +and ``--block-migration`` to the :command:`openstack server migrate` +command. + +.. _section-manual-selection-of-dest: + +Manual selection of the destination host +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Obtain the ID of the instance you want to migrate: + + .. code-block:: console + + $ openstack server list + + +--------------------------------------+------+--------+-----------------+------------+ + | ID | Name | Status | Networks | Image Name | + +--------------------------------------+------+--------+-----------------+------------+ + | d1df1b5a-70c4-4fed-98b7-423362f2c47c | vm1 | ACTIVE | private=a.b.c.d | ... | + | d693db9e-a7cf-45ef-a7c9-b3ecb5f22645 | vm2 | ACTIVE | private=e.f.g.h | ... | + +--------------------------------------+------+--------+-----------------+------------+ + +#. Determine on which host the instance is currently running. In this example, + ``vm1`` is running on ``HostB``: + + .. code-block:: console + + $ openstack server show d1df1b5a-70c4-4fed-98b7-423362f2c47c + + +----------------------+--------------------------------------+ + | Field | Value | + +----------------------+--------------------------------------+ + | ... | ... | + | OS-EXT-SRV-ATTR:host | HostB | + | ... | ... | + | addresses | a.b.c.d | + | flavor | m1.tiny | + | id | d1df1b5a-70c4-4fed-98b7-423362f2c47c | + | name | vm1 | + | status | ACTIVE | + | ... | ... | + +----------------------+--------------------------------------+ + +#. Select the compute node the instance will be migrated to. In this example, + we will migrate the instance to ``HostC``, because ``nova-compute`` is + running on it: + + .. code-block:: console + + $ openstack compute service list + + +----+------------------+-------+----------+---------+-------+----------------------------+ + | ID | Binary | Host | Zone | Status | State | Updated At | + +----+------------------+-------+----------+---------+-------+----------------------------+ + | 3 | nova-conductor | HostA | internal | enabled | up | 2017-02-18T09:42:29.000000 | + | 4 | nova-scheduler | HostA | internal | enabled | up | 2017-02-18T09:42:26.000000 | + | 5 | nova-consoleauth | HostA | internal | enabled | up | 2017-02-18T09:42:29.000000 | + | 6 | nova-compute | HostB | nova | enabled | up | 2017-02-18T09:42:29.000000 | + | 7 | nova-compute | HostC | nova | enabled | up | 2017-02-18T09:42:29.000000 | + +----+------------------+-------+----------+---------+-------+----------------------------+ + +#. Check that ``HostC`` has enough resources for migration: + + .. code-block:: console + + $ openstack host show HostC + + +-------+------------+-----+-----------+---------+ + | Host | Project | CPU | Memory MB | Disk GB | + +-------+------------+-----+-----------+---------+ + | HostC | (total) | 16 | 32232 | 878 | + | HostC | (used_now) | 22 | 21284 | 422 | + | HostC | (used_max) | 22 | 21284 | 422 | + | HostC | p1 | 22 | 21284 | 422 | + | HostC | p2 | 22 | 21284 | 422 | + +-------+------------+-----+-----------+---------+ + + - ``cpu``: Number of CPUs + + - ``memory_mb``: Total amount of memory, in MB + + - ``disk_gb``: Total amount of space for NOVA-INST-DIR/instances, in GB + + In this table, the first row shows the total amount of resources available + on the physical server. The second line shows the currently used resources. + The third line shows the maximum used resources. The fourth line and below + shows the resources available for each project. + +#. Migrate the instance: + + .. code-block:: console + + $ openstack server migrate d1df1b5a-70c4-4fed-98b7-423362f2c47c --live HostC + +#. Confirm that the instance has been migrated successfully: + + .. code-block:: console + + $ openstack server show d1df1b5a-70c4-4fed-98b7-423362f2c47c + + +----------------------+--------------------------------------+ + | Field | Value | + +----------------------+--------------------------------------+ + | ... | ... | + | OS-EXT-SRV-ATTR:host | HostC | + | ... | ... | + +----------------------+--------------------------------------+ + + If the instance is still running on ``HostB``, the migration failed. The + ``nova-scheduler`` and ``nova-conductor`` log files on the controller and + the ``nova-compute`` log file on the source compute host can help pin-point + the problem. + +.. _auto_selection_of_dest: + +Automatic selection of the destination host +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To leave the selection of the destination host to the Compute service, use the +nova command-line client. + +#. Obtain the instance ID as shown in step 1 of the section + :ref:`section-manual-selection-of-dest`. + +#. Leave out the host selection steps 2, 3, and 4. + +#. Migrate the instance: + + .. code-block:: console + + $ nova live-migration d1df1b5a-70c4-4fed-98b7-423362f2c47c + +Monitoring the migration +~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Confirm that the instance is migrating: + + .. code-block:: console + + $ openstack server show d1df1b5a-70c4-4fed-98b7-423362f2c47c + + +----------------------+--------------------------------------+ + | Field | Value | + +----------------------+--------------------------------------+ + | ... | ... | + | status | MIGRATING | + | ... | ... | + +----------------------+--------------------------------------+ + +#. Check progress + + Use the nova command-line client for nova's migration monitoring feature. + First, obtain the migration ID: + + .. code-block:: console + + $ nova server-migration-list d1df1b5a-70c4-4fed-98b7-423362f2c47c + +----+-------------+----------- (...) + | Id | Source Node | Dest Node | (...) + +----+-------------+-----------+ (...) + | 2 | - | - | (...) + +----+-------------+-----------+ (...) + + For readability, most output columns were removed. Only the first column, + **Id**, is relevant. In this example, the migration ID is 2. Use this to + get the migration status. + + .. code-block:: console + + $ nova server-migration-show d1df1b5a-70c4-4fed-98b7-423362f2c47c 2 + +------------------------+--------------------------------------+ + | Property | Value | + +------------------------+--------------------------------------+ + | created_at | 2017-03-08T02:53:06.000000 | + | dest_compute | controller | + | dest_host | - | + | dest_node | - | + | disk_processed_bytes | 0 | + | disk_remaining_bytes | 0 | + | disk_total_bytes | 0 | + | id | 2 | + | memory_processed_bytes | 65502513 | + | memory_remaining_bytes | 786427904 | + | memory_total_bytes | 1091379200 | + | server_uuid | d1df1b5a-70c4-4fed-98b7-423362f2c47c | + | source_compute | compute2 | + | source_node | - | + | status | running | + | updated_at | 2017-03-08T02:53:47.000000 | + +------------------------+--------------------------------------+ + + The output shows that the migration is running. Progress is measured by the + number of memory bytes that remain to be copied. If this number is not + decreasing over time, the migration may be unable to complete, and it may be + aborted by the Compute service. + + .. note:: + + The command reports that no disk bytes are processed, even in the event + of block migration. + +What to do when the migration times out +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +During the migration process, the instance may write to a memory page after +that page has been copied to the destination. When that happens, the same page +has to be copied again. The instance may write to memory pages faster than they +can be copied, so that the migration cannot complete. The Compute service will +cancel it when the ``live_migration_completion_timeout``, a configuration +parameter, is reached. + +The following remarks assume the KVM/Libvirt hypervisor. + +How to know that the migration timed out +---------------------------------------- + +To determine that the migration timed out, inspect the ``nova-compute`` log +file on the source host. The following log entry shows that the migration timed +out: + +.. code-block:: console + + # grep WARNING.*d1df1b5a-70c4-4fed-98b7-423362f2c47c /var/log/nova/nova-compute.log + ... + WARNING nova.virt.libvirt.migration [req-...] [instance: ...] + live migration not completed after 1800 sec + +The Compute service also cancels migrations when the memory copy seems to make +no progress. Ocata disables this feature by default, but it can be enabled +using the configuration parameter ``live_migration_progress_timeout``. Should +this be the case, you may find the following message in the log: + +.. code-block:: console + + WARNING nova.virt.libvirt.migration [req-...] [instance: ...] + live migration stuck for 150 sec + +Addressing migration timeouts +----------------------------- + +To stop the migration from putting load on infrastructure resources like +network and disks, you may opt to cancel it manually. + +.. code-block:: console + + $ nova live-migration-abort INSTANCE_ID MIGRATION_ID + +To make live-migration succeed, you have several options: + +- **Manually force-complete the migration** + + .. code-block:: console + + $ nova live-migration-force-complete INSTANCE_ID MIGRATION_ID + + The instance is paused until memory copy completes. + + .. caution:: + + Since the pause impacts time keeping on the instance and not all + applications tolerate incorrect time settings, use this approach with + caution. + +- **Enable auto-convergence** + + Auto-convergence is a Libvirt feature. Libvirt detects that the migration is + unlikely to complete and slows down its CPU until the memory copy process is + faster than the instance's memory writes. + + To enable auto-convergence, set + ``live_migration_permit_auto_convergence=true`` in ``nova.conf`` and restart + ``nova-compute``. Do this on all compute hosts. + + .. caution:: + + One possible downside of auto-convergence is the slowing down of the + instance. + +- **Enable post-copy** + + This is a Libvirt feature. Libvirt detects that the migration does not + progress and responds by activating the virtual machine on the destination + host before all its memory has been copied. Access to missing memory pages + result in page faults that are satisfied from the source host. + + To enable post-copy, set ``live_migration_permit_post_copy=true`` in + ``nova.conf`` and restart ``nova-compute``. Do this on all compute hosts. + + When post-copy is enabled, manual force-completion does not pause the + instance but switches to the post-copy process. + + .. caution:: + + Possible downsides: + + - When the network connection between source and destination is + interrupted, page faults cannot be resolved anymore, and the virtual + machine is rebooted. + + - Post-copy may lead to an increased page fault rate during migration, + which can slow the instance down. diff --git a/doc/source/admin/manage-logs.rst b/doc/source/admin/manage-logs.rst new file mode 100644 index 00000000000..a4e3c33fbfa --- /dev/null +++ b/doc/source/admin/manage-logs.rst @@ -0,0 +1,230 @@ +======= +Logging +======= + +Logging module +~~~~~~~~~~~~~~ + +Logging behavior can be changed by creating a configuration file. To specify +the configuration file, add this line to the ``/etc/nova/nova.conf`` file: + +.. code-block:: ini + + log-config=/etc/nova/logging.conf + +To change the logging level, add ``DEBUG``, ``INFO``, ``WARNING``, or ``ERROR`` +as a parameter. + +The logging configuration file is an INI-style configuration file, which must +contain a section called ``logger_nova``. This controls the behavior of the +logging facility in the ``nova-*`` services. For example: + +.. code-block:: ini + + [logger_nova] + level = INFO + handlers = stderr + qualname = nova + +This example sets the debugging level to ``INFO`` (which is less verbose than +the default ``DEBUG`` setting). + +For more about the logging configuration syntax, including the ``handlers`` and +``quaname`` variables, see the `Python documentation +`__ +on logging configuration files. + +For an example of the ``logging.conf`` file with various defined handlers, see +the `Example Configuration File for nova +`__. + +Syslog +~~~~~~ + +OpenStack Compute services can send logging information to syslog. This is +useful if you want to use rsyslog to forward logs to a remote machine. +Separately configure the Compute service (nova), the Identity service +(keystone), the Image service (glance), and, if you are using it, the Block +Storage service (cinder) to send log messages to syslog. Open these +configuration files: + +- ``/etc/nova/nova.conf`` + +- ``/etc/keystone/keystone.conf`` + +- ``/etc/glance/glance-api.conf`` + +- ``/etc/glance/glance-registry.conf`` + +- ``/etc/cinder/cinder.conf`` + +In each configuration file, add these lines: + +.. code-block:: ini + + debug = False + use_syslog = True + syslog_log_facility = LOG_LOCAL0 + +In addition to enabling syslog, these settings also turn off debugging output +from the log. + +.. note:: + + Although this example uses the same local facility for each service + (``LOG_LOCAL0``, which corresponds to syslog facility ``LOCAL0``), we + recommend that you configure a separate local facility for each service, as + this provides better isolation and more flexibility. For example, you can + capture logging information at different severity levels for different + services. syslog allows you to define up to eight local facilities, + ``LOCAL0, LOCAL1, ..., LOCAL7``. For more information, see the syslog + documentation. + +Rsyslog +~~~~~~~ + +rsyslog is useful for setting up a centralized log server across multiple +machines. This section briefly describe the configuration to set up an rsyslog +server. A full treatment of rsyslog is beyond the scope of this book. This +section assumes rsyslog has already been installed on your hosts (it is +installed by default on most Linux distributions). + +This example provides a minimal configuration for ``/etc/rsyslog.conf`` on the +log server host, which receives the log files + +.. code-block:: console + + # provides TCP syslog reception + $ModLoad imtcp + $InputTCPServerRun 1024 + +Add a filter rule to ``/etc/rsyslog.conf`` which looks for a host name. This +example uses COMPUTE_01 as the compute host name: + +.. code-block:: none + + :hostname, isequal, "COMPUTE_01" /mnt/rsyslog/logs/compute-01.log + +On each compute host, create a file named ``/etc/rsyslog.d/60-nova.conf``, with +the following content: + +.. code-block:: none + + # prevent debug from dnsmasq with the daemon.none parameter + *.*;auth,authpriv.none,daemon.none,local0.none -/var/log/syslog + # Specify a log level of ERROR + local0.error @@172.20.1.43:1024 + +Once you have created the file, restart the ``rsyslog`` service. Error-level +log messages on the compute hosts should now be sent to the log server. + +Serial console +~~~~~~~~~~~~~~ + +The serial console provides a way to examine kernel output and other system +messages during troubleshooting if the instance lacks network connectivity. + +Read-only access from server serial console is possible using the +``os-GetSerialOutput`` server action. Most cloud images enable this feature by +default. For more information, see :ref:`compute-common-errors-and-fixes`. + +OpenStack Juno and later supports read-write access using the serial console +using the ``os-GetSerialConsole`` server action. This feature also requires a +websocket client to access the serial console. + +.. rubric:: Configuring read-write serial console access + +#. On a compute node, edit the ``/etc/nova/nova.conf`` file: + + In the ``[serial_console]`` section, enable the serial console: + + .. code-block:: ini + + [serial_console] + # ... + enabled = true + +#. In the ``[serial_console]`` section, configure the serial console proxy + similar to graphical console proxies: + + .. code-block:: ini + + [serial_console] + # ... + base_url = ws://controller:6083/ + listen = 0.0.0.0 + proxyclient_address = MANAGEMENT_INTERFACE_IP_ADDRESS + + The ``base_url`` option specifies the base URL that clients receive from the + API upon requesting a serial console. Typically, this refers to the host + name of the controller node. + + The ``listen`` option specifies the network interface nova-compute should + listen on for virtual console connections. Typically, 0.0.0.0 will enable + listening on all interfaces. + + The ``proxyclient_address`` option specifies which network interface the + proxy should connect to. Typically, this refers to the IP address of the + management interface. + + When you enable read-write serial console access, Compute will add serial + console information to the Libvirt XML file for the instance. For example: + + .. code-block:: xml + + + + + + + + +.. rubric:: Accessing the serial console on an instance + +#. Use the :command:`nova get-serial-proxy` command to retrieve the websocket + URL for the serial console on the instance: + + .. code-block:: console + + $ nova get-serial-proxy INSTANCE_NAME + + .. list-table:: + :header-rows: 0 + :widths: 9 65 + + * - Type + - Url + * - serial + - ws://127.0.0.1:6083/?token=18510769-71ad-4e5a-8348-4218b5613b3d + + Alternatively, use the API directly: + + .. code-block:: console + + $ curl -i 'http://:8774/v2.1//servers//action' \ + -X POST \ + -H "Accept: application/json" \ + -H "Content-Type: application/json" \ + -H "X-Auth-Project-Id: " \ + -H "X-Auth-Token: " \ + -d '{"os-getSerialConsole": {"type": "serial"}}' + +#. Use Python websocket with the URL to generate ``.send``, ``.recv``, and + ``.fileno`` methods for serial console access. For example: + + .. code-block:: python + + import websocket + ws = websocket.create_connection( + 'ws://127.0.0.1:6083/?token=18510769-71ad-4e5a-8348-4218b5613b3d', + subprotocols=['binary', 'base64']) + +Alternatively, use a `Python websocket client +`__. + +.. note:: + + When you enable the serial console, typical instance logging using the + :command:`nova console-log` command is disabled. Kernel output and other + system messages will not be visible unless you are actively viewing the + serial console. diff --git a/doc/source/admin/manage-the-cloud.rst b/doc/source/admin/manage-the-cloud.rst new file mode 100644 index 00000000000..5a9de363120 --- /dev/null +++ b/doc/source/admin/manage-the-cloud.rst @@ -0,0 +1,69 @@ +.. _section_manage-the-cloud: + +================ +Manage the cloud +================ + +.. toctree:: + + euca2ools.rst + common/nova-show-usage-statistics-for-hosts-instances.rst + +System administrators can use the :command:`openstack` and :command:`euca2ools` +commands to manage their clouds. + +The ``openstack`` client and ``euca2ools`` can be used by all users, though +specific commands might be restricted by the Identity service. + +**Managing the cloud with the openstack client** + +#. The ``python-openstackclient`` package provides an ``openstack`` shell that + enables Compute API interactions from the command line. Install the client, + and provide your user name and password (which can be set as environment + variables for convenience), for the ability to administer the cloud from the + command line. + + To install python-openstackclient, follow the instructions in the `OpenStack + User Guide + `_. + +#. Confirm the installation was successful: + + .. code-block:: console + + $ openstack help + usage: openstack [--version] [-v | -q] [--log-file LOG_FILE] [-h] [--debug] + [--os-cloud ] + [--os-region-name ] + [--os-cacert ] [--verify | --insecure] + [--os-default-domain ] + ... + + Running :command:`openstack help` returns a list of ``openstack`` commands + and parameters. To get help for a subcommand, run: + + .. code-block:: console + + $ openstack help SUBCOMMAND + + For a complete list of ``openstack`` commands and parameters, see the + `OpenStack Command-Line Reference + `__. + +#. Set the required parameters as environment variables to make running + commands easier. For example, you can add ``--os-username`` as an + ``openstack`` option, or set it as an environment variable. To set the user + name, password, and project as environment variables, use: + + .. code-block:: console + + $ export OS_USERNAME=joecool + $ export OS_PASSWORD=coolword + $ export OS_TENANT_NAME=coolu + +#. The Identity service gives you an authentication endpoint, which Compute + recognizes as ``OS_AUTH_URL``: + + .. code-block:: console + + $ export OS_AUTH_URL=http://hostname:5000/v2.0 diff --git a/doc/source/admin/manage-users.rst b/doc/source/admin/manage-users.rst new file mode 100644 index 00000000000..41a925ff76f --- /dev/null +++ b/doc/source/admin/manage-users.rst @@ -0,0 +1,12 @@ +.. _section_manage-compute-users: + +==================== +Manage Compute users +==================== + +Access to the Euca2ools (ec2) API is controlled by an access key and a secret +key. The user's access key needs to be included in the request, and the request +must be signed with the secret key. Upon receipt of API requests, Compute +verifies the signature and runs commands on behalf of the user. + +To begin using Compute, you must create a user with the Identity service. diff --git a/doc/source/admin/manage-volumes.rst b/doc/source/admin/manage-volumes.rst new file mode 100644 index 00000000000..0b7cda8a2c7 --- /dev/null +++ b/doc/source/admin/manage-volumes.rst @@ -0,0 +1,53 @@ +============== +Manage volumes +============== + +Depending on the setup of your cloud provider, they may give you an endpoint to +use to manage volumes, or there may be an extension under the covers. In either +case, you can use the ``openstack`` CLI to manage volumes. + +.. list-table:: **openstack volume commands** + :header-rows: 1 + + * - Command + - Description + * - server add volume + - Attach a volume to a server. + * - volume create + - Add a new volume. + * - volume delete + - Remove or delete a volume. + * - server remove volume + - Detach or remove a volume from a server. + * - volume list + - List all the volumes. + * - volume show + - Show details about a volume. + * - snapshot create + - Add a new snapshot. + * - snapshot delete + - Remove a snapshot. + * - snapshot list + - List all the snapshots. + * - snapshot show + - Show details about a snapshot. + * - volume type create + - Create a new volume type. + * - volume type delete + - Delete a specific flavor + * - volume type list + - Print a list of available 'volume types'. + +| + +For example, to list IDs and names of volumes, run: + +.. code-block:: console + + $ openstack volume list + +--------+--------------+-----------+------+-------------+ + | ID | Display Name | Status | Size | Attached to | + +--------+--------------+-----------+------+-------------+ + | 86e6cb | testnfs | available | 1 | | + | e389f7 | demo | available | 1 | | + +--------+--------------+-----------+------+-------------+ diff --git a/doc/source/admin/migration.rst b/doc/source/admin/migration.rst new file mode 100644 index 00000000000..613462c9980 --- /dev/null +++ b/doc/source/admin/migration.rst @@ -0,0 +1,79 @@ +================= +Migrate instances +================= + +When you want to move an instance from one compute host to another, you can use +the :command:`openstack server migrate` command. The scheduler chooses the +destination compute host based on its settings. This process does not assume +that the instance has shared storage available on the target host. If you are +using SSH tunneling, you must ensure that each node is configured with SSH key +authentication so that the Compute service can use SSH to move disks to other +nodes. For more information, see :ref:`cli-os-migrate-cfg-ssh`. + +#. To list the VMs you want to migrate, run: + + .. code-block:: console + + $ openstack server list + +#. Use the :command:`openstack server migrate` command. + + .. code-block:: console + + $ openstack server migrate --live TARGET_HOST VM_INSTANCE + +#. To migrate an instance and watch the status, use this example script: + + .. code-block:: bash + + #!/bin/bash + + # Provide usage + usage() { + echo "Usage: $0 VM_ID" + exit 1 + } + + [[ $# -eq 0 ]] && usage + + # Migrate the VM to an alternate hypervisor + echo -n "Migrating instance to alternate host" + VM_ID=$1 + openstack server migrate $VM_ID + VM_OUTPUT=$(openstack server show $VM_ID) + VM_STATUS=$(echo "$VM_OUTPUT" | grep status | awk '{print $4}') + while [[ "$VM_STATUS" != "VERIFY_RESIZE" ]]; do + echo -n "." + sleep 2 + VM_OUTPUT=$(openstack server show $VM_ID) + VM_STATUS=$(echo "$VM_OUTPUT" | grep status | awk '{print $4}') + done + nova resize-confirm $VM_ID + echo " instance migrated and resized." + echo; + + # Show the details for the VM + echo "Updated instance details:" + openstack server show $VM_ID + + # Pause to allow users to examine VM details + read -p "Pausing, press to exit." + +.. note:: + + If you see the following error, it means you are either running the command + with the wrong credentials, such as a non-admin user, or the ``policy.json`` + file prevents migration for your user:: + + ERROR (Forbidden): Policy doesn't allow compute_extension:admin_actions:migrate to be performed. (HTTP 403)`` + +.. note:: + + If you see the following error, similar to this message, SSH tunneling was + not set up between the compute nodes:: + + ProcessExecutionError: Unexpected error while running command. + Stderr: u Host key verification failed.\r\n + +The instance is booted from a new host, but preserves its configuration +including instance ID, name, IP address, any metadata, and other properties. diff --git a/doc/source/admin/networking-nova.rst b/doc/source/admin/networking-nova.rst new file mode 100644 index 00000000000..9da41129e66 --- /dev/null +++ b/doc/source/admin/networking-nova.rst @@ -0,0 +1,1019 @@ +============================ +Networking with nova-network +============================ + +.. important:: + + ``nova-network`` was deprecated in the OpenStack Newton release. In Ocata + and future releases, you can start ``nova-network`` only with a cells v1 + configuration. This is not a recommended configuration for deployment. + +Understanding the networking configuration options helps you design the best +configuration for your Compute instances. + +You can choose to either install and configure ``nova-network`` or use the +OpenStack Networking service (neutron). This section contains a brief overview +of ``nova-network``. For more information about OpenStack Networking, see +`Networking `_. + +Networking concepts +~~~~~~~~~~~~~~~~~~~ + +Compute assigns a private IP address to each VM instance. Compute makes a +distinction between fixed IPs and floating IP. Fixed IPs are IP addresses that +are assigned to an instance on creation and stay the same until the instance is +explicitly terminated. Floating IPs are addresses that can be dynamically +associated with an instance. A floating IP address can be disassociated and +associated with another instance at any time. A user can reserve a floating IP +for their project. + +.. note:: + + Currently, Compute with ``nova-network`` only supports Linux bridge + networking that allows virtual interfaces to connect to the outside network + through the physical interface. + +The network controller with ``nova-network`` provides virtual networks to +enable compute servers to interact with each other and with the public network. +Compute with ``nova-network`` supports the following network modes, which are +implemented as Network Manager types: + +Flat Network Manager + In this mode, a network administrator specifies a subnet. IP addresses for VM + instances are assigned from the subnet, and then injected into the image on + launch. Each instance receives a fixed IP address from the pool of available + addresses. A system administrator must create the Linux networking bridge + (typically named ``br100``, although this is configurable) on the systems + running the ``nova-network`` service. All instances of the system are + attached to the same bridge, which is configured manually by the network + administrator. + +.. note:: + + Configuration injection currently only works on Linux-style systems that + keep networking configuration in ``/etc/network/interfaces``. + +Flat DHCP Network Manager + In this mode, OpenStack starts a DHCP server (dnsmasq) to allocate IP + addresses to VM instances from the specified subnet, in addition to manually + configuring the networking bridge. IP addresses for VM instances are assigned + from a subnet specified by the network administrator. + + Like flat mode, all instances are attached to a single bridge on the compute + node. Additionally, a DHCP server configures instances depending on + single-/multi-host mode, alongside each ``nova-network``. In this mode, + Compute does a bit more configuration. It attempts to bridge into an Ethernet + device (``flat_interface``, eth0 by default). For every instance, Compute + allocates a fixed IP address and configures dnsmasq with the MAC ID and IP + address for the VM. Dnsmasq does not take part in the IP address allocation + process, it only hands out IPs according to the mapping done by Compute. + Instances receive their fixed IPs with the :command:`dhcpdiscover` command. + These IPs are not assigned to any of the host's network interfaces, only to + the guest-side interface for the VM. + + In any setup with flat networking, the hosts providing the ``nova-network`` + service are responsible for forwarding traffic from the private network. They + also run and configure dnsmasq as a DHCP server listening on this bridge, + usually on IP address 10.0.0.1 (see :ref:`compute-dnsmasq`). Compute can + determine the NAT entries for each network, although sometimes NAT is not + used, such as when the network has been configured with all public IPs, or if + a hardware router is used (which is a high availability option). In this + case, hosts need to have ``br100`` configured and physically connected to any + other nodes that are hosting VMs. You must set the ``flat_network_bridge`` + option or create networks with the bridge parameter in order to avoid raising + an error. Compute nodes have iptables or ebtables entries created for each + project and instance to protect against MAC ID or IP address spoofing and ARP + poisoning. + +.. note:: + + In single-host Flat DHCP mode you will be able to ping VMs through their + fixed IP from the ``nova-network`` node, but you cannot ping them from the + compute nodes. This is expected behavior. + +VLAN Network Manager + This is the default mode for OpenStack Compute. In this mode, Compute creates + a VLAN and bridge for each project. For multiple-machine installations, the + VLAN Network Mode requires a switch that supports VLAN tagging (IEEE 802.1Q). + The project gets a range of private IPs that are only accessible from inside + the VLAN. In order for a user to access the instances in their project, a + special VPN instance (code named ``cloudpipe``) needs to be created. Compute + generates a certificate and key for the user to access the VPN and starts the + VPN automatically. It provides a private network segment for each project's + instances that can be accessed through a dedicated VPN connection from the + internet. In this mode, each project gets its own VLAN, Linux networking + bridge, and subnet. + + The subnets are specified by the network administrator, and are assigned + dynamically to a project when required. A DHCP server is started for each + VLAN to pass out IP addresses to VM instances from the subnet assigned to the + project. All instances belonging to one project are bridged into the same + VLAN for that project. OpenStack Compute creates the Linux networking bridges + and VLANs when required. + +These network managers can co-exist in a cloud system. However, because you +cannot select the type of network for a given project, you cannot configure +multiple network types in a single Compute installation. + +All network managers configure the network using network drivers. For example, +the Linux L3 driver (``l3.py`` and ``linux_net.py``), which makes use of +``iptables``, ``route`` and other network management facilities, and the +libvirt `network filtering facilities +`__. The driver is not tied to any +particular network manager; all network managers use the same driver. The +driver usually initializes only when the first VM lands on this host node. + +All network managers operate in either single-host or multi-host mode. This +choice greatly influences the network configuration. In single-host mode, a +single ``nova-network`` service provides a default gateway for VMs and hosts a +single DHCP server (dnsmasq). In multi-host mode, each compute node runs its +own ``nova-network`` service. In both cases, all traffic between VMs and the +internet flows through ``nova-network``. Each mode has benefits and drawbacks. +For more on this, see the Network Topology section in the `OpenStack Operations +Guide +`__. + +All networking options require network connectivity to be already set up +between OpenStack physical nodes. OpenStack does not configure any physical +network interfaces. All network managers automatically create VM virtual +interfaces. Some network managers can also create network bridges such as +``br100``. + +The internal network interface is used for communication with VMs. The +interface should not have an IP address attached to it before OpenStack +installation, it serves only as a fabric where the actual endpoints are VMs and +dnsmasq. Additionally, the internal network interface must be in +``promiscuous`` mode, so that it can receive packets whose target MAC address +is the guest VM, not the host. + +All machines must have a public and internal network interface (controlled by +these options: ``public_interface`` for the public interface, and +``flat_interface`` and ``vlan_interface`` for the internal interface with flat +or VLAN managers). This guide refers to the public network as the external +network and the private network as the internal or project network. + +For flat and flat DHCP modes, use the :command:`nova network-create` command to +create a network: + +.. code-block:: console + + $ nova network-create vmnet \ + --fixed-range-v4 10.0.0.0/16 --fixed-cidr 10.0.20.0/24 --bridge br100 + +This example uses the following parameters: + +``--fixed-range-v4`` + Specifies the network subnet. +``--fixed-cidr`` + Specifies a range of fixed IP addresses to allocate, and can be a subset of + the ``--fixed-range-v4`` argument. +``--bridge`` + Specifies the bridge device to which this network is connected on every + compute node. + +.. _compute-dnsmasq: + +DHCP server: dnsmasq +~~~~~~~~~~~~~~~~~~~~ + +The Compute service uses `dnsmasq +`__ as the DHCP server when +using either Flat DHCP Network Manager or VLAN Network Manager. For Compute to +operate in IPv4/IPv6 dual-stack mode, use at least dnsmasq v2.63. The +``nova-network`` service is responsible for starting dnsmasq processes. + +The behavior of dnsmasq can be customized by creating a dnsmasq configuration +file. Specify the configuration file using the ``dnsmasq_config_file`` +configuration option: + +.. code-block:: ini + + dnsmasq_config_file=/etc/dnsmasq-nova.conf + +For more information about creating a dnsmasq configuration file, see the +:doc:`/configuration/config`, and `the dnsmasq documentation +`__. + +Dnsmasq also acts as a caching DNS server for instances. You can specify the +DNS server that dnsmasq uses by setting the ``dns_server`` configuration option +in ``/etc/nova/nova.conf``. This example configures dnsmasq to use Google's +public DNS server: + +.. code-block:: ini + + dns_server=8.8.8.8 + +Dnsmasq logs to syslog (typically ``/var/log/syslog`` or ``/var/log/messages``, +depending on Linux distribution). Logs can be useful for troubleshooting, +especially in a situation where VM instances boot successfully but are not +reachable over the network. + +Administrators can specify the starting point IP address to reserve with the +DHCP server (in the format n.n.n.n) with this command: + +.. code-block:: console + + $ nova-manage fixed reserve --address IP_ADDRESS + +This reservation only affects which IP address the VMs start at, not the fixed +IP addresses that ``nova-network`` places on the bridges. + +Configure Compute to use IPv6 addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are using OpenStack Compute with ``nova-network``, you can put Compute +into dual-stack mode, so that it uses both IPv4 and IPv6 addresses for +communication. In dual-stack mode, instances can acquire their IPv6 global +unicast addresses by using a stateless address auto-configuration mechanism +[RFC 4862/2462]. IPv4/IPv6 dual-stack mode works with both ``VlanManager`` and +``FlatDHCPManager`` networking modes. + +In ``VlanManager`` networking mode, each project uses a different 64-bit global +routing prefix. In ``FlatDHCPManager`` mode, all instances use one 64-bit +global routing prefix. + +This configuration was tested with virtual machine images that have an IPv6 +stateless address auto-configuration capability. This capability is required +for any VM to run with an IPv6 address. You must use an EUI-64 address for +stateless address auto-configuration. Each node that executes a ``nova-*`` +service must have ``python-netaddr`` and ``radvd`` installed. + +.. rubric:: Switch into IPv4/IPv6 dual-stack mode + +#. For every node running a ``nova-*`` service, install ``python-netaddr``: + + .. code-block:: console + + # apt-get install python-netaddr + +#. For every node running ``nova-network``, install ``radvd`` and configure + IPv6 networking: + + .. code-block:: console + + # apt-get install radvd + # echo 1 > /proc/sys/net/ipv6/conf/all/forwarding + # echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra + +#. On all nodes, edit the ``nova.conf`` file and specify ``use_ipv6 = True``. + +#. Restart all ``nova-*`` services. + +.. rubric:: IPv6 configuration options + +You can use the following options with the :command:`nova network-create` +command: + +- Add a fixed range for IPv6 addresses to the :command:`nova network-create` + command. Specify ``public`` or ``private`` after the ``network-create`` + parameter. + + .. code-block:: console + + $ nova network-create public --fixed-range-v4 FIXED_RANGE_V4 \ + --vlan VLAN_ID --vpn VPN_START --fixed-range-v6 FIXED_RANGE_V6 + +- Set the IPv6 global routing prefix by using the ``--fixed_range_v6`` + parameter. The default value for the parameter is ``fd00::/48``. + + When you use ``FlatDHCPManager``, the command uses the original + ``--fixed_range_v6`` value. For example: + + .. code-block:: console + + $ nova network-create public --fixed-range-v4 10.0.2.0/24 \ + --fixed-range-v6 fd00:1::/48 + +- When you use ``VlanManager``, the command increments the subnet ID to create + subnet prefixes. Guest VMs use this prefix to generate their IPv6 global + unicast addresses. For example: + + .. code-block:: console + + $ nova network-create public --fixed-range-v4 10.0.1.0/24 --vlan 100 \ + --vpn 1000 --fixed-range-v6 fd00:1::/48 + +.. list-table:: Description of IPv6 configuration options + :header-rows: 2 + + * - Configuration option = Default value + - Description + * - [DEFAULT] + - + * - fixed_range_v6 = fd00::/48 + - (StrOpt) Fixed IPv6 address block + * - gateway_v6 = None + - (StrOpt) Default IPv6 gateway + * - ipv6_backend = rfc2462 + - (StrOpt) Backend to use for IPv6 generation + * - use_ipv6 = False + - (BoolOpt) Use IPv6 + +.. _metadata-service: + +Metadata service +~~~~~~~~~~~~~~~~ + +Compute uses a metadata service for virtual machine instances to retrieve +instance-specific data. Instances access the metadata service at +``http://169.254.169.254``. The metadata service supports two sets of APIs: an +OpenStack metadata API and an EC2-compatible API. Both APIs are versioned by +date. + +To retrieve a list of supported versions for the OpenStack metadata API, make a +GET request to ``http://169.254.169.254/openstack``: + +.. code-block:: console + + $ curl http://169.254.169.254/openstack + 2012-08-10 + 2013-04-04 + 2013-10-17 + latest + +To list supported versions for the EC2-compatible metadata API, make a GET +request to ``http://169.254.169.254``: + +.. code-block:: console + + $ curl http://169.254.169.254 + 1.0 + 2007-01-19 + 2007-03-01 + 2007-08-29 + 2007-10-10 + 2007-12-15 + 2008-02-01 + 2008-09-01 + 2009-04-04 + latest + +If you write a consumer for one of these APIs, always attempt to access the +most recent API version supported by your consumer first, then fall back to an +earlier version if the most recent one is not available. + +Metadata from the OpenStack API is distributed in JSON format. To retrieve the +metadata, make a GET request to +``http://169.254.169.254/openstack/2012-08-10/meta_data.json``: + +.. code-block:: console + + $ curl http://169.254.169.254/openstack/2012-08-10/meta_data.json + +.. code-block:: json + + { + "uuid": "d8e02d56-2648-49a3-bf97-6be8f1204f38", + "availability_zone": "nova", + "hostname": "test.novalocal", + "launch_index": 0, + "meta": { + "priority": "low", + "role": "webserver" + }, + "project_id": "f7ac731cc11f40efbc03a9f9e1d1d21f", + "public_keys": { + "mykey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDYVEprvtYJXVOBN0XNKV\ + VRNCRX6BlnNbI+USLGais1sUWPwtSg7z9K9vhbYAPUZcq8c/s5S9dg5vTH\ + bsiyPCIDOKyeHba4MUJq8Oh5b2i71/3BISpyxTBH/uZDHdslW2a+SrPDCe\ + uMMoss9NFhBdKtDkdG9zyi0ibmCP6yMdEX8Q== Generated by Nova\n" + }, + "name": "test" + } + +Instances also retrieve user data (passed as the ``user_data`` parameter in the +API call or by the ``--user_data`` flag in the :command:`openstack server +create` command) through the metadata service, by making a GET request to +``http://169.254.169.254/openstack/2012-08-10/user_data``: + +.. code-block:: console + + $ curl http://169.254.169.254/openstack/2012-08-10/user_data + #!/bin/bash + echo 'Extra user data here' + +The metadata service has an API that is compatible with version 2009-04-04 of +the `Amazon EC2 metadata service +`__. +This means that virtual machine images designed for EC2 will work properly with +OpenStack. + +The EC2 API exposes a separate URL for each metadata element. Retrieve a +listing of these elements by making a GET query to +``http://169.254.169.254/2009-04-04/meta-data/``: + +.. code-block:: console + + $ curl http://169.254.169.254/2009-04-04/meta-data/ + ami-id + ami-launch-index + ami-manifest-path + block-device-mapping/ + hostname + instance-action + instance-id + instance-type + kernel-id + local-hostname + local-ipv4 + placement/ + public-hostname + public-ipv4 + public-keys/ + ramdisk-id + reservation-id + security-groups + +.. code-block:: console + + $ curl http://169.254.169.254/2009-04-04/meta-data/block-device-mapping/ + ami + +.. code-block:: console + + $ curl http://169.254.169.254/2009-04-04/meta-data/placement/ + availability-zone + +.. code-block:: console + + $ curl http://169.254.169.254/2009-04-04/meta-data/public-keys/ + 0=mykey + +Instances can retrieve the public SSH key (identified by keypair name when a +user requests a new instance) by making a GET request to +``http://169.254.169.254/2009-04-04/meta-data/public-keys/0/openssh-key``: + +.. code-block:: console + + $ curl http://169.254.169.254/2009-04-04/meta-data/public-keys/0/openssh-key + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDYVEprvtYJXVOBN0XNKVVRNCRX6BlnNbI+US\ + LGais1sUWPwtSg7z9K9vhbYAPUZcq8c/s5S9dg5vTHbsiyPCIDOKyeHba4MUJq8Oh5b2i71/3B\ + ISpyxTBH/uZDHdslW2a+SrPDCeuMMoss9NFhBdKtDkdG9zyi0ibmCP6yMdEX8Q== Generated\ + by Nova + +Instances can retrieve user data by making a GET request to +``http://169.254.169.254/2009-04-04/user-data``: + +.. code-block:: console + + $ curl http://169.254.169.254/2009-04-04/user-data + #!/bin/bash + echo 'Extra user data here' + +The metadata service is implemented by either the ``nova-api`` service or the +``nova-api-metadata`` service. Note that the ``nova-api-metadata`` service is +generally only used when running in multi-host mode, as it retrieves +instance-specific metadata. If you are running the ``nova-api`` service, you +must have ``metadata`` as one of the elements listed in the ``enabled_apis`` +configuration option in ``/etc/nova/nova.conf``. The default ``enabled_apis`` +configuration setting includes the metadata service, so you do not need to +modify it. + +Hosts access the service at ``169.254.169.254:80``, and this is translated to +``metadata_host:metadata_port`` by an iptables rule established by the +``nova-network`` service. In multi-host mode, you can set ``metadata_host`` to +``127.0.0.1``. + +For instances to reach the metadata service, the ``nova-network`` service must +configure iptables to NAT port ``80`` of the ``169.254.169.254`` address to the +IP address specified in ``metadata_host`` (this defaults to ``$my_ip``, which +is the IP address of the ``nova-network`` service) and port specified in +``metadata_port`` (which defaults to ``8775``) in ``/etc/nova/nova.conf``. + +.. note:: + + The ``metadata_host`` configuration option must be an IP address, not a host + name. + +The default Compute service settings assume that ``nova-network`` and +``nova-api`` are running on the same host. If this is not the case, in the +``/etc/nova/nova.conf`` file on the host running ``nova-network``, set the +``metadata_host`` configuration option to the IP address of the host where +``nova-api`` is running. + +.. list-table:: Description of metadata configuration options + :header-rows: 2 + + * - Configuration option = Default value + - Description + * - [DEFAULT] + - + * - metadata_cache_expiration = 15 + - (IntOpt) Time in seconds to cache metadata; 0 to disable metadata + caching entirely (not recommended). Increasing this should improve + response times of the metadata API when under heavy load. Higher values + may increase memory usage and result in longer times for host metadata + changes to take effect. + * - metadata_host = $my_ip + - (StrOpt) The IP address for the metadata API server + * - metadata_listen = 0.0.0.0 + - (StrOpt) The IP address on which the metadata API will listen. + * - metadata_listen_port = 8775 + - (IntOpt) The port on which the metadata API will listen. + * - metadata_manager = nova.api.manager.MetadataManager + - (StrOpt) OpenStack metadata service manager + * - metadata_port = 8775 + - (IntOpt) The port for the metadata API port + * - metadata_workers = None + - (IntOpt) Number of workers for metadata service. The default will be the number of CPUs available. + * - vendordata_driver = nova.api.metadata.vendordata_json.JsonFileVendorData + - (StrOpt) Driver to use for vendor data + * - vendordata_jsonfile_path = None + - (StrOpt) File to load JSON formatted vendor data from + +Enable ping and SSH on VMs +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You need to enable ``ping`` and ``ssh`` on your VMs for network access. This +can be done with either the :command:`nova` or :command:`euca2ools` commands. + +.. note:: + + Run these commands as root only if the credentials used to interact with + ``nova-api`` are in ``/root/.bashrc``. If the EC2 credentials in the + ``.bashrc`` file are for an unprivileged user, you must run these commands + as that user instead. + +Enable ping and SSH with :command:`openstack security group rule create` +commands: + +.. code-block:: console + + $ openstack security group rule create --protocol icmp default + $ openstack security group rule create --protocol tcp --dst-port 22:22 default + +Enable ping and SSH with ``euca2ools``: + +.. code-block:: console + + $ euca-authorize -P icmp -t -1:-1 -s 0.0.0.0/0 default + $ euca-authorize -P tcp -p 22 -s 0.0.0.0/0 default + +If you have run these commands and still cannot ping or SSH your instances, +check the number of running ``dnsmasq`` processes, there should be two. If not, +kill the processes and restart the service with these commands: + +.. code-block:: console + + # killall dnsmasq + # service nova-network restart + +Configure public (floating) IP addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to configure floating IP addresses with +``nova-network``. For information about doing this with OpenStack Networking, +see `L3-routing-and-NAT +`_. + +Private and public IP addresses +------------------------------- + +In this section, the term floating IP address is used to refer to an IP +address, usually public, that you can dynamically add to a running virtual +instance. + +Every virtual instance is automatically assigned a private IP address. You can +choose to assign a public (or floating) IP address instead. OpenStack Compute +uses network address translation (NAT) to assign floating IPs to virtual +instances. + +To be able to assign a floating IP address, edit the ``/etc/nova/nova.conf`` +file to specify which interface the ``nova-network`` service should bind public +IP addresses to: + +.. code-block:: ini + + public_interface=VLAN100 + +If you make changes to the ``/etc/nova/nova.conf`` file while the +``nova-network`` service is running, you will need to restart the service to +pick up the changes. + +.. note:: + + Floating IPs are implemented by using a source NAT (SNAT rule in iptables), + so security groups can sometimes display inconsistent behavior if VMs use + their floating IP to communicate with other VMs, particularly on the same + physical host. Traffic from VM to VM across the fixed network does not have + this issue, and so this is the recommended setup. To ensure that traffic + does not get SNATed to the floating range, explicitly set: + + .. code-block:: ini + + dmz_cidr=x.x.x.x/y + + The ``x.x.x.x/y`` value specifies the range of floating IPs for each pool of + floating IPs that you define. This configuration is also required if the VMs + in the source group have floating IPs. + +Enable IP forwarding +-------------------- + +IP forwarding is disabled by default on most Linux distributions. You will need +to enable it in order to use floating IPs. + +.. note:: + + IP forwarding only needs to be enabled on the nodes that run + ``nova-network``. However, you will need to enable it on all compute nodes + if you use ``multi_host`` mode. + +To check if IP forwarding is enabled, run: + +.. code-block:: console + + $ cat /proc/sys/net/ipv4/ip_forward + 0 + +Alternatively, run: + +.. code-block:: console + + $ sysctl net.ipv4.ip_forward + net.ipv4.ip_forward = 0 + +In these examples, IP forwarding is disabled. + +To enable IP forwarding dynamically, run: + +.. code-block:: console + + # sysctl -w net.ipv4.ip_forward=1 + +Alternatively, run: + +.. code-block:: console + + # echo 1 > /proc/sys/net/ipv4/ip_forward + +To make the changes permanent, edit the ``/etc/sysctl.conf`` file and update +the IP forwarding setting: + +.. code-block:: ini + + net.ipv4.ip_forward = 1 + +Save the file and run this command to apply the changes: + +.. code-block:: console + + # sysctl -p + +You can also apply the changes by restarting the network service: + +- on Ubuntu, Debian: + + .. code-block:: console + + # /etc/init.d/networking restart + +- on RHEL, Fedora, CentOS, openSUSE and SLES: + + .. code-block:: console + + # service network restart + +Create a list of available floating IP addresses +------------------------------------------------ + +Compute maintains a list of floating IP addresses that are available for +assigning to instances. Use the :command:`nova-manage floating` commands to +perform floating IP operations: + +- Add entries to the list: + + .. code-block:: console + + # nova-manage floating create --pool nova --ip_range 68.99.26.170/31 + +- List the floating IP addresses in the pool: + + .. code-block:: console + + # openstack floating ip list + +- Create specific floating IPs for either a single address or a subnet: + + .. code-block:: console + + # nova-manage floating create --pool POOL_NAME --ip_range CIDR + +- Remove floating IP addresses using the same parameters as the create command: + + .. code-block:: console + + # openstack floating ip delete CIDR + +For more information about how administrators can associate floating IPs with +instances, see `ip floating +`__ in the python-openstackclient User Documentation. + +Automatically add floating IPs +------------------------------ + +You can configure ``nova-network`` to automatically allocate and assign a +floating IP address to virtual instances when they are launched. Add this line +to the ``/etc/nova/nova.conf`` file: + +.. code-block:: ini + + auto_assign_floating_ip=True + +Save the file, and restart ``nova-network`` + +.. note:: + + If this option is enabled, but all floating IP addresses have already been + allocated, the :command:`openstack server create` command will fail. + +Remove a network from a project +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You cannot delete a network that has been associated to a project. This section +describes the procedure for dissociating it so that it can be deleted. + +In order to disassociate the network, you will need the ID of the project it +has been associated to. To get the project ID, you will need to be an +administrator. + +Disassociate the network from the project using the :command:`nova-manage +project scrub` command, with the project ID as the final parameter: + +.. code-block:: console + + # nova-manage project scrub --project ID + +Multiple interfaces for instances (multinic) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The multinic feature allows you to use more than one interface with your +instances. This is useful in several scenarios: + +- SSL Configurations (VIPs) + +- Services failover/HA + +- Bandwidth Allocation + +- Administrative/Public access to your instances + +Each VIP represents a separate network with its own IP block. Every network +mode has its own set of changes regarding multinic usage: + +.. figure:: figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-Flat-manager.jpg + :width: 600 + +.. figure:: figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-Flat-DHCP-manager.jpg + :width: 600 + +.. figure:: figures/SCH_5007_V00_NUAC-multi_nic_OpenStack-VLAN-manager.jpg + :width: 600 + +Using multinic +-------------- + +In order to use multinic, create two networks, and attach them to the project +(named ``project`` on the command line): + +.. code-block:: console + + $ nova network-create first-net --fixed-range-v4 20.20.0.0/24 --project-id $your-project + $ nova network-create second-net --fixed-range-v4 20.20.10.0/24 --project-id $your-project + +Each new instance will now receive two IP addresses from their respective DHCP +servers: + +.. code-block:: console + + $ openstack server list + +---------+----------+--------+-----------------------------------------+------------+ + |ID | Name | Status | Networks | Image Name | + +---------+----------+--------+-----------------------------------------+------------+ + | 1234... | MyServer | ACTIVE | network2=20.20.0.3; private=20.20.10.14 | cirros | + +---------+----------+--------+-----------------------------------------+------------+ + +.. note:: + + Make sure you start the second interface on the instance, or it won't be + reachable through the second IP. + +This example demonstrates how to set up the interfaces within the instance. +This is the configuration that needs to be applied inside the image. + +Edit the ``/etc/network/interfaces`` file: + +.. code-block:: bash + + # The loopback network interface + auto lo + iface lo inet loopback + + auto eth0 + iface eth0 inet dhcp + + auto eth1 + iface eth1 inet dhcp + +If the Virtual Network Service Neutron is installed, you can specify the +networks to attach to the interfaces by using the ``--nic`` flag with the +:command:`openstack server create` command: + +.. code-block:: console + + $ openstack server create --image ed8b2a37-5535-4a5f-a615-443513036d71 \ + --flavor 1 --nic net-id=NETWORK1_ID --nic net-id=NETWORK2_ID test-vm1 + +Troubleshooting Networking +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Cannot reach floating IPs +------------------------- + +Problem +------- + +You cannot reach your instances through the floating IP address. + +Solution +-------- + +- Check that the default security group allows ICMP (ping) and SSH (port 22), + so that you can reach the instances: + + .. code-block:: console + + $ openstack security group rule list default + +--------------------------------------+-------------+-----------+-----------------+-----------------------+ + | ID | IP Protocol | IP Range | Port Range | Remote Security Group | + +--------------------------------------+-------------+-----------+-----------------+-----------------------+ + | 63536865-e5b6-4df1-bac5-ca6d97d8f54d | tcp | 0.0.0.0/0 | 22:22 | None | + | e9d3200f-647a-4293-a9fc-e65ceee189ae | icmp | 0.0.0.0/0 | type=1:code=-1 | None | + +--------------------------------------+-------------+-----------+-----------------+-----------------------+ + +- Check the NAT rules have been added to iptables on the node that is running + ``nova-network``: + + .. code-block:: console + + # iptables -L -nv -t nat \ + -A nova-network-PREROUTING -d 68.99.26.170/32 -j DNAT --to-destination 10.0.0.3 \ + -A nova-network-floating-snat -s 10.0.0.3/32 -j SNAT --to-source 68.99.26.170 + +- Check that the public address (``68.99.26.170`` in this example), has been + added to your public interface. You should see the address in the listing + when you use the :command:`ip addr` command: + + .. code-block:: console + + $ ip addr + 2: eth0: mtu 1500 qdisc mq state UP qlen 1000 + link/ether xx:xx:xx:17:4b:c2 brd ff:ff:ff:ff:ff:ff + inet 13.22.194.80/24 brd 13.22.194.255 scope global eth0 + inet 68.99.26.170/32 scope global eth0 + inet6 fe80::82b:2bf:fe1:4b2/64 scope link + valid_lft forever preferred_lft forever + + .. note:: + + You cannot use ``SSH`` to access an instance with a public IP from within + the same server because the routing configuration does not allow it. + +- Use ``tcpdump`` to identify if packets are being routed to the inbound + interface on the compute host. If the packets are reaching the compute hosts + but the connection is failing, the issue may be that the packet is being + dropped by reverse path filtering. Try disabling reverse-path filtering on + the inbound interface. For example, if the inbound interface is ``eth2``, + run: + + .. code-block:: console + + # sysctl -w net.ipv4.conf.ETH2.rp_filter=0 + + If this solves the problem, add the following line to ``/etc/sysctl.conf`` so + that the reverse-path filter is persistent: + + .. code-block:: ini + + net.ipv4.conf.rp_filter=0 + +Temporarily disable firewall +---------------------------- + +Problem +------- + +Networking issues prevent administrators accessing or reaching VM's through +various pathways. + +Solution +-------- + +You can disable the firewall by setting this option in ``/etc/nova/nova.conf``: + +.. code-block:: ini + + firewall_driver=nova.virt.firewall.NoopFirewallDriver + +.. warning:: + + We strongly recommend you remove this line to re-enable the firewall once + your networking issues have been resolved. + +Packet loss from instances to nova-network server (VLANManager mode) +-------------------------------------------------------------------- + +Problem +------- + +If you can access your instances with ``SSH`` but the network to your instance +is slow, or if you find that running certain operations are slower than they +should be (for example, ``sudo``), packet loss could be occurring on the +connection to the instance. + +Packet loss can be caused by Linux networking configuration settings related to +bridges. Certain settings can cause packets to be dropped between the VLAN +interface (for example, ``vlan100``) and the associated bridge interface (for +example, ``br100``) on the host running ``nova-network``. + +Solution +-------- + +One way to check whether this is the problem is to open three terminals and run +the following commands: + +#. In the first terminal, on the host running ``nova-network``, use ``tcpdump`` + on the VLAN interface to monitor DNS-related traffic (UDP, port 53). As + root, run: + + .. code-block:: console + + # tcpdump -K -p -i vlan100 -v -vv udp port 53 + +#. In the second terminal, also on the host running ``nova-network``, use + ``tcpdump`` to monitor DNS-related traffic on the bridge interface. As + root, run: + + .. code-block:: console + + # tcpdump -K -p -i br100 -v -vv udp port 53 + +#. In the third terminal, use ``SSH`` to access the instance and generate DNS + requests by using the :command:`nslookup` command: + + .. code-block:: console + + $ nslookup www.google.com + + The symptoms may be intermittent, so try running :command:`nslookup` + multiple times. If the network configuration is correct, the command should + return immediately each time. If it is not correct, the command hangs for + several seconds before returning. + +#. If the :command:`nslookup` command sometimes hangs, and there are packets + that appear in the first terminal but not the second, then the problem may + be due to filtering done on the bridges. Try disabling filtering, and + running these commands as root: + + .. code-block:: console + + # sysctl -w net.bridge.bridge-nf-call-arptables=0 + # sysctl -w net.bridge.bridge-nf-call-iptables=0 + # sysctl -w net.bridge.bridge-nf-call-ip6tables=0 + + If this solves your issue, add the following line to ``/etc/sysctl.conf`` so + that the changes are persistent: + + .. code-block:: ini + + net.bridge.bridge-nf-call-arptables=0 + net.bridge.bridge-nf-call-iptables=0 + net.bridge.bridge-nf-call-ip6tables=0 + +KVM: Network connectivity works initially, then fails +----------------------------------------------------- + +Problem +------- + +With KVM hypervisors, instances running Ubuntu 12.04 sometimes lose network +connectivity after functioning properly for a period of time. + +Solution +-------- + +Try loading the ``vhost_net`` kernel module as a workaround for this issue (see +`bug #997978 +`__) . This +kernel module may also `improve network performance +`__ on KVM. To load the kernel module: + +.. code-block:: console + + # modprobe vhost_net + +.. note:: + + Loading the module has no effect on running instances. diff --git a/doc/source/admin/node-down.rst b/doc/source/admin/node-down.rst new file mode 100644 index 00000000000..f1c0cf8484a --- /dev/null +++ b/doc/source/admin/node-down.rst @@ -0,0 +1,334 @@ +================================== +Recover from a failed compute node +================================== + +If you deploy Compute with a shared file system, you can use several methods to +quickly recover from a node failure. This section discusses manual recovery. + +Evacuate instances +~~~~~~~~~~~~~~~~~~ + +If a hardware malfunction or other error causes the cloud compute node to fail, +you can use the :command:`nova evacuate` command to evacuate instances. See +:doc:`evacuate instances ` for more information on using the command. + +.. _nova-compute-node-down-manual-recovery: + +Manual recovery +~~~~~~~~~~~~~~~ + +To manually recover a failed compute node: + +#. Identify the VMs on the affected hosts by using a combination of the + :command:`openstack server list` and :command:`openstack server show` + commands or the :command:`euca-describe-instances` command. + + For example, this command displays information about the i-000015b9 instance + that runs on the np-rcc54 node: + + .. code-block:: console + + $ euca-describe-instances + i-000015b9 at3-ui02 running nectarkey (376, np-rcc54) 0 m1.xxlarge 2012-06-19T00:48:11.000Z 115.146.93.60 + +#. Query the Compute database for the status of the host. This example converts + an EC2 API instance ID to an OpenStack ID. If you use the :command:`nova` + commands, you can substitute the ID directly. This example output is + truncated: + + .. code-block:: none + + mysql> SELECT * FROM instances WHERE id = CONV('15b9', 16, 10) \G; + *************************** 1. row *************************** + created_at: 2012-06-19 00:48:11 + updated_at: 2012-07-03 00:35:11 + deleted_at: NULL + ... + id: 5561 + ... + power_state: 5 + vm_state: shutoff + ... + hostname: at3-ui02 + host: np-rcc54 + ... + uuid: 3f57699a-e773-4650-a443-b4b37eed5a06 + ... + task_state: NULL + ... + + .. note:: + + Find the credentials for your database in ``/etc/nova.conf`` file. + +#. Decide to which compute host to move the affected VM. Run this database + command to move the VM to that host: + + .. code-block:: mysql + + mysql> UPDATE instances SET host = 'np-rcc46' WHERE uuid = '3f57699a-e773-4650-a443-b4b37eed5a06'; + +#. If you use a hypervisor that relies on libvirt, such as KVM, update the + ``libvirt.xml`` file in ``/var/lib/nova/instances/[instance ID]`` with these + changes: + + - Change the ``DHCPSERVER`` value to the host IP address of the new compute + host. + + - Update the VNC IP to ``0.0.0.0``. + +#. Reboot the VM: + + .. code-block:: console + + $ openstack server reboot 3f57699a-e773-4650-a443-b4b37eed5a06 + +Typically, the database update and :command:`openstack server reboot` command +recover a VM from a failed host. However, if problems persist, try one of these +actions: + +- Use :command:`virsh` to recreate the network filter configuration. + +- Restart Compute services. + +- Update the ``vm_state`` and ``power_state`` fields in the Compute database. + +Recover from a UID/GID mismatch +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes when you run Compute with a shared file system or an automated +configuration tool, files on your compute node might use the wrong UID or GID. +This UID or GID mismatch can prevent you from running live migrations or +starting virtual machines. + +This procedure runs on ``nova-compute`` hosts, based on the KVM hypervisor: + +#. Set the nova UID to the same number in ``/etc/passwd`` on all hosts. For + example, set the UID to ``112``. + + .. note:: + + Choose UIDs or GIDs that are not in use for other users or groups. + +#. Set the ``libvirt-qemu`` UID to the same number in the ``/etc/passwd`` file + on all hosts. For example, set the UID to ``119``. + +#. Set the ``nova`` group to the same number in the ``/etc/group`` file on all + hosts. For example, set the group to ``120``. + +#. Set the ``libvirtd`` group to the same number in the ``/etc/group`` file on + all hosts. For example, set the group to ``119``. + +#. Stop the services on the compute node. + +#. Change all files that the nova user or group owns. For example: + + .. code-block:: console + + # find / -uid 108 -exec chown nova {} \; + # note the 108 here is the old nova UID before the change + # find / -gid 120 -exec chgrp nova {} \; + +#. Repeat all steps for the ``libvirt-qemu`` files, if required. + +#. Restart the services. + +#. To verify that all files use the correct IDs, run the :command:`find` + command. + +Recover cloud after disaster +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to manage your cloud after a disaster and back up +persistent storage volumes. Backups are mandatory, even outside of disaster +scenarios. + +For a definition of a disaster recovery plan (DRP), see +`https://en.wikipedia.org/wiki/Disaster\_Recovery\_Plan +`_. + +A disk crash, network loss, or power failure can affect several components in +your cloud architecture. The worst disaster for a cloud is a power loss. A +power loss affects these components: + +- A cloud controller (``nova-api``, ``nova-objectstore``, ``nova-network``) + +- A compute node (``nova-compute``) + +- A storage area network (SAN) used by OpenStack Block Storage + (``cinder-volumes``) + +Before a power loss: + +- Create an active iSCSI session from the SAN to the cloud controller (used + for the ``cinder-volumes`` LVM's VG). + +- Create an active iSCSI session from the cloud controller to the compute node + (managed by ``cinder-volume``). + +- Create an iSCSI session for every volume (so 14 EBS volumes requires 14 + iSCSI sessions). + +- Create ``iptables`` or ``ebtables`` rules from the cloud controller to the + compute node. This allows access from the cloud controller to the running + instance. + +- Save the current state of the database, the current state of the running + instances, and the attached volumes (mount point, volume ID, volume status, + etc), at least from the cloud controller to the compute node. + +After power resumes and all hardware components restart: + +- The iSCSI session from the SAN to the cloud no longer exists. + +- The iSCSI session from the cloud controller to the compute node no longer + exists. + +- nova-network reapplies configurations on boot and, as a result, recreates + the iptables and ebtables from the cloud controller to the compute node. + +- Instances stop running. + + Instances are not lost because neither ``destroy`` nor ``terminate`` ran. + The files for the instances remain on the compute node. + +- The database does not update. + +.. rubric:: Begin recovery + +.. warning:: + + Do not add any steps or change the order of steps in this procedure. + +#. Check the current relationship between the volume and its instance, so that + you can recreate the attachment. + + Use the :command:`openstack volume list` command to get this information. + Note that the :command:`openstack` client can get volume information from + OpenStack Block Storage. + +#. Update the database to clean the stalled state. Do this for every volume by + using these queries: + + .. code-block:: mysql + + mysql> use cinder; + mysql> update volumes set mountpoint=NULL; + mysql> update volumes set status="available" where status <>"error_deleting"; + mysql> update volumes set attach_status="detached"; + mysql> update volumes set instance_id=0; + + Use :command:`openstack volume list` command to list all volumes. + +#. Restart the instances by using the :command:`openstack server reboot + INSTANCE` command. + + .. important:: + + Some instances completely reboot and become reachable, while some might + stop at the plymouth stage. This is expected behavior. DO NOT reboot a + second time. + + Instance state at this stage depends on whether you added an `/etc/fstab` + entry for that volume. Images built with the cloud-init package remain in + a ``pending`` state, while others skip the missing volume and start. You + perform this step to ask Compute to reboot every instance so that the + stored state is preserved. It does not matter if not all instances come + up successfully. For more information about cloud-init, see + `help.ubuntu.com/community/CloudInit/ + `__. + +#. If required, run the :command:`openstack server add volume` command to + reattach the volumes to their respective instances. This example uses a file + of listed volumes to reattach them: + + .. code-block:: bash + + #!/bin/bash + + while read line; do + volume=`echo $line | $CUT -f 1 -d " "` + instance=`echo $line | $CUT -f 2 -d " "` + mount_point=`echo $line | $CUT -f 3 -d " "` + echo "ATTACHING VOLUME FOR INSTANCE - $instance" + openstack server add volume $instance $volume $mount_point + sleep 2 + done < $volumes_tmp_file + + Instances that were stopped at the plymouth stage now automatically continue + booting and start normally. Instances that previously started successfully + can now see the volume. + +#. Log in to the instances with SSH and reboot them. + + If some services depend on the volume or if a volume has an entry in fstab, + you can now restart the instance. Restart directly from the instance itself + and not through :command:`nova`: + + .. code-block:: console + + # shutdown -r now + + When you plan for and complete a disaster recovery, follow these tips: + +- Use the ``errors=remount`` option in the ``fstab`` file to prevent data + corruption. + + In the event of an I/O error, this option prevents writes to the disk. Add + this configuration option into the cinder-volume server that performs the + iSCSI connection to the SAN and into the instances' ``fstab`` files. + +- Do not add the entry for the SAN's disks to the cinder-volume's ``fstab`` + file. + + Some systems hang on that step, which means you could lose access to your + cloud-controller. To re-run the session manually, run this command before + performing the mount: + + .. code-block:: console + + # iscsiadm -m discovery -t st -p $SAN_IP $ iscsiadm -m node --target-name $IQN -p $SAN_IP -l + +- On your instances, if you have the whole ``/home/`` directory on the disk, + leave a user's directory with the user's bash files and the + ``authorized_keys`` file instead of emptying the ``/home/`` directory and + mapping the disk on it. + + This action enables you to connect to the instance without the volume + attached, if you allow only connections through public keys. + +To script the disaster recovery plan (DRP), use the `https://github.com/Razique +`_ +bash script. + +This script completes these steps: + +#. Creates an array for instances and their attached volumes. + +#. Updates the MySQL database. + +#. Restarts all instances with euca2ools. + +#. Reattaches the volumes. + +#. Uses Compute credentials to make an SSH connection into every instance. + +The script includes a ``test mode``, which enables you to perform the sequence +for only one instance. + +To reproduce the power loss, connect to the compute node that runs that +instance and close the iSCSI session. Do not detach the volume by using the +:command:`openstack server remove volume` command. You must manually close the +iSCSI session. This example closes an iSCSI session with the number ``15``: + +.. code-block:: console + + # iscsiadm -m session -u -r 15 + +Do not forget the ``-r`` option. Otherwise, all sessions close. + +.. warning:: + + There is potential for data loss while running instances during this + procedure. If you are using Liberty or earlier, ensure you have the correct + patch and set the options appropriately. diff --git a/doc/source/admin/numa.rst b/doc/source/admin/numa.rst new file mode 100644 index 00000000000..21187387dc7 --- /dev/null +++ b/doc/source/admin/numa.rst @@ -0,0 +1,26 @@ +============================================= +Consider NUMA topology when booting instances +============================================= + +.. todo:: Merge this into 'cpu-topologies.rst' + +NUMA topology can exist on both the physical hardware of the host, and the +virtual hardware of the instance. OpenStack Compute uses libvirt to tune +instances to take advantage of NUMA topologies. The libvirt driver boot +process looks at the NUMA topology field of both the instance and the host it +is being booted on, and uses that information to generate an appropriate +configuration. + +If the host is NUMA capable, but the instance has not requested a NUMA +topology, Compute attempts to pack the instance into a single cell. +If this fails, though, Compute will not continue to try. + +If the host is NUMA capable, and the instance has requested a specific NUMA +topology, Compute will try to pin the vCPUs of different NUMA cells +on the instance to the corresponding NUMA cells on the host. It will also +expose the NUMA topology of the instance to the guest OS. + +If you want Compute to pin a particular vCPU as part of this process, +set the ``vcpu_pin_set`` parameter in the ``nova.conf`` configuration +file. For more information about the ``vcpu_pin_set`` parameter, see the +Configuration Reference Guide. diff --git a/doc/source/admin/pci-passthrough.rst b/doc/source/admin/pci-passthrough.rst new file mode 100644 index 00000000000..a130b4ff15b --- /dev/null +++ b/doc/source/admin/pci-passthrough.rst @@ -0,0 +1,145 @@ +======================================== +Attaching physical PCI devices to guests +======================================== + +The PCI passthrough feature in OpenStack allows full access and direct control +of a physical PCI device in guests. This mechanism is generic for any kind of +PCI device, and runs with a Network Interface Card (NIC), Graphics Processing +Unit (GPU), or any other devices that can be attached to a PCI bus. Correct +driver installation is the only requirement for the guest to properly use the +devices. + +Some PCI devices provide Single Root I/O Virtualization and Sharing (SR-IOV) +capabilities. When SR-IOV is used, a physical device is virtualized and appears +as multiple PCI devices. Virtual PCI devices are assigned to the same or +different guests. In the case of PCI passthrough, the full physical device is +assigned to only one guest and cannot be shared. + +.. note:: + + For information on attaching virtual SR-IOV devices to guests, refer to the + `Networking Guide`_. + +To enable PCI passthrough, follow the steps below: + +#. Configure nova-scheduler (Controller) + +#. Configure nova-api (Controller)** + +#. Configure a flavor (Controller) + +#. Enable PCI passthrough (Compute) + +#. Configure PCI devices in nova-compute (Compute) + +.. note:: + + The PCI device with address ``0000:41:00.0`` is used as an example. This + will differ between environments. + +Configure nova-scheduler (Controller) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Configure ``nova-scheduler`` as specified in `Configure nova-scheduler`_. + +#. Restart the ``nova-scheduler`` service. + +Configure nova-api (Controller) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Specify the PCI alias for the device. + + Configure a PCI alias ``a1`` to request a PCI device with a ``vendor_id`` of + ``0x8086`` and a ``product_id`` of ``0x154d``. The ``vendor_id`` and + ``product_id`` correspond the PCI device with address ``0000:41:00.0``. + + Edit ``/etc/nova/nova.conf``: + + .. code-block:: ini + + [pci] + alias = { "vendor_id":"8086", "product_id":"154d", "device_type":"type-PF", "name":"a1" } + + For more information about the syntax of ``alias``, refer to + :doc:`/configuration/config`. + +#. Restart the ``nova-api`` service. + +Configure a flavor (Controller) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Configure a flavor to request two PCI devices, each with ``vendor_id`` of +``0x8086`` and ``product_id`` of ``0x154d``: + +.. code-block:: console + + # openstack flavor set m1.large --property "pci_passthrough:alias"="a1:2" + +For more information about the syntax for ``pci_passthrough:alias``, refer to +:doc:`/admin/flavors`. + +Enable PCI passthrough (Compute) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Enable VT-d and IOMMU. For more information, refer to steps one and two in +`Create Virtual Functions`_. + +Configure PCI devices (Compute) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. Configure ``nova-compute`` to allow the PCI device to pass through to + VMs. Edit ``/etc/nova/nova.conf``: + + .. code-block:: ini + + [pci] + passthrough_whitelist = { "address": "0000:41:00.0" } + + Alternatively specify multiple PCI devices using whitelisting: + + .. code-block:: ini + + [pci] + passthrough_whitelist = { "vendor_id": "8086", "product_id": "10fb" } + + All PCI devices matching the ``vendor_id`` and ``product_id`` are added to + the pool of PCI devices available for passthrough to VMs. + + For more information about the syntax of ``passthrough_whitelist``, + refer to :doc:`/configuration/config`. + +#. Specify the PCI alias for the device. + + From the Newton release, to resize guest with PCI device, configure the PCI + alias on the compute node as well. + + Configure a PCI alias ``a1`` to request a PCI device with a ``vendor_id`` of + ``0x8086`` and a ``product_id`` of ``0x154d``. The ``vendor_id`` and + ``product_id`` correspond the PCI device with address ``0000:41:00.0``. + + Edit ``/etc/nova/nova.conf``: + + .. code-block:: ini + + [pci] + alias = { "vendor_id":"8086", "product_id":"154d", "device_type":"type-PF", "name":"a1" } + + For more information about the syntax of ``alias``, refer to :doc:`/configuration/config`. + +#. Restart the ``nova-compute`` service. + +Create instances with PCI passthrough devices +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``nova-scheduler`` selects a destination host that has PCI devices +available with the specified ``vendor_id`` and ``product_id`` that matches the +``alias`` from the flavor. + +.. code-block:: console + + # openstack server create --flavor m1.large --image cirros-0.3.5-x86_64-uec --wait test-pci + +.. Links +.. _`Create Virtual Functions`: https://docs.openstack.org/neutron/latest/admin/config-sriov.html#create-virtual-functions-compute +.. _`Configure nova-scheduler`: https://docs.openstack.org/neutron/latest/admin/config-sriov.html#configure-nova-scheduler-controller +.. _`Networking Guide`: https://docs.openstack.org/neutron/latest/admin/config-sriov.html diff --git a/doc/source/admin/quotas.rst b/doc/source/admin/quotas.rst new file mode 100644 index 00000000000..60d9a669116 --- /dev/null +++ b/doc/source/admin/quotas.rst @@ -0,0 +1,304 @@ +============================= +Manage Compute service quotas +============================= + +As an administrative user, you can use the :command:`nova quota-*` commands, +which are provided by the ``python-novaclient`` package, to update the Compute +service quotas for a specific project or project user, as well as update the +quota defaults for a new project. + +.. todo:: + + At some point, probably in Queens, we need to scrub this page and mention + the microversions that remove the proxy and network-related resource quotas. + +.. rubric:: Compute quota descriptions + +.. list-table:: + :header-rows: 1 + :widths: 10 40 + + * - Quota name + - Description + * - cores + - Number of instance cores (VCPUs) allowed per project. + * - fixed-ips + - Number of fixed IP addresses allowed per project. This number + must be equal to or greater than the number of allowed + instances. + * - floating-ips + - Number of floating IP addresses allowed per project. + * - injected-file-content-bytes + - Number of content bytes allowed per injected file. + * - injected-file-path-bytes + - Length of injected file path. + * - injected-files + - Number of injected files allowed per project. + * - instances + - Number of instances allowed per project. + * - key-pairs + - Number of key pairs allowed per user. + * - metadata-items + - Number of metadata items allowed per instance. + * - ram + - Megabytes of instance ram allowed per project. + * - security-groups + - Number of security groups per project. + * - security-group-rules + - Number of security group rules per project. + * - server-groups + - Number of server groups per project. + * - server-group-members + - Number of servers per server group. + +View and update Compute quotas for a project +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To view and update default quota values +--------------------------------------- + +#. List all default quotas for all projects: + + .. code-block:: console + + $ openstack quota show --default + + +-----------------------------+-------+ + | Quota | Limit | + +-----------------------------+-------+ + | instances | 10 | + | cores | 20 | + | ram | 51200 | + | floating_ips | 10 | + | fixed_ips | -1 | + | metadata_items | 128 | + | injected_files | 5 | + | injected_file_content_bytes | 10240 | + | injected_file_path_bytes | 255 | + | key_pairs | 100 | + | security_groups | 10 | + | security_group_rules | 20 | + | server_groups | 10 | + | server_group_members | 10 | + +-----------------------------+-------+ + +#. Update a default value for a new project, for example: + + .. code-block:: console + + $ openstack quota set --instances 15 default + +To view quota values for an existing project +-------------------------------------------- + +#. List the currently set quota values for a project: + + .. code-block:: console + + $ openstack quota show PROJECT_NAME + + +-----------------------------+-------+ + | Quota | Limit | + +-----------------------------+-------+ + | instances | 10 | + | cores | 20 | + | ram | 51200 | + | floating_ips | 10 | + | fixed_ips | -1 | + | metadata_items | 128 | + | injected_files | 5 | + | injected_file_content_bytes | 10240 | + | injected_file_path_bytes | 255 | + | key_pairs | 100 | + | security_groups | 10 | + | security_group_rules | 20 | + | server_groups | 10 | + | server_group_members | 10 | + +-----------------------------+-------+ + +To update quota values for an existing project +---------------------------------------------- + +#. Obtain the project ID. + + .. code-block:: console + + $ project=$(openstack project show -f value -c id PROJECT_NAME) + +#. Update a particular quota value. + + .. code-block:: console + + $ openstack quota set --QUOTA_NAME QUOTA_VALUE PROJECT_OR_CLASS + + For example: + + .. code-block:: console + + $ openstack quota set --floating-ips 20 PROJECT_OR_CLASS + $ openstack quota show PROJECT_NAME + +-----------------------------+-------+ + | Quota | Limit | + +-----------------------------+-------+ + | instances | 10 | + | cores | 20 | + | ram | 51200 | + | floating_ips | 20 | + | fixed_ips | -1 | + | metadata_items | 128 | + | injected_files | 5 | + | injected_file_content_bytes | 10240 | + | injected_file_path_bytes | 255 | + | key_pairs | 100 | + | security_groups | 10 | + | security_group_rules | 20 | + | server_groups | 10 | + | server_group_members | 10 | + +-----------------------------+-------+ + + .. note:: + + To view a list of options for the :command:`openstack quota set` command, + run: + + .. code-block:: console + + $ openstack help quota set + +View and update Compute quotas for a project user +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To view quota values for a project user +--------------------------------------- + +#. Place the user ID in a usable variable. + + .. code-block:: console + + $ projectUser=$(openstack user show -f value -c id USER_NAME) + +#. Place the user's project ID in a usable variable, as follows: + + .. code-block:: console + + $ project=$(openstack project show -f value -c id PROJECT_NAME) + +#. List the currently set quota values for a project user. + + .. code-block:: console + + $ nova quota-show --user $projectUser --tenant $project + + For example: + + .. code-block:: console + + $ nova quota-show --user $projecUser --tenant $project + +-----------------------------+-------+ + | Quota | Limit | + +-----------------------------+-------+ + | instances | 10 | + | cores | 20 | + | ram | 51200 | + | floating_ips | 20 | + | fixed_ips | -1 | + | metadata_items | 128 | + | injected_files | 5 | + | injected_file_content_bytes | 10240 | + | injected_file_path_bytes | 255 | + | key_pairs | 100 | + | security_groups | 10 | + | security_group_rules | 20 | + | server_groups | 10 | + | server_group_members | 10 | + +-----------------------------+-------+ + +To update quota values for a project user +----------------------------------------- + +#. Place the user ID in a usable variable. + + .. code-block:: console + + $ projectUser=$(openstack user show -f value -c id USER_NAME) + +#. Place the user's project ID in a usable variable, as follows: + + .. code-block:: console + + $ project=$(openstack project show -f value -c id PROJECT_NAME) + +#. Update a particular quota value, as follows: + + .. code-block:: console + + $ nova quota-update --user $projectUser --QUOTA_NAME QUOTA_VALUE $project + + For example: + + .. code-block:: console + + $ nova quota-update --user $projectUser --floating-ips 12 $project + $ nova quota-show --user $projectUser --tenant $project + +-----------------------------+-------+ + | Quota | Limit | + +-----------------------------+-------+ + | instances | 10 | + | cores | 20 | + | ram | 51200 | + | floating_ips | 12 | + | fixed_ips | -1 | + | metadata_items | 128 | + | injected_files | 5 | + | injected_file_content_bytes | 10240 | + | injected_file_path_bytes | 255 | + | key_pairs | 100 | + | security_groups | 10 | + | security_group_rules | 20 | + | server_groups | 10 | + | server_group_members | 10 | + +-----------------------------+-------+ + + .. note:: + + To view a list of options for the :command:`nova quota-update` command, + run: + + .. code-block:: console + + $ nova help quota-update + +To display the current quota usage for a project user +----------------------------------------------------- + +Use :command:`nova limits` to get a list of the +current quota values and the current quota usage: + +.. code-block:: console + + $ nova limits --tenant PROJET_NAME + + +------+-----+-------+--------+------+----------------+ + | Verb | URI | Value | Remain | Unit | Next_Available | + +------+-----+-------+--------+------+----------------+ + +------+-----+-------+--------+------+----------------+ + + +--------------------+------+-------+ + | Name | Used | Max | + +--------------------+------+-------+ + | Cores | 0 | 20 | + | Instances | 0 | 10 | + | Keypairs | - | 100 | + | Personality | - | 5 | + | Personality Size | - | 10240 | + | RAM | 0 | 51200 | + | Server Meta | - | 128 | + | ServerGroupMembers | - | 10 | + | ServerGroups | 0 | 10 | + +--------------------+------+-------+ + +.. note:: + + The :command:`nova limits` command generates an empty + table as a result of the Compute API, which prints an + empty list for backward compatibility purposes. diff --git a/doc/source/admin/quotas2.rst b/doc/source/admin/quotas2.rst new file mode 100644 index 00000000000..19cd8d50751 --- /dev/null +++ b/doc/source/admin/quotas2.rst @@ -0,0 +1,54 @@ +.. _manage-quotas: + +============= +Manage quotas +============= + +.. todo:: Merge this into 'quotas.rst' + +To prevent system capacities from being exhausted without notification, you can +set up quotas. Quotas are operational limits. For example, the number of +gigabytes allowed for each project can be controlled so that cloud resources +are optimized. Quotas can be enforced at both the project and the project-user +level. + +Using the command-line interface, you can manage quotas for the OpenStack +Compute service, the OpenStack Block Storage service, and the OpenStack +Networking service. + +The cloud operator typically changes default values because a project requires +more than ten volumes or 1 TB on a compute node. + +.. note:: + + To view all projects, run: + + .. code-block:: console + + $ openstack project list + +----------------------------------+----------+ + | ID | Name | + +----------------------------------+----------+ + | e66d97ac1b704897853412fc8450f7b9 | admin | + | bf4a37b885fe46bd86e999e50adad1d3 | services | + | 21bd1c7c95234fd28f589b60903606fa | tenant01 | + | f599c5cd1cba4125ae3d7caed08e288c | tenant02 | + +----------------------------------+----------+ + + To display all current users for a project, run: + + .. code-block:: console + + $ openstack user list --project PROJECT_NAME + +----------------------------------+--------+ + | ID | Name | + +----------------------------------+--------+ + | ea30aa434ab24a139b0e85125ec8a217 | demo00 | + | 4f8113c1d838467cad0c2f337b3dfded | demo01 | + +----------------------------------+--------+ + +Use :samp:`openstack quota show {PROJECT_NAME}` to list all quotas for a +project. + +Use :samp:`openstack quota set {PROJECT_NAME} {--parameters}` to set quota +values. diff --git a/doc/source/admin/remote-console-access.rst b/doc/source/admin/remote-console-access.rst new file mode 100644 index 00000000000..077fc0a0b78 --- /dev/null +++ b/doc/source/admin/remote-console-access.rst @@ -0,0 +1,324 @@ +=============================== +Configure remote console access +=============================== + +To provide a remote console or remote desktop access to guest virtual machines, +use VNC or SPICE HTML5 through either the OpenStack dashboard or the command +line. Best practice is to select one or the other to run. + +.. _about-nova-consoleauth: + +About nova-consoleauth +~~~~~~~~~~~~~~~~~~~~~~ + +Both client proxies leverage a shared service to manage token authentication +called ``nova-consoleauth``. This service must be running for either proxy to +work. Many proxies of either type can be run against a single +``nova-consoleauth`` service in a cluster configuration. + +Do not confuse the ``nova-consoleauth`` shared service with ``nova-console``, +which is a XenAPI-specific service that most recent VNC proxy architectures do +not use. + +SPICE console +~~~~~~~~~~~~~ + +OpenStack Compute supports VNC consoles to guests. The VNC protocol is fairly +limited, lacking support for multiple monitors, bi-directional audio, reliable +cut-and-paste, video streaming and more. SPICE is a new protocol that aims to +address the limitations in VNC and provide good remote desktop support. + +SPICE support in OpenStack Compute shares a similar architecture to the VNC +implementation. The OpenStack dashboard uses a SPICE-HTML5 widget in its +console tab that communicates to the ``nova-spicehtml5proxy`` service by using +SPICE-over-websockets. The ``nova-spicehtml5proxy`` service communicates +directly with the hypervisor process by using SPICE. + +VNC must be explicitly disabled to get access to the SPICE console. Set the +``vnc_enabled`` option to ``False`` in the ``[DEFAULT]`` section to disable the +VNC console. + +Use the following options to configure SPICE as the console for OpenStack +Compute: + +.. code-block:: console + + [spice] + agent_enabled = False + enabled = True + html5proxy_base_url = http://IP_ADDRESS:6082/spice_auto.html + html5proxy_host = 0.0.0.0 + html5proxy_port = 6082 + keymap = en-us + server_listen = 127.0.0.1 + server_proxyclient_address = 127.0.0.1 + +Replace ``IP_ADDRESS`` with the management interface IP address of the +controller or the VIP. + +VNC console proxy +~~~~~~~~~~~~~~~~~ + +The VNC proxy is an OpenStack component that enables compute service users to +access their instances through VNC clients. + +.. note:: + + The web proxy console URLs do not support the websocket protocol scheme + (ws://) on python versions less than 2.7.4. + +The VNC console connection works as follows: + +#. A user connects to the API and gets an ``access_url`` such as, + ``http://ip:port/?token=xyz``. + +#. The user pastes the URL in a browser or uses it as a client + parameter. + +#. The browser or client connects to the proxy. + +#. The proxy talks to ``nova-consoleauth`` to authorize the token for the user, + and maps the token to the *private* host and port of the VNC server for an + instance. + + The compute host specifies the address that the proxy should use to connect + through the ``nova.conf`` file option, ``vncserver_proxyclient_address``. In + this way, the VNC proxy works as a bridge between the public network and + private host network. + +#. The proxy initiates the connection to VNC server and continues to proxy + until the session ends. + +The proxy also tunnels the VNC protocol over WebSockets so that the ``noVNC`` +client can talk to VNC servers. In general, the VNC proxy: + +- Bridges between the public network where the clients live and the private + network where VNC servers live. + +- Mediates token authentication. + +- Transparently deals with hypervisor-specific connection details to provide a + uniform client experience. + +.. figure:: figures/SCH_5009_V00_NUAC-VNC_OpenStack.png + :alt: noVNC process + :width: 95% + +VNC configuration options +------------------------- + +To customize the VNC console, use the following configuration options in your +``nova.conf`` file: + +.. note:: + + To support :ref:`live migration `, + you cannot specify a specific IP address for ``vncserver_listen``, because + that IP address does not exist on the destination host. + +.. list-table:: **Description of VNC configuration options** + :header-rows: 1 + :widths: 25 25 + + * - Configuration option = Default value + - Description + * - **[DEFAULT]** + - + * - ``daemon = False`` + - (BoolOpt) Become a daemon (background process) + * - ``key = None`` + - (StrOpt) SSL key file (if separate from cert) + * - ``novncproxy_host = 0.0.0.0`` + - (StrOpt) Host on which to listen for incoming requests + * - ``novncproxy_port = 6080`` + - (IntOpt) Port on which to listen for incoming requests + * - ``record = False`` + - (BoolOpt) Record sessions to FILE.[session_number] + * - ``source_is_ipv6 = False`` + - (BoolOpt) Source is ipv6 + * - ``ssl_only = False`` + - (BoolOpt) Disallow non-encrypted connections + * - ``web = /usr/share/spice-html5`` + - (StrOpt) Run webserver on same port. Serve files from DIR. + * - **[vmware]** + - + * - ``vnc_port = 5900`` + - (IntOpt) VNC starting port + * - ``vnc_port_total = 10000`` + - vnc_port_total = 10000 + * - **[vnc]** + - + * - enabled = True + - (BoolOpt) Enable VNC related features + * - novncproxy_base_url = http://127.0.0.1:6080/vnc_auto.html + - (StrOpt) Location of VNC console proxy, in the form + "http://127.0.0.1:6080/vnc_auto.html" + * - vncserver_listen = 127.0.0.1 + - (StrOpt) IP address on which instance vncservers should listen + * - vncserver_proxyclient_address = 127.0.0.1 + - (StrOpt) The address to which proxy clients (like nova-xvpvncproxy) + should connect + * - xvpvncproxy_base_url = http://127.0.0.1:6081/console + - (StrOpt) Location of nova xvp VNC console proxy, in the form + "http://127.0.0.1:6081/console" + +.. note:: + + - The ``vncserver_proxyclient_address`` defaults to ``127.0.0.1``, which is + the address of the compute host that Compute instructs proxies to use when + connecting to instance servers. + + - For all-in-one XenServer domU deployments, set this to ``169.254.0.1.`` + + - For multi-host XenServer domU deployments, set to a ``dom0 management IP`` + on the same network as the proxies. + + - For multi-host libvirt deployments, set to a host management IP on the + same network as the proxies. + +Typical deployment +------------------ + +A typical deployment has the following components: + +- A ``nova-consoleauth`` process. Typically runs on the controller host. + +- One or more ``nova-novncproxy`` services. Supports browser-based noVNC + clients. For simple deployments, this service typically runs on the same + machine as ``nova-api`` because it operates as a proxy between the public + network and the private compute host network. + +- One or more ``nova-xvpvncproxy`` services. Supports the special Java client + discussed here. For simple deployments, this service typically runs on the + same machine as ``nova-api`` because it acts as a proxy between the public + network and the private compute host network. + +- One or more compute hosts. These compute hosts must have correctly configured + options, as follows. + +nova-novncproxy (noVNC) +----------------------- + +You must install the noVNC package, which contains the ``nova-novncproxy`` +service. As root, run the following command: + +.. code-block:: console + + # apt-get install nova-novncproxy + +The service starts automatically on installation. + +To restart the service, run: + +.. code-block:: console + + # service nova-novncproxy restart + +The configuration option parameter should point to your ``nova.conf`` file, +which includes the message queue server address and credentials. + +By default, ``nova-novncproxy`` binds on ``0.0.0.0:6080``. + +To connect the service to your Compute deployment, add the following +configuration options to your ``nova.conf`` file: + +- ``vncserver_listen=0.0.0.0`` + + Specifies the address on which the VNC service should bind. Make sure it is + assigned one of the compute node interfaces. This address is the one used by + your domain file. + + .. code-block:: console + + + + .. note:: + + To use live migration, use the 0.0.0.0 address. + +- ``vncserver_proxyclient_address=127.0.0.1`` + + The address of the compute host that Compute instructs proxies to use when + connecting to instance ``vncservers``. + +Frequently asked questions about VNC access to virtual machines +--------------------------------------------------------------- + +- **Q: What is the difference between ``nova-xvpvncproxy`` and + ``nova-novncproxy``?** + + A: ``nova-xvpvncproxy``, which ships with OpenStack Compute, is a proxy that + supports a simple Java client. nova-novncproxy uses noVNC to provide VNC + support through a web browser. + +- **Q: I want VNC support in the OpenStack dashboard. What services do I + need?** + + A: You need ``nova-novncproxy``, ``nova-consoleauth``, and correctly + configured compute hosts. + +- **Q: When I use ``nova get-vnc-console`` or click on the VNC tab of the + OpenStack dashboard, it hangs. Why?** + + A: Make sure you are running ``nova-consoleauth`` (in addition to + ``nova-novncproxy``). The proxies rely on ``nova-consoleauth`` to validate + tokens, and waits for a reply from them until a timeout is reached. + +- **Q: My VNC proxy worked fine during my all-in-one test, but now it doesn't + work on multi host. Why?** + + A: The default options work for an all-in-one install, but changes must be + made on your compute hosts once you start to build a cluster. As an example, + suppose you have two servers: + + .. code-block:: bash + + PROXYSERVER (public_ip=172.24.1.1, management_ip=192.168.1.1) + COMPUTESERVER (management_ip=192.168.1.2) + + Your ``nova-compute`` configuration file must set the following values: + + .. code-block:: console + + # These flags help construct a connection data structure + vncserver_proxyclient_address=192.168.1.2 + novncproxy_base_url=http://172.24.1.1:6080/vnc_auto.html + xvpvncproxy_base_url=http://172.24.1.1:6081/console + + # This is the address where the underlying vncserver (not the proxy) + # will listen for connections. + vncserver_listen=192.168.1.2 + + .. note:: + + ``novncproxy_base_url`` and ``xvpvncproxy_base_url`` use a public IP; this + is the URL that is ultimately returned to clients, which generally do not + have access to your private network. Your PROXYSERVER must be able to + reach ``vncserver_proxyclient_address``, because that is the address over + which the VNC connection is proxied. + +- **Q: My noVNC does not work with recent versions of web browsers. Why?** + + A: Make sure you have installed ``python-numpy``, which is required to + support a newer version of the WebSocket protocol (HyBi-07+). + +- **Q: How do I adjust the dimensions of the VNC window image in the OpenStack + dashboard?** + + A: These values are hard-coded in a Django HTML template. To alter them, edit + the ``_detail_vnc.html`` template file. The location of this file varies + based on Linux distribution. On Ubuntu 14.04, the file is at + ``/usr/share/pyshared/horizon/dashboards/nova/instances/templates/instances/_detail_vnc.html``. + + Modify the ``width`` and ``height`` options, as follows: + + .. code-block:: console + + + +- **Q: My noVNC connections failed with ValidationError: Origin header protocol + does not match. Why?** + + A: Make sure the ``base_url`` match your TLS setting. If you are using https + console connections, make sure that the value of ``novncproxy_base_url`` is + set explicitly where the ``nova-novncproxy`` service is running. diff --git a/doc/source/admin/root-wrap-reference.rst b/doc/source/admin/root-wrap-reference.rst new file mode 100644 index 00000000000..b25b8200e8c --- /dev/null +++ b/doc/source/admin/root-wrap-reference.rst @@ -0,0 +1,112 @@ +==================== +Secure with rootwrap +==================== + +Rootwrap allows unprivileged users to safely run Compute actions as the root +user. Compute previously used :command:`sudo` for this purpose, but this was +difficult to maintain, and did not allow advanced filters. The +:command:`rootwrap` command replaces :command:`sudo` for Compute. + +To use rootwrap, prefix the Compute command with :command:`nova-rootwrap`. For +example: + +.. code-block:: console + + $ sudo nova-rootwrap /etc/nova/rootwrap.conf command + +A generic ``sudoers`` entry lets the Compute user run :command:`nova-rootwrap` +as root. The :command:`nova-rootwrap` code looks for filter definition +directories in its configuration file, and loads command filters from them. It +then checks if the command requested by Compute matches one of those filters +and, if so, executes the command (as root). If no filter matches, it denies the +request. + +.. note:: + + Be aware of issues with using NFS and root-owned files. The NFS share must + be configured with the ``no_root_squash`` option enabled, in order for + rootwrap to work correctly. + +Rootwrap is fully controlled by the root user. The root user owns the sudoers +entry which allows Compute to run a specific rootwrap executable as root, and +only with a specific configuration file (which should also be owned by root). +The :command:`nova-rootwrap` command imports the Python modules it needs from a +cleaned, system-default PYTHONPATH. The root-owned configuration file points +to root-owned filter definition directories, which contain root-owned filters +definition files. This chain ensures that the Compute user itself is not in +control of the configuration or modules used by the :command:`nova-rootwrap` +executable. + +Configure rootwrap +~~~~~~~~~~~~~~~~~~ + +Configure rootwrap in the ``rootwrap.conf`` file. Because it is in the trusted +security path, it must be owned and writable by only the root user. The +``rootwrap_config=entry`` parameter specifies the file's location in the +sudoers entry and in the ``nova.conf`` configuration file. + +The ``rootwrap.conf`` file uses an INI file format with these sections and +parameters: + +.. list-table:: **rootwrap.conf configuration options** + :widths: 64 31 + + * - Configuration option=Default value + - (Type) Description + * - [DEFAULT] + filters\_path=/etc/nova/rootwrap.d,/usr/share/nova/rootwrap + - (ListOpt) Comma-separated list of directories + containing filter definition files. + Defines where rootwrap filters are stored. + Directories defined on this line should all + exist, and be owned and writable only by the + root user. + +If the root wrapper is not performing correctly, you can add a workaround +option into the ``nova.conf`` configuration file. This workaround re-configures +the root wrapper configuration to fall back to running commands as ``sudo``, +and is a Kilo release feature. + +Including this workaround in your configuration file safeguards your +environment from issues that can impair root wrapper performance. Tool changes +that have impacted `Python Build Reasonableness (PBR) +`__ for example, are a known +issue that affects root wrapper performance. + +To set up this workaround, configure the ``disable_rootwrap`` option in the +``[workaround]`` section of the ``nova.conf`` configuration file. + +The filters definition files contain lists of filters that rootwrap will use to +allow or deny a specific command. They are generally suffixed by ``.filters`` . +Since they are in the trusted security path, they need to be owned and writable +only by the root user. Their location is specified in the ``rootwrap.conf`` +file. + +Filter definition files use an INI file format with a ``[Filters]`` section and +several lines, each with a unique parameter name, which should be different for +each filter you define: + +.. list-table:: **Filters configuration options** + :widths: 72 39 + + + * - Configuration option=Default value + - (Type) Description + * - [Filters] + filter\_name=kpartx: CommandFilter, /sbin/kpartx, root + - (ListOpt) Comma-separated list containing the filter class to + use, followed by the Filter arguments (which vary depending + on the Filter class selected). + +Configure the rootwrap daemon +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Administrators can use rootwrap daemon support instead of running rootwrap with +:command:`sudo`. The rootwrap daemon reduces the overhead and performance loss +that results from running ``oslo.rootwrap`` with :command:`sudo`. Each call +that needs rootwrap privileges requires a new instance of rootwrap. The daemon +prevents overhead from the repeated calls. The daemon does not support long +running processes, however. + +To enable the rootwrap daemon, set ``use_rootwrap_daemon`` to ``True`` in the +Compute service configuration file. diff --git a/doc/source/admin/security-groups.rst b/doc/source/admin/security-groups.rst new file mode 100644 index 00000000000..9a55ef2800e --- /dev/null +++ b/doc/source/admin/security-groups.rst @@ -0,0 +1,251 @@ +======================= +Manage project security +======================= + +Security groups are sets of IP filter rules that are applied to all project +instances, which define networking access to the instance. Group rules are +project specific; project members can edit the default rules for their group +and add new rule sets. + +All projects have a ``default`` security group which is applied to any instance +that has no other defined security group. Unless you change the default, this +security group denies all incoming traffic and allows only outgoing traffic to +your instance. + +You can use the ``allow_same_net_traffic`` option in the +``/etc/nova/nova.conf`` file to globally control whether the rules apply to +hosts which share a network. There are two possible values: + +``True`` (default) + Hosts on the same subnet are not filtered and are allowed to pass all types + of traffic between them. On a flat network, this allows all instances from + all projects unfiltered communication. With VLAN networking, this allows + access between instances within the same project. You can also simulate this + setting by configuring the default security group to allow all traffic from + the subnet. + +``False`` + Security groups are enforced for all connections. + +Additionally, the number of maximum rules per security group is controlled by +the ``security_group_rules`` and the number of allowed security groups per +project is controlled by the ``security_groups`` quota (see +:ref:`manage-quotas`). + +List and view current security groups +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +From the command-line you can get a list of security groups for the project, +using the :command:`openstack` and :command:`nova` commands: + +#. Ensure your system variables are set for the user and project for which you + are checking security group rules. For example: + + .. code-block:: console + + export OS_USERNAME=demo00 + export OS_TENANT_NAME=tenant01 + +#. Output security groups, as follows: + + .. code-block:: console + + $ openstack security group list + +--------------------------------------+---------+-------------+ + | Id | Name | Description | + +--------------------------------------+---------+-------------+ + | 73580272-d8fa-4927-bd55-c85e43bc4877 | default | default | + | 6777138a-deb7-4f10-8236-6400e7aff5b0 | open | all ports | + +--------------------------------------+---------+-------------+ + +#. View the details of a group, as follows: + + .. code-block:: console + + $ openstack security group rule list GROUPNAME + + For example: + + .. code-block:: console + + $ openstack security group rule list open + +--------------------------------------+-------------+-----------+-----------------+-----------------------+ + | ID | IP Protocol | IP Range | Port Range | Remote Security Group | + +--------------------------------------+-------------+-----------+-----------------+-----------------------+ + | 353d0611-3f67-4848-8222-a92adbdb5d3a | udp | 0.0.0.0/0 | 1:65535 | None | + | 63536865-e5b6-4df1-bac5-ca6d97d8f54d | tcp | 0.0.0.0/0 | 1:65535 | None | + +--------------------------------------+-------------+-----------+-----------------+-----------------------+ + + These rules are allow type rules as the default is deny. The first column is + the IP protocol (one of ICMP, TCP, or UDP). The second and third columns + specify the affected port range. The third column specifies the IP range in + CIDR format. This example shows the full port range for all protocols + allowed from all IPs. + +Create a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +When adding a new security group, you should pick a descriptive but brief name. +This name shows up in brief descriptions of the instances that use it where the +longer description field often does not. For example, seeing that an instance +is using security group "http" is much easier to understand than "bobs\_group" +or "secgrp1". + +#. Ensure your system variables are set for the user and project for which you + are creating security group rules. + +#. Add the new security group, as follows: + + .. code-block:: console + + $ openstack security group create GroupName --description Description + + For example: + + .. code-block:: console + + $ openstack security group create global_http --description "Allows Web traffic anywhere on the Internet." + +-----------------+--------------------------------------------------------------------------------------------------------------------------+ + | Field | Value | + +-----------------+--------------------------------------------------------------------------------------------------------------------------+ + | created_at | 2016-11-03T13:50:53Z | + | description | Allows Web traffic anywhere on the Internet. | + | headers | | + | id | c0b92b20-4575-432a-b4a9-eaf2ad53f696 | + | name | global_http | + | project_id | 5669caad86a04256994cdf755df4d3c1 | + | project_id | 5669caad86a04256994cdf755df4d3c1 | + | revision_number | 1 | + | rules | created_at='2016-11-03T13:50:53Z', direction='egress', ethertype='IPv4', id='4d8cec94-e0ee-4c20-9f56-8fb67c21e4df', | + | | project_id='5669caad86a04256994cdf755df4d3c1', revision_number='1', updated_at='2016-11-03T13:50:53Z' | + | | created_at='2016-11-03T13:50:53Z', direction='egress', ethertype='IPv6', id='31be2ad1-be14-4aef-9492-ecebede2cf12', | + | | project_id='5669caad86a04256994cdf755df4d3c1', revision_number='1', updated_at='2016-11-03T13:50:53Z' | + | updated_at | 2016-11-03T13:50:53Z | + +-----------------+--------------------------------------------------------------------------------------------------------------------------+ + +#. Add a new group rule, as follows: + + .. code-block:: console + + $ openstack security group rule create SEC_GROUP_NAME \ + --protocol PROTOCOL --dst-port FROM_PORT:TO_PORT --remote-ip CIDR + + The arguments are positional, and the ``from-port`` and ``to-port`` + arguments specify the local port range connections are allowed to access, + not the source and destination ports of the connection. For example: + + .. code-block:: console + + $ openstack security group rule create global_http \ + --protocol tcp --dst-port 80:80 --remote-ip 0.0.0.0/0 + +-------------------+--------------------------------------+ + | Field | Value | + +-------------------+--------------------------------------+ + | created_at | 2016-11-06T14:02:00Z | + | description | | + | direction | ingress | + | ethertype | IPv4 | + | headers | | + | id | 2ba06233-d5c8-43eb-93a9-8eaa94bc9eb5 | + | port_range_max | 80 | + | port_range_min | 80 | + | project_id | 5669caad86a04256994cdf755df4d3c1 | + | project_id | 5669caad86a04256994cdf755df4d3c1 | + | protocol | tcp | + | remote_group_id | None | + | remote_ip_prefix | 0.0.0.0/0 | + | revision_number | 1 | + | security_group_id | c0b92b20-4575-432a-b4a9-eaf2ad53f696 | + | updated_at | 2016-11-06T14:02:00Z | + +-------------------+--------------------------------------+ + + You can create complex rule sets by creating additional rules. For example, + if you want to pass both HTTP and HTTPS traffic, run: + + .. code-block:: console + + $ openstack security group rule create global_http \ + --protocol tcp --dst-port 443:443 --remote-ip 0.0.0.0/0 + +-------------------+--------------------------------------+ + | Field | Value | + +-------------------+--------------------------------------+ + | created_at | 2016-11-06T14:09:20Z | + | description | | + | direction | ingress | + | ethertype | IPv4 | + | headers | | + | id | 821c3ef6-9b21-426b-be5b-c8a94c2a839c | + | port_range_max | 443 | + | port_range_min | 443 | + | project_id | 5669caad86a04256994cdf755df4d3c1 | + | project_id | 5669caad86a04256994cdf755df4d3c1 | + | protocol | tcp | + | remote_group_id | None | + | remote_ip_prefix | 0.0.0.0/0 | + | revision_number | 1 | + | security_group_id | c0b92b20-4575-432a-b4a9-eaf2ad53f696 | + | updated_at | 2016-11-06T14:09:20Z | + +-------------------+--------------------------------------+ + + Despite only outputting the newly added rule, this operation is additive + (both rules are created and enforced). + +#. View all rules for the new security group, as follows: + + .. code-block:: console + + $ openstack security group rule list global_http + +--------------------------------------+-------------+-----------+-----------------+-----------------------+ + | ID | IP Protocol | IP Range | Port Range | Remote Security Group | + +--------------------------------------+-------------+-----------+-----------------+-----------------------+ + | 353d0611-3f67-4848-8222-a92adbdb5d3a | tcp | 0.0.0.0/0 | 80:80 | None | + | 63536865-e5b6-4df1-bac5-ca6d97d8f54d | tcp | 0.0.0.0/0 | 443:443 | None | + +--------------------------------------+-------------+-----------+-----------------+-----------------------+ + +Delete a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +#. Ensure your system variables are set for the user and project for which you + are deleting a security group. + +#. Delete the new security group, as follows: + + .. code-block:: console + + $ openstack security group delete GROUPNAME + + For example: + + .. code-block:: console + + $ openstack security group delete global_http + +Create security group rules for a cluster of instances +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Source Groups are a special, dynamic way of defining the CIDR of allowed +sources. The user specifies a Source Group (Security Group name), and all the +user's other Instances using the specified Source Group are selected +dynamically. This alleviates the need for individual rules to allow each new +member of the cluster. + +#. Make sure to set the system variables for the user and project for which you + are creating a security group rule. + +#. Add a source group, as follows: + + .. code-block:: console + + $ openstack security group rule create secGroupName \ + --remote-group source-group --protocol ip-protocol \ + --dst-port from-port:to-port + + For example: + + .. code-block:: console + + $ openstack security group rule create cluster \ + --remote-group global_http --protocol tcp --dst-port 22:22 + + The ``cluster`` rule allows SSH access from any other instance that uses the + ``global_http`` group. diff --git a/doc/source/admin/security.rst b/doc/source/admin/security.rst new file mode 100644 index 00000000000..1727f441ffd --- /dev/null +++ b/doc/source/admin/security.rst @@ -0,0 +1,168 @@ +================== +Security hardening +================== + +OpenStack Compute can be integrated with various third-party technologies to +increase security. For more information, see the `OpenStack Security Guide +`_. + +Trusted compute pools +~~~~~~~~~~~~~~~~~~~~~ + +Administrators can designate a group of compute hosts as trusted using trusted +compute pools. The trusted hosts use hardware-based security features, such as +the Intel Trusted Execution Technology (TXT), to provide an additional level of +security. Combined with an external stand-alone, web-based remote attestation +server, cloud providers can ensure that the compute node runs only software +with verified measurements and can ensure a secure cloud stack. + +Trusted compute pools provide the ability for cloud subscribers to request +services run only on verified compute nodes. + +The remote attestation server performs node verification like this: + +1. Compute nodes boot with Intel TXT technology enabled. + +2. The compute node BIOS, hypervisor, and operating system are measured. + +3. When the attestation server challenges the compute node, the measured data + is sent to the attestation server. + +4. The attestation server verifies the measurements against a known good + database to determine node trustworthiness. + +A description of how to set up an attestation service is beyond the scope of +this document. For an open source project that you can use to implement an +attestation service, see the `Open Attestation +`__ project. + +.. figure:: figures/OpenStackTrustedComputePool1.png + + **Configuring Compute to use trusted compute pools** + +#. Enable scheduling support for trusted compute pools by adding these lines to + the ``DEFAULT`` section of the ``/etc/nova/nova.conf`` file: + + .. code-block:: ini + + [DEFAULT] + compute_scheduler_driver=nova.scheduler.filter_scheduler.FilterScheduler + scheduler_available_filters=nova.scheduler.filters.all_filters + scheduler_default_filters=AvailabilityZoneFilter,RamFilter,ComputeFilter,TrustedFilter + +#. Specify the connection information for your attestation service by adding + these lines to the ``trusted_computing`` section of the + ``/etc/nova/nova.conf`` file: + + .. code-block:: ini + + [trusted_computing] + attestation_server = 10.1.71.206 + attestation_port = 8443 + # If using OAT v2.0 after, use this port: + # attestation_port = 8181 + attestation_server_ca_file = /etc/nova/ssl.10.1.71.206.crt + # If using OAT v1.5, use this api_url: + attestation_api_url = /AttestationService/resources + # If using OAT pre-v1.5, use this api_url: + # attestation_api_url = /OpenAttestationWebServices/V1.0 + attestation_auth_blob = i-am-openstack + + In this example: + + ``server`` + Host name or IP address of the host that runs the attestation service + + ``port`` + HTTPS port for the attestation service + + ``server_ca_file`` + Certificate file used to verify the attestation server's identity + + ``api_url`` + The attestation service's URL path + + ``auth_blob`` + An authentication blob, required by the attestation service. + +#. Save the file, and restart the ``nova-compute`` and ``nova-scheduler`` + service to pick up the changes. + +To customize the trusted compute pools, use these configuration option +settings: + +.. list-table:: **Description of trusted computing configuration options** + :header-rows: 2 + + * - Configuration option = Default value + - Description + * - [trusted_computing] + - + * - attestation_api_url = /OpenAttestationWebServices/V1.0 + - (StrOpt) Attestation web API URL + * - attestation_auth_blob = None + - (StrOpt) Attestation authorization blob - must change + * - attestation_auth_timeout = 60 + - (IntOpt) Attestation status cache valid period length + * - attestation_insecure_ssl = False + - (BoolOpt) Disable SSL cert verification for Attestation service + * - attestation_port = 8443 + - (StrOpt) Attestation server port + * - attestation_server = None + - (StrOpt) Attestation server HTTP + * - attestation_server_ca_file = None + - (StrOpt) Attestation server Cert file for Identity verification + +**Specifying trusted flavors** + +#. Flavors can be designated as trusted using the :command:`openstack flavor + set` command. In this example, the ``m1.tiny`` flavor is being set as + trusted: + + .. code-block:: console + + $ openstack flavor set --property trusted_host=trusted m1.tiny + +#. You can request that your instance is run on a trusted host by specifying a + trusted flavor when booting the instance: + + .. code-block:: console + + $ openstack server create --flavor m1.tiny \ + --key-name myKeypairName --image myImageID newInstanceName + + +.. figure:: figures/OpenStackTrustedComputePool2.png + +Encrypt Compute metadata traffic +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Enabling SSL encryption** + +OpenStack supports encrypting Compute metadata traffic with HTTPS. Enable SSL +encryption in the ``metadata_agent.ini`` file. + +#. Enable the HTTPS protocol. + + .. code-block:: ini + + nova_metadata_protocol = https + +#. Determine whether insecure SSL connections are accepted for Compute metadata + server requests. The default value is ``False``. + + .. code-block:: ini + + nova_metadata_insecure = False + +#. Specify the path to the client certificate. + + .. code-block:: ini + + nova_client_cert = PATH_TO_CERT + +#. Specify the path to the private key. + + .. code-block:: ini + + nova_client_priv_key = PATH_TO_KEY diff --git a/doc/source/admin/service-groups.rst b/doc/source/admin/service-groups.rst new file mode 100644 index 00000000000..f6e0f8fa8a7 --- /dev/null +++ b/doc/source/admin/service-groups.rst @@ -0,0 +1,67 @@ +================================ +Configure Compute service groups +================================ + +The Compute service must know the status of each compute node to effectively +manage and use them. This can include events like a user launching a new VM, +the scheduler sending a request to a live node, or a query to the ServiceGroup +API to determine if a node is live. + +When a compute worker running the nova-compute daemon starts, it calls the join +API to join the compute group. Any service (such as the scheduler) can query +the group's membership and the status of its nodes. Internally, the +ServiceGroup client driver automatically updates the compute worker status. + +.. _database-servicegroup-driver: + +Database ServiceGroup driver +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, Compute uses the database driver to track if a node is live. In a +compute worker, this driver periodically sends a ``db update`` command to the +database, saying “I'm OK” with a timestamp. Compute uses a pre-defined +timeout (``service_down_time``) to determine if a node is dead. + +The driver has limitations, which can be problematic depending on your +environment. If a lot of compute worker nodes need to be checked, the database +can be put under heavy load, which can cause the timeout to trigger, and a live +node could incorrectly be considered dead. By default, the timeout is 60 +seconds. Reducing the timeout value can help in this situation, but you must +also make the database update more frequently, which again increases the +database workload. + +The database contains data that is both transient (such as whether the node is +alive) and persistent (such as entries for VM owners). With the ServiceGroup +abstraction, Compute can treat each type separately. + +.. _memcache-servicegroup-driver: + +Memcache ServiceGroup driver +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The memcache ServiceGroup driver uses memcached, a distributed memory object +caching system that is used to increase site performance. For more details, see +`memcached.org `_. + +To use the memcache driver, you must install memcached. You might already have +it installed, as the same driver is also used for the OpenStack Object Storage +and OpenStack dashboard. To install memcached, see the *Environment -> +Memcached* section in the `Installation Tutorials and Guides +`_ depending on your +distribution. + +These values in the ``/etc/nova/nova.conf`` file are required on every node for +the memcache driver: + +.. code-block:: ini + + # Driver for the ServiceGroup service + servicegroup_driver = "mc" + + # Memcached servers. Use either a list of memcached servers to use for caching (list value), + # or "" for in-process caching (default). + memcached_servers = + + # Timeout; maximum time since last check-in for up service (integer value). + # Helps to define whether a node is dead + service_down_time = 60 diff --git a/doc/source/admin/services.rst b/doc/source/admin/services.rst new file mode 100644 index 00000000000..648340fb07f --- /dev/null +++ b/doc/source/admin/services.rst @@ -0,0 +1,72 @@ +======================= +Manage Compute services +======================= + +You can enable and disable Compute services. The following examples disable and +enable the ``nova-compute`` service. + +#. List the Compute services: + + .. code-block:: console + + $ openstack compute service list + +----+--------------+------------+----------+---------+-------+--------------+ + | ID | Binary | Host | Zone | Status | State | Updated At | + +----+--------------+------------+----------+---------+-------+--------------+ + | 4 | nova- | controller | internal | enabled | up | 2016-12-20T0 | + | | consoleauth | | | | | 0:44:48.0000 | + | | | | | | | 00 | + | 5 | nova- | controller | internal | enabled | up | 2016-12-20T0 | + | | scheduler | | | | | 0:44:48.0000 | + | | | | | | | 00 | + | 6 | nova- | controller | internal | enabled | up | 2016-12-20T0 | + | | conductor | | | | | 0:44:54.0000 | + | | | | | | | 00 | + | 9 | nova-compute | compute | nova | enabled | up | 2016-10-21T0 | + | | | | | | | 2:35:03.0000 | + | | | | | | | 00 | + +----+--------------+------------+----------+---------+-------+--------------+ + +#. Disable a nova service: + + .. code-block:: console + + $ openstack compute service set --disable --disable-reason trial log nova nova-compute + +----------+--------------+----------+-------------------+ + | Host | Binary | Status | Disabled Reason | + +----------+--------------+----------+-------------------+ + | compute | nova-compute | disabled | trial log | + +----------+--------------+----------+-------------------+ + +#. Check the service list: + + .. code-block:: console + + $ openstack compute service list + +----+--------------+------------+----------+---------+-------+--------------+ + | ID | Binary | Host | Zone | Status | State | Updated At | + +----+--------------+------------+----------+---------+-------+--------------+ + | 4 | nova- | controller | internal | enabled | up | 2016-12-20T0 | + | | consoleauth | | | | | 0:44:48.0000 | + | | | | | | | 00 | + | 5 | nova- | controller | internal | enabled | up | 2016-12-20T0 | + | | scheduler | | | | | 0:44:48.0000 | + | | | | | | | 00 | + | 6 | nova- | controller | internal | enabled | up | 2016-12-20T0 | + | | conductor | | | | | 0:44:54.0000 | + | | | | | | | 00 | + | 9 | nova-compute | compute | nova | disabled| up | 2016-10-21T0 | + | | | | | | | 2:35:03.0000 | + | | | | | | | 00 | + +----+--------------+------------+----------+---------+-------+--------------+ + +#. Enable the service: + + .. code-block:: console + + $ openstack compute service set --enable nova nova-compute + +----------+--------------+---------+ + | Host | Binary | Status | + +----------+--------------+---------+ + | compute | nova-compute | enabled | + +----------+--------------+---------+ diff --git a/doc/source/admin/ssh-configuration.rst b/doc/source/admin/ssh-configuration.rst new file mode 100644 index 00000000000..d3fb9261128 --- /dev/null +++ b/doc/source/admin/ssh-configuration.rst @@ -0,0 +1,77 @@ +.. _cli-os-migrate-cfg-ssh: + +=================================== +Configure SSH between compute nodes +=================================== + +.. todo:: + + Consider merging this into a larger "live-migration" document or to the + installation guide + +If you are resizing or migrating an instance between hypervisors, you might +encounter an SSH (Permission denied) error. Ensure that each node is configured +with SSH key authentication so that the Compute service can use SSH to move +disks to other nodes. + +To share a key pair between compute nodes, complete the following steps: + +#. On the first node, obtain a key pair (public key and private key). Use the + root key that is in the ``/root/.ssh/id_rsa`` and ``/root/.ssh/id_ras.pub`` + directories or generate a new key pair. + +#. Run :command:`setenforce 0` to put SELinux into permissive mode. + +#. Enable login abilities for the nova user: + + .. code-block:: console + + # usermod -s /bin/bash nova + + Switch to the nova account. + + .. code-block:: console + + # su nova + +#. As root, create the folder that is needed by SSH and place the private key + that you obtained in step 1 into this folder: + + .. code-block:: console + + mkdir -p /var/lib/nova/.ssh + cp /var/lib/nova/.ssh/id_rsa + echo 'StrictHostKeyChecking no' >> /var/lib/nova/.ssh/config + chmod 600 /var/lib/nova/.ssh/id_rsa /var/lib/nova/.ssh/authorized_keys + +#. Repeat steps 2-4 on each node. + + .. note:: + + The nodes must share the same key pair, so do not generate a new key pair + for any subsequent nodes. + +#. From the first node, where you created the SSH key, run: + + .. code-block:: console + + ssh-copy-id -i nova@remote-host + + This command installs your public key in a remote machine's + ``authorized_keys`` folder. + +#. Ensure that the nova user can now log in to each node without using a + password: + + .. code-block:: console + + # su nova + $ ssh *computeNodeAddress* + $ exit + +#. As root on each node, restart both libvirt and the Compute services: + + .. code-block:: console + + # systemctl restart libvirtd.service + # systemctl restart openstack-nova-compute.service diff --git a/doc/source/admin/support-compute.rst b/doc/source/admin/support-compute.rst new file mode 100644 index 00000000000..1379486aabe --- /dev/null +++ b/doc/source/admin/support-compute.rst @@ -0,0 +1,426 @@ +==================== +Troubleshoot Compute +==================== + +Common problems for Compute typically involve misconfigured networking or +credentials that are not sourced properly in the environment. Also, most flat +networking configurations do not enable :command:`ping` or :command:`ssh` from +a compute node to the instances that run on that node. Another common problem +is trying to run 32-bit images on a 64-bit compute node. This section shows +you how to troubleshoot Compute. + +Compute service logging +~~~~~~~~~~~~~~~~~~~~~~~ + +Compute stores a log file for each service in ``/var/log/nova``. For example, +``nova-compute.log`` is the log for the ``nova-compute`` service. You can set +the following options to format log strings for the ``nova.log`` module in the +``nova.conf`` file: + +* ``logging_context_format_string`` + +* ``logging_default_format_string`` + +If the log level is set to ``debug``, you can also specify +``logging_debug_format_suffix`` to append extra formatting. For information +about what variables are available for the formatter, see `Formatter Objects +`_. + +You have two logging options for OpenStack Compute based on configuration +settings. In ``nova.conf``, include the ``logfile`` option to enable logging. +Alternatively you can set ``use_syslog = 1`` so that the nova daemon logs to +syslog. + +Guru Meditation reports +~~~~~~~~~~~~~~~~~~~~~~~ + +A Guru Meditation report is sent by the Compute service upon receipt of the +``SIGUSR2`` signal (``SIGUSR1`` before Mitaka). This report is a +general-purpose error report that includes details about the current state of +the service. The error report is sent to ``stderr``. + +For example, if you redirect error output to ``nova-api-err.log`` using +:command:`nova-api 2>/var/log/nova/nova-api-err.log`, resulting in the process +ID 8675, you can then run: + +.. code-block:: console + + # kill -USR2 8675 + +This command triggers the Guru Meditation report to be printed to +``/var/log/nova/nova-api-err.log``. + +The report has the following sections: + +* Package: Displays information about the package to which the process belongs, + including version information. + +* Threads: Displays stack traces and thread IDs for each of the threads within + the process. + +* Green Threads: Displays stack traces for each of the green threads within the + process (green threads do not have thread IDs). + +* Configuration: Lists all configuration options currently accessible through + the CONF object for the current process. + +For more information, see `Guru Meditation Reports +`_. + +.. _compute-common-errors-and-fixes: + +Common errors and fixes for Compute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `ask.openstack.org `_ site offers a place to ask +and answer questions, and you can also mark questions as frequently asked +questions. This section describes some errors people have posted previously. +Bugs are constantly being fixed, so online resources are a great way to get the +most up-to-date errors and fixes. + +Credential errors, 401, and 403 forbidden errors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +Missing credentials cause a ``403 forbidden`` error. + +Solution +-------- + +To resolve this issue, use one of these methods: + +#. Manual method + + Gets the ``novarc`` file from the project ZIP file, saves existing + credentials in case of override, and manually sources the ``novarc`` file. + +#. Script method + + Generates ``novarc`` from the project ZIP file and sources it for you. + +When you run ``nova-api`` the first time, it generates the certificate +authority information, including ``openssl.cnf``. If you start the CA services +before this, you might not be able to create your ZIP file. Restart the +services. When your CA information is available, create your ZIP file. + +Also, check your HTTP proxy settings to see whether they cause problems with +``novarc`` creation. + +Instance errors +~~~~~~~~~~~~~~~ + +Problem +------- + +Sometimes a particular instance shows ``pending`` or you cannot SSH to it. +Sometimes the image itself is the problem. For example, when you use flat +manager networking, you do not have a DHCP server and certain images do not +support interface injection; you cannot connect to them. + +Solution +-------- + +To fix instance errors use an image that does support this method, such as +Ubuntu, which obtains an IP address correctly with FlatManager network +settings. + +To troubleshoot other possible problems with an instance, such as an instance +that stays in a spawning state, check the directory for the particular instance +under ``/var/lib/nova/instances`` on the ``nova-compute`` host and make sure +that these files are present: + +* ``libvirt.xml`` +* ``disk`` +* ``disk-raw`` +* ``kernel`` +* ``ramdisk`` +* ``console.log``, after the instance starts. + +If any files are missing, empty, or very small, the ``nova-compute`` service +did not successfully download the images from the Image service. + +Also check ``nova-compute.log`` for exceptions. Sometimes they do not appear in +the console output. + +Next, check the log file for the instance in the ``/var/log/libvirt/qemu`` +directory to see if it exists and has any useful error messages in it. + +Finally, from the ``/var/lib/nova/instances`` directory for the instance, see +if this command returns an error: + +.. code-block:: console + + # virsh create libvirt.xml + +Empty log output for Linux instances +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +You can view the log output of running instances from either the +:guilabel:`Log` tab of the dashboard or the output of :command:`nova +console-log`. In some cases, the log output of a running Linux instance will be +empty or only display a single character (for example, the `?` character). + +This occurs when the Compute service attempts to retrieve the log output of the +instance via a serial console while the instance itself is not configured to +send output to the console. + +Solution +-------- + +To rectify this, append the following parameters to kernel arguments specified +in the instance's boot loader: + +.. code-block:: ini + + console=tty0 console=ttyS0,115200n8 + +Upon rebooting, the instance will be configured to send output to the Compute +service. + +Reset the state of an instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +Instances can remain in an intermediate state, such as ``deleting``. + +Solution +-------- + +You can use the :command:`nova reset-state` command to manually reset the state +of an instance to an error state. You can then delete the instance. For +example: + +.. code-block:: console + + $ nova reset-state c6bbbf26-b40a-47e7-8d5c-eb17bf65c485 + $ openstack server delete c6bbbf26-b40a-47e7-8d5c-eb17bf65c485 + +You can also use the ``--active`` parameter to force the instance back to an +active state instead of an error state. For example: + +.. code-block:: console + + $ nova reset-state --active c6bbbf26-b40a-47e7-8d5c-eb17bf65c485 + +Injection problems +~~~~~~~~~~~~~~~~~~ + +Problem +------- + +Instances may boot slowly, or do not boot. File injection can cause this +problem. + +Solution +-------- + +To disable injection in libvirt, set the following in ``nova.conf``: + +.. code-block:: ini + + [libvirt] + inject_partition = -2 + +.. note:: + + If you have not enabled the configuration drive and you want to make + user-specified files available from the metadata server for to improve + performance and avoid boot failure if injection fails, you must disable + injection. + +Disable live snapshotting +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +Administrators using libvirt version ``1.2.2`` may experience problems with +live snapshot creation. Occasionally, libvirt version ``1.2.2`` fails to create +live snapshots under the load of creating concurrent snapshot. + +Solution +-------- + +To effectively disable the libvirt live snapshotting, until the problem is +resolved, configure the ``disable_libvirt_livesnapshot`` option. You can turn +off the live snapshotting mechanism by setting up its value to ``True`` in the +``[workarounds]`` section of the ``nova.conf`` file: + +.. code-block:: ini + + [workarounds] + disable_libvirt_livesnapshot = True + +Cannot find suitable emulator for x86_64 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +When you attempt to create a VM, the error shows the VM is in the ``BUILD`` +then ``ERROR`` state. + +Solution +-------- + +On the KVM host, run :command:`cat /proc/cpuinfo`. Make sure the ``vmx`` or +``svm`` flags are set. + +Follow the instructions in the :ref:`enable-kvm` +section in the Nova Configuration Reference to enable hardware +virtualization support in your BIOS. + +Failed to attach volume after detaching +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +Failed to attach a volume after detaching the same volume. + +Solution +-------- + +You must change the device name on the :command:`nova-attach` command. The VM +might not clean up after a :command:`nova-detach` command runs. This example +shows how the :command:`nova-attach` command fails when you use the ``vdb``, +``vdc``, or ``vdd`` device names: + +.. code-block:: console + + # ls -al /dev/disk/by-path/ + total 0 + drwxr-xr-x 2 root root 200 2012-08-29 17:33 . + drwxr-xr-x 5 root root 100 2012-08-29 17:33 .. + lrwxrwxrwx 1 root root 9 2012-08-29 17:33 pci-0000:00:04.0-virtio-pci-virtio0 -> ../../vda + lrwxrwxrwx 1 root root 10 2012-08-29 17:33 pci-0000:00:04.0-virtio-pci-virtio0-part1 -> ../../vda1 + lrwxrwxrwx 1 root root 10 2012-08-29 17:33 pci-0000:00:04.0-virtio-pci-virtio0-part2 -> ../../vda2 + lrwxrwxrwx 1 root root 10 2012-08-29 17:33 pci-0000:00:04.0-virtio-pci-virtio0-part5 -> ../../vda5 + lrwxrwxrwx 1 root root 9 2012-08-29 17:33 pci-0000:00:06.0-virtio-pci-virtio2 -> ../../vdb + lrwxrwxrwx 1 root root 9 2012-08-29 17:33 pci-0000:00:08.0-virtio-pci-virtio3 -> ../../vdc + lrwxrwxrwx 1 root root 9 2012-08-29 17:33 pci-0000:00:09.0-virtio-pci-virtio4 -> ../../vdd + lrwxrwxrwx 1 root root 10 2012-08-29 17:33 pci-0000:00:09.0-virtio-pci-virtio4-part1 -> ../../vdd1 + +You might also have this problem after attaching and detaching the same volume +from the same VM with the same mount point multiple times. In this case, +restart the KVM host. + +Failed to attach volume, systool is not installed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +This warning and error occurs if you do not have the required ``sysfsutils`` +package installed on the compute node: + +.. code-block:: console + + WARNING nova.virt.libvirt.utils [req-1200f887-c82b-4e7c-a891-fac2e3735dbb\ + admin admin|req-1200f887-c82b-4e7c-a891-fac2e3735dbb admin admin] systool\ + is not installed + ERROR nova.compute.manager [req-1200f887-c82b-4e7c-a891-fac2e3735dbb admin\ + admin|req-1200f887-c82b-4e7c-a891-fac2e3735dbb admin admin] + [instance: df834b5a-8c3f-477a-be9b-47c97626555c|instance: df834b5a-8c3f-47\ + 7a-be9b-47c97626555c] + Failed to attach volume 13d5c633-903a-4764-a5a0-3336945b1db1 at /dev/vdk. + +Solution +-------- + +Install the ``sysfsutils`` package on the compute node. For example: + +.. code-block:: console + + # apt-get install sysfsutils + +Failed to connect volume in FC SAN +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +The compute node failed to connect to a volume in a Fibre Channel (FC) SAN +configuration. The WWN may not be zoned correctly in your FC SAN that links the +compute host to the storage array: + +.. code-block:: console + + ERROR nova.compute.manager [req-2ddd5297-e405-44ab-aed3-152cd2cfb8c2 admin\ + demo|req-2ddd5297-e405-44ab-aed3-152cd2cfb8c2 admin demo] [instance: 60ebd\ + 6c7-c1e3-4bf0-8ef0-f07aa4c3d5f3|instance: 60ebd6c7-c1e3-4bf0-8ef0-f07aa4c3\ + d5f3] + Failed to connect to volume 6f6a6a9c-dfcf-4c8d-b1a8-4445ff883200 while\ + attaching at /dev/vdjTRACE nova.compute.manager [instance: 60ebd6c7-c1e3-4\ + bf0-8ef0-f07aa4c3d5f3|instance: 60ebd6c7-c1e3-4bf0-8ef0-f07aa4c3d5f3] + Traceback (most recent call last):…f07aa4c3d5f3\] ClientException: The\ + server has either erred or is incapable of performing the requested\ + operation.(HTTP 500)(Request-ID: req-71e5132b-21aa-46ee-b3cc-19b5b4ab2f00) + +Solution +-------- + +The network administrator must configure the FC SAN fabric by correctly zoning +the WWN (port names) from your compute node HBAs. + +Multipath call failed exit +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +Multipath call failed exit. This warning occurs in the Compute log if you do +not have the optional ``multipath-tools`` package installed on the compute +node. This is an optional package and the volume attachment does work without +the multipath tools installed. If the ``multipath-tools`` package is installed +on the compute node, it is used to perform the volume attachment. The IDs in +your message are unique to your system. + +.. code-block:: console + + WARNING nova.storage.linuxscsi [req-cac861e3-8b29-4143-8f1b-705d0084e571 \ + admin admin|req-cac861e3-8b29-4143-8f1b-705d0084e571 admin admin] \ + Multipath call failed exit (96) + +Solution +-------- + +Install the ``multipath-tools`` package on the compute node. For example: + +.. code-block:: console + + # apt-get install multipath-tools + +Failed to Attach Volume, Missing sg_scan +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem +------- + +Failed to attach volume to an instance, ``sg_scan`` file not found. This error +occurs when the sg3-utils package is not installed on the compute node. The +IDs in your message are unique to your system: + +.. code-block:: console + + ERROR nova.compute.manager [req-cf2679fd-dd9e-4909-807f-48fe9bda3642 admin admin|req-cf2679fd-dd9e-4909-807f-48fe9bda3642 admin admin] + [instance: 7d7c92e0-49fa-4a8e-87c7-73f22a9585d5|instance: 7d7c92e0-49fa-4a8e-87c7-73f22a9585d5] + Failed to attach volume 4cc104c4-ac92-4bd6-9b95-c6686746414a at /dev/vdcTRACE nova.compute.manager + [instance: 7d7c92e0-49fa-4a8e-87c7-73f22a9585d5|instance: 7d7c92e0-49fa-4a8e-87c7-73f22a9585d5] + Stdout: '/usr/local/bin/nova-rootwrap: Executable not found: /usr/bin/sg_scan' + +Solution +-------- + +Install the ``sg3-utils`` package on the compute node. For example: + +.. code-block:: console + + # apt-get install sg3-utils diff --git a/doc/source/admin/system-admin.rst b/doc/source/admin/system-admin.rst new file mode 100644 index 00000000000..47147fc8eac --- /dev/null +++ b/doc/source/admin/system-admin.rst @@ -0,0 +1,84 @@ +.. _compute-trusted-pools.rst: + +===================== +System administration +===================== + +.. toctree:: + :maxdepth: 2 + + manage-users.rst + manage-volumes.rst + flavors.rst + default-ports.rst + admin-password-injection.rst + manage-the-cloud.rst + manage-logs.rst + root-wrap-reference.rst + configuring-migrations.rst + live-migration-usage.rst + remote-console-access.rst + service-groups.rst + security.rst + node-down.rst + adv-config.rst + +To effectively administer compute, you must understand how the different +installed nodes interact with each other. Compute can be installed in many +different ways using multiple servers, but generally multiple compute nodes +control the virtual servers and a cloud controller node contains the remaining +Compute services. + +The Compute cloud works using a series of daemon processes named ``nova-*`` +that exist persistently on the host machine. These binaries can all run on the +same machine or be spread out on multiple boxes in a large deployment. The +responsibilities of services and drivers are: + +**Services** + +``nova-api`` + Receives XML requests and sends them to the rest of the system. A WSGI app + routes and authenticates requests. Supports the EC2 and OpenStack APIs. A + ``nova.conf`` configuration file is created when Compute is installed. + +``nova-cert`` + Manages certificates. + +``nova-compute`` + Manages virtual machines. Loads a Service object, and exposes the public + methods on ComputeManager through a Remote Procedure Call (RPC). + +``nova-conductor`` + Provides database-access support for compute nodes (thereby reducing security + risks). + +``nova-consoleauth`` + Manages console authentication. + +``nova-objectstore`` + A simple file-based storage system for images that replicates most of the S3 + API. It can be replaced with OpenStack Image service and either a simple + image manager or OpenStack Object Storage as the virtual machine image + storage facility. It must exist on the same node as ``nova-compute``. + +``nova-network`` + Manages floating and fixed IPs, DHCP, bridging and VLANs. Loads a Service + object which exposes the public methods on one of the subclasses of + NetworkManager. Different networking strategies are available by changing the + ``network_manager`` configuration option to ``FlatManager``, + ``FlatDHCPManager``, or ``VLANManager`` (defaults to ``VLANManager`` if + nothing is specified). + +``nova-scheduler`` + Dispatches requests for new virtual machines to the correct node. + +``nova-novncproxy`` + Provides a VNC proxy for browsers, allowing VNC consoles to access virtual + machines. + +.. note:: + + Some services have drivers that change how the service implements its core + functionality. For example, the ``nova-compute`` service supports drivers + that let you choose which hypervisor type it can use. ``nova-network`` and + ``nova-scheduler`` also have drivers. diff --git a/doc/source/api_microversion_dev.rst b/doc/source/api_microversion_dev.rst deleted file mode 100644 index ae75e7fbc07..00000000000 --- a/doc/source/api_microversion_dev.rst +++ /dev/null @@ -1,370 +0,0 @@ -API Microversions -================= - -Background ----------- - -Nova uses a framework we call 'API Microversions' for allowing changes -to the API while preserving backward compatibility. The basic idea is -that a user has to explicitly ask for their request to be treated with -a particular version of the API. So breaking changes can be added to -the API without breaking users who don't specifically ask for it. This -is done with an HTTP header ``X-OpenStack-Nova-API-Version`` which -is a monotonically increasing semantic version number starting from -``2.1``. - -If a user makes a request without specifying a version, they will get -the ``DEFAULT_API_VERSION`` as defined in -``nova/api/openstack/wsgi.py``. This value is currently ``2.1`` and -is expected to remain so for quite a long time. - -There is a special value ``latest`` which can be specified, which will -allow a client to always receive the most recent version of API -responses from the server. - -.. warning:: The ``latest`` value is mostly meant for integration testing and - would be dangerous to rely on in client code since Nova microversions are not - following semver and therefore backward compatibility is not guaranteed. - Clients, like python-novaclient, should always require a specific - microversion but limit what is acceptable to the version range that it - understands at the time. - -For full details please read the `Kilo spec for microversions -`_ - -When do I need a new Microversion? ----------------------------------- - -A microversion is needed when the contract to the user is -changed. The user contract covers many kinds of information such as: - -- the Request - - - the list of resource urls which exist on the server - - Example: adding a new servers/{ID}/foo which didn't exist in a - previous version of the code - - - the list of query parameters that are valid on urls - - Example: adding a new parameter ``is_yellow`` servers/{ID}?is_yellow=True - - - the list of query parameter values for non free form fields - - Example: parameter filter_by takes a small set of constants/enums "A", - "B", "C". Adding support for new enum "D". - - - new headers accepted on a request - - - the list of attributes and data structures accepted. - - Example: adding a new attribute 'locked': True/False to the request body - - However, the attribute ``os.scheduler_hints`` of the "create a server" API - is an exception to this. A new scheduler which adds a new attribute - to ``os:scheduler_hints`` doesn't require a new microversion, because - available schedulers depend on cloud environments, and we accept customized - schedulers as a rule. - -- the Response - - - the list of attributes and data structures returned - - Example: adding a new attribute 'locked': True/False to the output - of servers/{ID} - - - the allowed values of non free form fields - - Example: adding a new allowed ``status`` to servers/{ID} - - - the list of status codes allowed for a particular request - - Example: an API previously could return 200, 400, 403, 404 and the - change would make the API now also be allowed to return 409. - - See [#f2]_ for the 400, 403 and 404 cases. - - - changing a status code on a particular response - - Example: changing the return code of an API from 501 to 400. - - .. note:: Fixing a bug so that a 400+ code is returned rather than a 500 or - 503 does not require a microversion change. It's assumed that clients are - not expected to handle a 500 or 503 response and therefore should not - need to opt-in to microversion changes that fixes a 500 or 503 response - from happening. - According to the OpenStack API Working Group, a - **500 Internal Server Error** should **not** be returned to the user for - failures due to user error that can be fixed by changing the request on - the client side. See [#f1]_. - - - new headers returned on a response - -The following flow chart attempts to walk through the process of "do -we need a microversion". - - -.. graphviz:: - - digraph states { - - label="Do I need a microversion?" - - silent_fail[shape="diamond", style="", group=g1, label="Did we silently - fail to do what is asked?"]; - ret_500[shape="diamond", style="", group=g1, label="Did we return a 500 - before?"]; - new_error[shape="diamond", style="", group=g1, label="Are we changing what - status code is returned?"]; - new_attr[shape="diamond", style="", group=g1, label="Did we add or remove an - attribute to a payload?"]; - new_param[shape="diamond", style="", group=g1, label="Did we add or remove - an accepted query string parameter or value?"]; - new_resource[shape="diamond", style="", group=g1, label="Did we add or remove a - resource url?"]; - - - no[shape="box", style=rounded, label="No microversion needed"]; - yes[shape="box", style=rounded, label="Yes, you need a microversion"]; - no2[shape="box", style=rounded, label="No microversion needed, it's - a bug"]; - - silent_fail -> ret_500[label=" no"]; - silent_fail -> no2[label="yes"]; - - ret_500 -> no2[label="yes [1]"]; - ret_500 -> new_error[label=" no"]; - - new_error -> new_attr[label=" no"]; - new_error -> yes[label="yes"]; - - new_attr -> new_param[label=" no"]; - new_attr -> yes[label="yes"]; - - new_param -> new_resource[label=" no"]; - new_param -> yes[label="yes"]; - - new_resource -> no[label=" no"]; - new_resource -> yes[label="yes"]; - - {rank=same; yes new_attr} - {rank=same; no2 ret_500} - {rank=min; silent_fail} - } - - -**Footnotes** - -.. [#f1] When fixing 500 errors that previously caused stack traces, try - to map the new error into the existing set of errors that API call - could previously return (400 if nothing else is appropriate). Changing - the set of allowed status codes from a request is changing the - contract, and should be part of a microversion (except in [#f2]_). - - The reason why we are so strict on contract is that we'd like - application writers to be able to know, for sure, what the contract is - at every microversion in Nova. If they do not, they will need to write - conditional code in their application to handle ambiguities. - - When in doubt, consider application authors. If it would work with no - client side changes on both Nova versions, you probably don't need a - microversion. If, on the other hand, there is any ambiguity, a - microversion is probably needed. - -.. [#f2] The exception to not needing a microversion when returning a - previously unspecified error code is the 400, 403 and 404 cases. This is - considered OK to return even if previously unspecified in the code since - it's implied given keystone authentication can fail with a 403 and API - validation can fail with a 400 for invalid json request body. Request to - url/resource that does not exist always fails with 404. - -When a microversion is not needed ---------------------------------- - -A microversion is not needed in the following situation: - -- the response - - - Changing the error message without changing the response code - does not require a new microversion. - - - Removing an inapplicable HTTP header, for example, suppose the Retry-After - HTTP header is being returned with a 4xx code. This header should only be - returned with a 503 or 3xx response, so it may be removed without bumping - the microversion. - -In Code -------- - -In ``nova/api/openstack/wsgi.py`` we define an ``@api_version`` decorator -which is intended to be used on top-level Controller methods. It is -not appropriate for lower-level methods. Some examples: - -Adding a new API method -~~~~~~~~~~~~~~~~~~~~~~~ - -In the controller class:: - - @wsgi.Controller.api_version("2.4") - def my_api_method(self, req, id): - .... - -This method would only be available if the caller had specified an -``X-OpenStack-Nova-API-Version`` of >= ``2.4``. If they had specified a -lower version (or not specified it and received the default of ``2.1``) -the server would respond with ``HTTP/404``. - -Removing an API method -~~~~~~~~~~~~~~~~~~~~~~ - -In the controller class:: - - @wsgi.Controller.api_version("2.1", "2.4") - def my_api_method(self, req, id): - .... - -This method would only be available if the caller had specified an -``X-OpenStack-Nova-API-Version`` of <= ``2.4``. If ``2.5`` or later -is specified the server will respond with ``HTTP/404``. - -Changing a method's behavior -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In the controller class:: - - @wsgi.Controller.api_version("2.1", "2.3") - def my_api_method(self, req, id): - .... method_1 ... - - @wsgi.Controller.api_version("2.4") # noqa - def my_api_method(self, req, id): - .... method_2 ... - -If a caller specified ``2.1``, ``2.2`` or ``2.3`` (or received the -default of ``2.1``) they would see the result from ``method_1``, -``2.4`` or later ``method_2``. - -It is vital that the two methods have the same name, so the second of -them will need ``# noqa`` to avoid failing flake8's ``F811`` rule. The -two methods may be different in any kind of semantics (schema -validation, return values, response codes, etc) - -A change in schema only -~~~~~~~~~~~~~~~~~~~~~~~ - -If there is no change to the method, only to the schema that is used for -validation, you can add a version range to the ``validation.schema`` -decorator:: - - @wsgi.Controller.api_version("2.1") - @validation.schema(dummy_schema.dummy, "2.3", "2.8") - @validation.schema(dummy_schema.dummy2, "2.9") - def update(self, req, id, body): - .... - -This method will be available from version ``2.1``, validated according to -``dummy_schema.dummy`` from ``2.3`` to ``2.8``, and validated according to -``dummy_schema.dummy2`` from ``2.9`` onward. - - -When not using decorators -~~~~~~~~~~~~~~~~~~~~~~~~~ - -When you don't want to use the ``@api_version`` decorator on a method -or you want to change behavior within a method (say it leads to -simpler or simply a lot less code) you can directly test for the -requested version with a method as long as you have access to the api -request object (commonly called ``req``). Every API method has an -api_version_request object attached to the req object and that can be -used to modify behavior based on its value:: - - def index(self, req): - - - req_version = req.api_version_request - req1_min = api_version_request.APIVersionRequest("2.1") - req1_max = api_version_request.APIVersionRequest("2.5") - req2_min = api_version_request.APIVersionRequest("2.6") - req2_max = api_version_request.APIVersionRequest("2.10") - - if req_version.matches(req1_min, req1_max): - ....stuff.... - elif req_version.matches(req2min, req2_max): - ....other stuff.... - elif req_version > api_version_request.APIVersionRequest("2.10"): - ....more stuff..... - - - -The first argument to the matches method is the minimum acceptable version -and the second is maximum acceptable version. A specified version can be null:: - - null_version = APIVersionRequest() - -If the minimum version specified is null then there is no restriction on -the minimum version, and likewise if the maximum version is null there -is no restriction the maximum version. Alternatively a one sided comparison -can be used as in the example above. - -Other necessary changes ------------------------ - -If you are adding a patch which adds a new microversion, it is -necessary to add changes to other places which describe your change: - -* Update ``REST_API_VERSION_HISTORY`` in - ``nova/api/openstack/api_version_request.py`` - -* Update ``_MAX_API_VERSION`` in - ``nova/api/openstack/api_version_request.py`` - -* Add a verbose description to - ``nova/api/openstack/rest_api_version_history.rst``. There should - be enough information that it could be used by the docs team for - release notes. - -* Update the expected versions in affected tests, for example in - ``nova/tests/unit/api/openstack/compute/test_versions.py``. - -* Update the get versions api sample files: - ``doc/api_samples/versions/versions-get-resp.json`` and - ``nova/tests/functional/api_samples/versions/versions-get-resp.json.tpl``. - -* Make a new commit to python-novaclient and update corresponding - files to enable the newly added microversion API. - -Allocating a microversion -------------------------- - -If you are adding a patch which adds a new microversion, it is -necessary to allocate the next microversion number. Except under -extremely unusual circumstances and this would have been mentioned in -the nova spec for the change, the minor number of ``_MAX_API_VERSION`` -will be incremented. This will also be the new microversion number for -the API change. - -It is possible that multiple microversion patches would be proposed in -parallel and the microversions would conflict between patches. This -will cause a merge conflict. We don't reserve a microversion for each -patch in advance as we don't know the final merge order. Developers -may need over time to rebase their patch calculating a new version -number as above based on the updated value of ``_MAX_API_VERSION``. - -Testing Microversioned API Methods ----------------------------------- - -Testing a microversioned API method is very similar to a normal controller -method test, you just need to add the ``X-OpenStack-Nova-API-Version`` -header, for example:: - - req = fakes.HTTPRequest.blank('/testable/url/endpoint') - req.headers = {'X-OpenStack-Nova-API-Version': '2.2'} - req.api_version_request = api_version.APIVersionRequest('2.6') - - controller = controller.TestableController() - - res = controller.index(req) - ... assertions about the response ... - -For many examples of testing, the canonical examples are in -``nova/tests/unit/api/openstack/compute/test_microversions.py``. diff --git a/doc/source/api_microversion_history.rst b/doc/source/api_microversion_history.rst deleted file mode 100644 index e6e13b21e93..00000000000 --- a/doc/source/api_microversion_history.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../nova/api/openstack/rest_api_version_history.rst diff --git a/doc/source/api_plugins.rst b/doc/source/api_plugins.rst deleted file mode 100644 index 33ff899f6c2..00000000000 --- a/doc/source/api_plugins.rst +++ /dev/null @@ -1,195 +0,0 @@ -API Plugins -=========== - -Background ----------- - -Nova has two API plugin frameworks, one for the original V2 API and -one for what we call V2.1 which also supports V2.1 microversions. The -V2.1 API acts from a REST API user point of view in an identical way -to the original V2 API. V2.1 is implemented in the same framework as -microversions, with the version requested being 2.1. - -The V2 API is now frozen and with the exception of significant bugs no -change should be made to the V2 API code. API changes should only be -made through V2.1 microversions. - -This document covers how to write plugins for the v2.1 framework. A -`microversions specific document -`_ -covers the details around what is required for the microversions -part. It does not cover V2 plugins which should no longer be developed. - -There may still be references to a v3 API both in comments and in the -directory path of relevant files. This is because v2.1 first started -out being called v3 rather than v2.1. Where you see references to v3 -you can treat it as a reference to v2.1 with or without microversions -support. - -The original V2 API plugins live in ``nova/api/openstack/compute/legacy_v2`` -and the V2.1 plugins live in ``nova/api/openstack/compute``. - -Note that any change to the Nova API to be merged will first require a -spec be approved first. See `here `_ -for the appropriate repository. For guidance on the design of the API -please refer to the `OpenStack API WG -`_ - - -Basic plugin structure ----------------------- - -A very basic skeleton of a v2.1 plugin can be seen `here in the unittests `_. An annotated version below:: - - """Basic Test Extension""" - - from nova.api.openstack import extensions - from nova.api.openstack import wsgi - - - ALIAS = 'test-basic' - # ALIAS needs to be unique and should be of the format - # ^[a-z]+[a-z\-]*[a-z]$ - - class BasicController(wsgi.Controller): - - # Define support for GET on a collection - def index(self, req): - data = {'param': 'val'} - return data - - # Defining a method implements the following API responses: - # delete -> DELETE - # update -> PUT - # create -> POST - # show -> GET - # If a method is not definied a request to it will be a 404 response - - # It is also possible to define support for further responses - # See `servers.py `_. - - - class Basic(extensions.V3APIExtensionBase): - """Basic Test Extension.""" - - name = "BasicTest" - alias = ALIAS - version = 1 - - # Both get_resources and get_controller_extensions must always - # be definied by can return an empty array - def get_resources(self): - resource = extensions.ResourceExtension('test', BasicController()) - return [resource] - - def get_controller_extensions(self): - return [] - -All of these plugin files should live in the ``nova/api/openstack/compute`` directory. - - -Policy -~~~~~~ - -Policy (permission) is defined ``etc/nova/policy.json``. Implementation of policy -is changing a bit at the moment. Will add more to this document or reference -another one in the future. Note that a 'discoverable' policy needs to be added -for each plugin that you wish to appear in the ``/extension`` output. Also -look at the authorize call in plugins currently merged. - -Modularity -~~~~~~~~~~ - -The Nova REST API is separated into different plugins in the directory -'nova/api/openstack/compute/' - -Because microversions are supported in the Nova REST API, the API can be -extended without any new plugin. But for code readability, the Nova REST API -code still needs modularity. Here are rules for how to separate modules: - -* You are adding a new resource - The new resource should be in standalone module. There isn't any reason to - put different resources in a single module. - -* Add sub-resource for existing resource - To prevent an existing resource module becoming over-inflated, the - sub-resource should be implemented in a separate module. - -* Add extended attributes for existing resource - In normally, the extended attributes is part of existing resource's data - model too. So this can be added into existing resource module directly and - lightly. - To avoid namespace complexity, we should avoid to add extended attributes - in existing extended models. New extended attributes needn't any namespace - prefix anymore. - -JSON-Schema -~~~~~~~~~~~ - -The v2.1 API validates a REST request body with JSON-Schema library. -Valid body formats are defined with JSON-Schema in the directory -'nova/api/openstack/compute/schemas'. Each definition is used at the -corresponding method with the ``validation.schema`` decorator like:: - - @validation.schema(schema.update_something) - def update(self, req, id, body): - .... - -Nova supports the extension of JSON-Schema definitions based on the -loaded API extensions for some APIs. Stevedore library tries to find -specific name methods which return additional parameters and extends -them to the original JSON-Schema definitions. -The following are the combinations of extensible API and method name -which returns additional parameters: - -* Create a server API - get_server_create_schema() -* Update a server API - get_server_update_schema() -* Rebuild a server API - get_server_rebuild_schema() -* Resize a server API - get_server_resize_schema() - -For example, keypairs extension(Keypairs class) contains the method -get_server_create_schema() which returns:: - - { - 'key_name': parameter_types.name, - } - -then the parameter key_name is allowed on Create a server API. - -Support files -------------- - -At least one entry needs to made in ``setup.cfg`` for each plugin. -An entry point for the plugin must be added to nova.api.v21.extensions -even if no resource or controller is added. Other entry points available -are - -* Modify create behaviour (nova.api.v21.extensions.server.create) -* Modify rebuild behaviour (nova.api.v21.extensions.server.rebuild) -* Modify update behaviour (nova.api.v21.extensions.server.update) -* Modify resize behaviour (nova.api.v21.extensions.server.resize) - -These are essentially hooks into the servers plugin which allow other -plugins to modify behaviour without having to modify servers.py. In -the past not having this capability led to very large chunks of -unrelated code being added to servers.py which was difficult to -maintain. - - -Unit Tests ----------- - -Should write something more here. But you need to have -both unit and functional tests. - - -Functional tests and API Samples --------------------------------- - -Should write something here - -Commit message tags -------------------- - -Please ensure you add the ``DocImpact`` tag along with a short -description for any API change. diff --git a/doc/source/block_device_mapping.rst b/doc/source/block_device_mapping.rst deleted file mode 100644 index 4b849f8944c..00000000000 --- a/doc/source/block_device_mapping.rst +++ /dev/null @@ -1,204 +0,0 @@ -.. - 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. - -Block Device Mapping in Nova -============================ - -Nova has a concept of block devices that can be exposed to cloud instances. -There are several types of block devices an instance can have (we will go into -more details about this later in this document), and which ones are available -depends on a particular deployment and the usage limitations set for tenants -and users. Block device mapping is a way to organize and keep data about all of -the block devices an instance has. - -When we talk about block device mapping, we usually refer to one of two things - -1. API/CLI structure and syntax for specifying block devices for an instance - boot request - -2. The data structure internal to Nova that is used for recording and keeping, - which is ultimately persisted in the block_device_mapping table. However, - Nova internally has several "slightly" different formats for representing - the same data. All of them are documented in the code and or presented by - a distinct set of classes, but not knowing that they exist might trip up - people reading the code. So in addition to BlockDeviceMapping [1]_ objects - that mirror the database schema, we have: - - 2.1 The API format - this is the set of raw key-value pairs received from - the API client, and is almost immediately transformed into the object; - however, some validations are done using this format. We will refer to this - format as the 'API BDMs' from now on. - - 2.2 The virt driver format - this is the format defined by the classes in - :mod: `nova.virt.block_device`. This format is used and expected by the code - in the various virt drivers. These classes, in addition to exposing a - different format (mimicking the Python dict interface), also provide a place - to bundle some functionality common to certain types of block devices (for - example attaching volumes which has to interact with both Cinder and the - virt driver code). We will refer to this format as 'Driver BDMs' from now - on. - - -Data format and its history ----------------------------- - -In the early days of Nova, block device mapping general structure closely -mirrored that of the EC2 API. During the Havana release of Nova, block device -handling code, and in turn the block device mapping structure, had work done on -improving the generality and usefulness. These improvements included exposing -additional details and features in the API. In order to facilitate this, a new -extension was added to the v2 API called `BlockDeviceMappingV2Boot` [2]_, that -added an additional `block_device_mapping_v2` field to the instance boot API -request. - -Block device mapping v1 (aka legacy) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This was the original format that supported only cinder volumes (similar to how -EC2 block devices support only EBS volumes). Every entry was keyed by device -name (we will discuss why this was problematic in its own section later on -this page), and would accept only: - -* UUID of the Cinder volume or snapshot -* Type field - used only to distinguish between volumes and Cinder volume - snapshots -* Optional size field -* Optional `delete_on_termination` flag - -While all of Nova internal code only uses and stores the new data structure, we -still need to handle API requests that use the legacy format. This is handled -by the Nova API service on every request. As we will see later, since block -device mapping information can also be stored in the image metadata in Glance, -this is another place where we need to handle the v1 format. The code to handle -legacy conversions is part of the :mod: `nova.block_device` module. - -Intermezzo - problem with device names -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Using device names as the primary per-instance identifier, and exposing them in -the API, is problematic for Nova mostly because several hypervisors Nova -supports with its drivers can't guarantee that the device names the guest OS -assigns are the ones the user requested from Nova. Exposing such a detail -in the public API of Nova is obviously not ideal, but it needed to stay for -backwards compatibility. It is also required for some (slightly obscure) -features around overloading a block device in a Glance image when booting an -instance [3]. - -The plan for fixing this was to allow users to not specify the device name of a -block device, and Nova will determine it (with the help of the virt driver), so -that it can still be discovered through the API and used when necessary, like -for the features mentioned above (and preferably only then). - -Another use for specifying the device name was to allow the "boot from volume" -functionality, by specifying a device name that matches the root device name -for the instance (usually `/dev/vda`). - -Currently (mid Liberty) users are discouraged from specifying device names -for all calls requiring or allowing block device mapping, except when trying to -override the image block device mapping on instance boot, and it will likely -remain like that in the future. Libvirt device driver will outright override -any device names passed with it's own values. - -Block device mapping v2 -^^^^^^^^^^^^^^^^^^^^^^^ - -New format was introduced in an attempt to solve issues with the original -block device mapping format discussed above, and also to allow for more -flexibility and addition of features that were not possible with the simple -format we had. - -New block device mapping is a list of dictionaries containing the following -fields (in addition to the ones that were already there): - -* source_type - this can have one of the following values: - - * `image` - * `volume` - * `snapshot` - * `blank` - -* dest_type - this can have one of the following values: - - * `local` - * `volume` - -Combination of the above two fields would define what kind of block device the -entry is referring to. We currently support the following combinations: - - * `image` -> `local` - this is only currently reserved for the entry - referring to the Glance image that the instance is being booted with (it - should also be marked as a boot device). It is also worth noting that an - API request that specifies this, also has to provide the same Glance uuid - as the `image_ref` parameter to the boot request (this is done for - backwards compatibility and may be changed in the future). This - functionality might be extended to specify additional Glance images - to be attached to an instance after boot (similar to kernel/ramdisk - images) but this functionality is not supported by any of the current - drivers. - * `volume` -> `volume` - this is just a Cinder volume to be attached to the - instance. It can be marked as a boot device. - * `snapshot` -> `volume` - this works exactly as passing `type=snap` does. - It would create a volume from a Cinder volume snapshot and attach that - volume to the instance. Can be marked bootable. - * `image` -> `volume` - As one would imagine, this would download a Glance - image to a cinder volume and attach it to an instance. Can also be marked - as bootable. This is really only a shortcut for creating a volume out of - an image before booting an instance with the newly created volume. - * `blank` -> `volume` - Creates a blank Cinder volume and attaches it. This - will also require the volume size to be set. - * `blank` -> `local` - Depending on the guest_format field (see below), - this will either mean an ephemeral blank disk on hypervisor local - storage, or a swap disk (instances can have only one of those). - -* guest_format - Tells Nova how/if to format the device prior to attaching, - should be only used with blank local images. Denotes a swap disk if the value - is `swap`. - -* device_name - See the previous section for a more in depth explanation of - this - currently best left empty (not specified that is), unless the user - wants to override the existing device specified in the image metadata. - In case of Libvirt, even when passed in with the purpose of overriding the - existing image metadata, final set of device names for the instance may still - get changed by the driver. - -* disk_bus and device_type - low level details that some hypervisors (currently - only libvirt) may support. Some example disk_bus values can be: `ide`, `usb`, - `virtio`, `scsi`, while device_type may be `disk`, `cdrom`, `floppy`, `lun`. - This is not an exhaustive list as it depends on the virtualization driver, - and may change as more support is added. Leaving these empty is the most - common thing to do. - -* boot_index - Defines the order in which a hypervisor will try devices when - attempting to boot the guest from storage. Each device which is capable of - being used as boot device should be given a unique boot index, starting from - 0 in ascending order. Some hypervisors may not support booting from multiple - devices, so will only consider the device with boot index of 0. Some - hypervisors will support booting from multiple devices, but only if they are - of different types - eg a disk and CD-ROM. Setting a negative value or None - indicates that the device should not be used for booting. The simplest - usage is to set it to 0 for the boot device and leave it as None for any - other devices. - - -Nova will not allow mixing of two formats in a single request, and will do -basic validation to make sure that the requested block device mapping is valid -before accepting a boot request. - -.. [1] In addition to the BlockDeviceMapping Nova object, we also have the - BlockDeviceDict class in :mod: `nova.block_device` module. This class - handles transforming and validating the API BDM format. -.. [2] This work predates API microversions and thus the only way to add it was - by means of an API extension. -.. [3] This is a feature that the EC2 API offers as well and has been in Nova - for a long time, although it has been broken in several releases. More info - can be found on `this bug ` diff --git a/doc/source/cells.rst b/doc/source/cells.rst deleted file mode 100644 index c3039d2f3a5..00000000000 --- a/doc/source/cells.rst +++ /dev/null @@ -1,206 +0,0 @@ -.. - 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. - -======= - Cells -======= - - -Cells V1 -======== - -Historically, Nova has depended on a single logical database and message queue -that all nodes depend on for communication and data persistence. This becomes -an issue for deployers as scaling and providing fault tolerance for these -systems is difficult. - -We have an experimental feature in Nova called "cells", hereafter referred to -as "cells v1", which is used by some large deployments to partition compute -nodes into smaller groups, coupled with a database and queue. This seems to be -a well-liked and easy-to-understand arrangement of resources, but the -implementation of it has issues for maintenance and correctness. -See `Comparison with Cells V1`_ for more detail. - -Status -~~~~~~ - -Cells v1 is considered experimental and receives much less testing than the -rest of Nova. For example, there is no job for testing cells v1 with Neutron. - -The priority for the core team is implementation of and migration to cells v2. -Because of this, there are a few restrictions placed on cells v1: - -#. Cells v1 is in feature freeze. This means no new feature proposals for cells - v1 will be accepted by the core team, which includes but is not limited to - API parity, e.g. supporting virtual interface attach/detach with Neutron. -#. Latent bugs caused by the cells v1 design will not be fixed, e.g. - `bug 1489581 `_. So if new - tests are added to Tempest which trigger a latent bug in cells v1 it may not - be fixed. However, regressions in working function should be tracked with - bugs and fixed. - -**Suffice it to say, new deployments of cells v1 are not encouraged.** - -The restrictions above are basically meant to prioritize effort and focus on -getting cells v2 completed, and feature requests and hard to fix latent bugs -detract from that effort. Further discussion on this can be found in the -`2015/11/12 Nova meeting minutes -`_. - -There are no plans to remove Cells V1 until V2 is usable by existing -deployments and there is a migration path. - - -Cells V2 -======== - -Manifesto -~~~~~~~~~ - -Proposal --------- - -Right now, when a request hits the Nova API for a particular instance, the -instance information is fetched from the database, which contains the hostname -of the compute node on which the instance currently lives. If the request needs -to take action on the instance (which is most of them), the hostname is used to -calculate the name of a queue, and a message is written there which finds its -way to the proper compute node. - -The meat of this proposal is changing the above hostname lookup into two parts -that yield three pieces of information instead of one. Basically, instead of -merely looking up the *name* of the compute node on which an instance lives, we -will also obtain database and queue connection information. Thus, when asked to -take action on instance $foo, we will: - -1. Lookup the three-tuple of (database, queue, hostname) for that instance -2. Connect to that database and fetch the instance record -3. Connect to the queue and send the message to the proper hostname queue - -The above differs from the current organization in two ways. First, we need to -do two database lookups before we know where the instance lives. Second, we -need to demand-connect to the appropriate database and queue. Both of these -have performance implications, but we believe we can mitigate the impacts -through the use of things like a memcache of instance mapping information and -pooling of connections to database and queue systems. The number of cells will -always be much smaller than the number of instances. - -There are availability implications with this change since something like a -'nova list' which might query multiple cells could end up with a partial result -if there is a database failure in a cell. A database failure within a cell -would cause larger issues than a partial list result so the expectation is that -it would be addressed quickly and cellsv2 will handle it by indicating in the -response that the data may not be complete. - -Since this is very similar to what we have with current cells, in terms of -organization of resources, we have decided to call this "cellsv2" for -disambiguation. - -After this work is complete there will no longer be a "no cells" deployment. -The default installation of Nova will be a single cell setup. - -Benefits --------- - -The benefits of this new organization are: - -* Native sharding of the database and queue as a first-class-feature in nova. - All of the code paths will go through the lookup procedure and thus we won't - have the same feature parity issues as we do with current cells. - -* No high-level replication of all the cell databases at the top. The API will - need a database of its own for things like the instance index, but it will - not need to replicate all the data at the top level. - -* It draws a clear line between global and local data elements. Things like - flavors and keypairs are clearly global concepts that need only live at the - top level. Providing this separation allows compute nodes to become even more - stateless and insulated from things like deleted/changed global data. - -* Existing non-cells users will suddenly gain the ability to spawn a new "cell" - from their existing deployment without changing their architecture. Simply - adding information about the new database and queue systems to the new index - will allow them to consume those resources. - -* Existing cells users will need to fill out the cells mapping index, shutdown - their existing cells synchronization service, and ultimately clean up their - top level database. However, since the high-level organization is not - substantially different, they will not have to re-architect their systems to - move to cellsv2. - -* Adding new sets of hosts as a new "cell" allows them to be plugged into a - deployment and tested before allowing builds to be scheduled to them. - -Comparison with Cells V1 ------------------------- - -In reality, the proposed organization is nearly the same as what we currently -have in cells today. A cell mostly consists of a database, queue, and set of -compute nodes. The primary difference is that current cells require a -nova-cells service that synchronizes information up and down from the top level -to the child cell. Additionally, there are alternate code paths in -compute/api.py which handle routing messages to cells instead of directly down -to a compute host. Both of these differences are relevant to why we have a hard -time achieving feature and test parity with regular nova (because many things -take an alternate path with cells) and why it's hard to understand what is -going on (all the extra synchronization of data). The new proposed cellsv2 -organization avoids both of these problems by letting things live where they -should, teaching nova to natively find the right db, queue, and compute node to -handle a given request. - - -Database split -~~~~~~~~~~~~~~ - -As mentioned above there is a split between global data and data that is local -to a cell. - -The following is a breakdown of what data can uncontroversially considered -global versus local to a cell. Missing data will be filled in as consensus is -reached on the data that is more difficult to cleanly place. The missing data -is mostly concerned with scheduling and networking. - -Global (API-level) Tables -------------------------- - -instance_types -instance_type_projects -instance_type_extra_specs -quotas -project_user_quotas -quota_classes -quota_usages -security_groups -security_group_rules -security_group_default_rules -provider_fw_rules -key_pairs -migrations -networks -tags - -Cell-level Tables ------------------ - -instances -instance_info_caches -instance_extra -instance_metadata -instance_system_metadata -instance_faults -instance_actions -instance_actions_events -instance_id_mappings -pci_devices -block_device_mapping -virtual_interfaces diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst new file mode 100644 index 00000000000..467cbe0049d --- /dev/null +++ b/doc/source/cli/index.rst @@ -0,0 +1,94 @@ +.. + Copyright 2010-2011 United States Government as represented by the + Administrator of the National Aeronautics and Space Administration. + 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. + +Command-line Utilities +====================== + +In this section you will find information on Nova's command line utilities. + +Nova Management Commands +------------------------ + +These commands are used to manage existing installations. They are designed to +be run by operators in an environment where they have direct access to the nova +database. + +.. toctree:: + :maxdepth: 1 + + nova-manage + nova-status + +Service Daemons +--------------- + +The service daemons make up a functioning nova environment. All of these are +expected to be started by an init system, expect to read a nova.conf file, and +daemonize correctly after starting up. + +.. toctree:: + :maxdepth: 1 + + nova-api + nova-compute + nova-conductor + nova-console + nova-consoleauth + nova-novncproxy + nova-scheduler + nova-serialproxy + nova-spicehtml5proxy + nova-xvpvncproxy + +WSGI Services +------------- + +Starting in the Pike release, the prefered way to deploy the nova api is in a +wsgi container (uwsgi or apache/mod_wsgi). These are the wsgi entry points to +do that. + +.. toctree:: + :maxdepth: 1 + + nova-api-metadata + nova-api-os-compute + +Additional Tools +---------------- + +There are a few additional cli tools which nova services call when +appropriate. This should not need to be called directly by operators, but they +are documented for completeness and debugging if something goes wrong. + +.. toctree:: + :maxdepth: 1 + + nova-rootwrap + nova-idmapshift + +Deprecated Services +------------------- + +The following services are deprecated in nova. They should not be used in new +deployments, but are documented for existing ones. + +.. toctree:: + :maxdepth: 1 + + nova-cells + nova-dhcpbridge + nova-network diff --git a/doc/source/man/nova-api-metadata.rst b/doc/source/cli/nova-api-metadata.rst similarity index 84% rename from doc/source/man/nova-api-metadata.rst rename to doc/source/cli/nova-api-metadata.rst index 738dae4ecc6..d55eb05fce5 100644 --- a/doc/source/man/nova-api-metadata.rst +++ b/doc/source/cli/nova-api-metadata.rst @@ -40,7 +40,8 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ +* `Using WSGI with Nova `__ BUGS ==== diff --git a/doc/source/man/nova-api-os-compute.rst b/doc/source/cli/nova-api-os-compute.rst similarity index 84% rename from doc/source/man/nova-api-os-compute.rst rename to doc/source/cli/nova-api-os-compute.rst index b9a514c3993..4d687a53e1a 100644 --- a/doc/source/man/nova-api-os-compute.rst +++ b/doc/source/cli/nova-api-os-compute.rst @@ -40,7 +40,8 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ +* `Using WSGI with Nova `__ BUGS ==== diff --git a/doc/source/cli/nova-api.rst b/doc/source/cli/nova-api.rst new file mode 100644 index 00000000000..8589044afee --- /dev/null +++ b/doc/source/cli/nova-api.rst @@ -0,0 +1,50 @@ +======== +nova-api +======== + +------------------------------------------- +Server for the Nova EC2 and OpenStack APIs +------------------------------------------- + +:Author: openstack@lists.openstack.org +:Date: 2012-09-27 +:Copyright: OpenStack Foundation +:Version: 2012.1 +:Manual section: 1 +:Manual group: cloud computing + +SYNOPSIS +======== + + nova-api [options] + +DESCRIPTION +=========== + +nova-api is a server daemon that serves the metadata and compute APIs in +separate greenthreads + +OPTIONS +======= + + **General options** + +FILES +======== + +* /etc/nova/nova.conf +* /etc/nova/api-paste.ini +* /etc/nova/policy.json +* /etc/nova/rootwrap.conf +* /etc/nova/rootwrap.d/ + +SEE ALSO +======== + +* `OpenStack Nova `__ +* `Using WSGI with Nova `__ + +BUGS +==== + +* Nova bugs are managed at Launchpad `Bugs : Nova `__ diff --git a/doc/source/cli/nova-cells.rst b/doc/source/cli/nova-cells.rst new file mode 100644 index 00000000000..2b5fc92fd99 --- /dev/null +++ b/doc/source/cli/nova-cells.rst @@ -0,0 +1,55 @@ +========== +nova-cells +========== + +-------------------------------- +Server for the Nova Cells +-------------------------------- + +:Author: openstack@lists.openstack.org +:Date: 2012-09-27 +:Copyright: OpenStack Foundation +:Version: 2012.1 +:Manual section: 1 +:Manual group: cloud computing + +SYNOPSIS +======== + + nova-cells [options] + +DESCRIPTION +=========== + +Starts the nova-cells service. + +The nova-cells service handles communication between cells and selects cells +for new instances. + +.. warning:: Everything in this document is referring to Cells v1, which is + not recommended for new deployments and is deprecated in favor of Cells v2 + as of the 16.0.0 Pike release. For information about commands to use + with Cells v2, see the man page for :ref:`man-page-cells-v2`. + +OPTIONS +======= + + **General options** + +FILES +======== + +* /etc/nova/nova.conf +* /etc/nova/policy.json +* /etc/nova/rootwrap.conf +* /etc/nova/rootwrap.d/ + +SEE ALSO +======== + +* `OpenStack Nova `__ + +BUGS +==== + +* Nova bugs are managed at Launchpad `Bugs : Nova `__ diff --git a/doc/source/man/nova-compute.rst b/doc/source/cli/nova-compute.rst similarity index 93% rename from doc/source/man/nova-compute.rst rename to doc/source/cli/nova-compute.rst index afc50e1ebdb..5f7deef37db 100644 --- a/doc/source/man/nova-compute.rst +++ b/doc/source/cli/nova-compute.rst @@ -42,7 +42,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/man/nova-conductor.rst b/doc/source/cli/nova-conductor.rst similarity index 91% rename from doc/source/man/nova-conductor.rst rename to doc/source/cli/nova-conductor.rst index 8aef93d88b4..1418d8ee749 100644 --- a/doc/source/man/nova-conductor.rst +++ b/doc/source/cli/nova-conductor.rst @@ -36,7 +36,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/man/nova-console.rst b/doc/source/cli/nova-console.rst similarity index 91% rename from doc/source/man/nova-console.rst rename to doc/source/cli/nova-console.rst index eac8ecd8db3..3b809477299 100644 --- a/doc/source/man/nova-console.rst +++ b/doc/source/cli/nova-console.rst @@ -39,7 +39,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/man/nova-consoleauth.rst b/doc/source/cli/nova-consoleauth.rst similarity index 91% rename from doc/source/man/nova-consoleauth.rst rename to doc/source/cli/nova-consoleauth.rst index acb07849304..33d273f3c79 100644 --- a/doc/source/man/nova-consoleauth.rst +++ b/doc/source/cli/nova-consoleauth.rst @@ -39,7 +39,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/man/nova-dhcpbridge.rst b/doc/source/cli/nova-dhcpbridge.rst similarity index 93% rename from doc/source/man/nova-dhcpbridge.rst rename to doc/source/cli/nova-dhcpbridge.rst index 582f6739ac8..504c7c6f20e 100644 --- a/doc/source/man/nova-dhcpbridge.rst +++ b/doc/source/cli/nova-dhcpbridge.rst @@ -41,7 +41,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/man/nova-idmapshift.rst b/doc/source/cli/nova-idmapshift.rst similarity index 97% rename from doc/source/man/nova-idmapshift.rst rename to doc/source/cli/nova-idmapshift.rst index 1c66f23d6f8..1b50ecd0c88 100644 --- a/doc/source/man/nova-idmapshift.rst +++ b/doc/source/cli/nova-idmapshift.rst @@ -84,7 +84,7 @@ Optional arguments SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/cli/nova-manage.rst b/doc/source/cli/nova-manage.rst new file mode 100644 index 00000000000..c3628af26b0 --- /dev/null +++ b/doc/source/cli/nova-manage.rst @@ -0,0 +1,321 @@ +=========== +nova-manage +=========== + +------------------------------------------- +control and manage cloud computer instances +------------------------------------------- + +:Author: openstack@lists.openstack.org +:Date: 2017-01-15 +:Copyright: OpenStack Foundation +:Version: 15.0.0 +:Manual section: 1 +:Manual group: cloud computing + +SYNOPSIS +======== + + nova-manage [] + +DESCRIPTION +=========== + +nova-manage controls cloud computing instances by managing shell selection, vpn connections, and floating IP address configuration. More information about OpenStack Nova is at https://docs.openstack.org/developer/nova. + +OPTIONS +======= + +The standard pattern for executing a nova-manage command is: +``nova-manage []`` + +Run without arguments to see a list of available command categories: +``nova-manage`` + +You can also run with a category argument such as user to see a list of all commands in that category: +``nova-manage db`` + +These sections describe the available categories and arguments for nova-manage. + +Nova Database +~~~~~~~~~~~~~ + +``nova-manage db version`` + + Print the current main database version. + +``nova-manage db sync [--version ] [--local_cell]`` + + Upgrade the main database schema up to the most recent version or + ``--version`` if specified. By default, this command will also attempt to + upgrade the schema for the cell0 database if it is mapped (see the + ``map_cell0`` or ``simple_cell_setup`` commands for more details on mapping + the cell0 database). If ``--local_cell`` is specified, then only the main + database in the current cell is upgraded. The local database connection is + determined by ``[database]/connection`` in the configuration file passed to + nova-manage. + +``nova-manage db archive_deleted_rows [--max_rows ] [--verbose] [--until-complete]`` + + Move deleted rows from production tables to shadow tables. Specifying + --verbose will print the results of the archive operation for any tables + that were changed. Specifying --until-complete will make the command run + continuously until all deleted rows are archived. Use the --max_rows option, + which defaults to 1000, as a batch size for each iteration. + +``nova-manage db null_instance_uuid_scan [--delete]`` + + Lists and optionally deletes database records where instance_uuid is NULL. + +``nova-manage db ironic_flavor_migration [--all] [--host] [--node] [--resource_class]`` + + Perform the ironic flavor migration process against the database + while services are offline. This is `not recommended` for most + people. The ironic compute driver will do this online and as + necessary if run normally. This routine is provided only for + advanced users that may be skipping the 16.0.0 Pike release, never + able to run services normally at the Pike level. Since this utility + is for use when all services (including ironic) are down, you must + pass the resource class set on your node(s) with the + ``--resource_class`` parameter. + + To migrate a specific host and node, provide the hostname and node uuid with + ``--host $hostname --node $uuid``. To migrate all instances on nodes managed + by a single host, provide only ``--host``. To iterate over all nodes in the + system in a single pass, use ``--all``. Note that this process is not lightweight, + so it should not be run frequently without cause, although it is not harmful + to do so. If you have multiple cellsv2 cells, you should run this once per cell + with the corresponding cell config for each (i.e. this does not iterate cells + automatically). + + Note that this is not recommended unless you need to run this + specific data migration offline, and it should be used with care as + the work done is non-trivial. Running smaller and more targeted batches (such as + specific nodes) is recommended. + +Nova API Database +~~~~~~~~~~~~~~~~~ + +``nova-manage api_db version`` + + Print the current cells api database version. + +``nova-manage api_db sync`` + + Sync the api cells database up to the most recent version. This is the standard way to create the db as well. + +.. _man-page-cells-v2: + +Nova Cells v2 +~~~~~~~~~~~~~ + +``nova-manage cell_v2 simple_cell_setup [--transport-url ]`` + + Setup a fresh cells v2 environment; this should not be used if you + currently have a cells v1 environment. Returns 0 if setup is completed + (or has already been done), 1 if no hosts are reporting (and cannot be + mapped), 1 if no transport url is provided for the cell message queue, + and 2 if run in a cells v1 environment. + +``nova-manage cell_v2 map_cell0 [--database_connection ]`` + + Create a cell mapping to the database connection for the cell0 database. + If a database_connection is not specified, it will use the one defined by + ``[database]/connection`` in the configuration file passed to nova-manage. + The cell0 database is used for instances that have not been scheduled to + any cell. This generally applies to instances that have encountered an + error before they have been scheduled. Returns 0 if cell0 is created + successfully or already setup. + +``nova-manage cell_v2 map_instances --cell_uuid [--max-count ]`` + + Map instances to the provided cell. Instances in the nova database will + be queried from oldest to newest and mapped to the provided cell. A + max_count can be set on the number of instance to map in a single run. + Repeated runs of the command will start from where the last run finished + so it is not necessary to increase max-count to finish. Returns 0 if all + instances have been mapped, and 1 if there are still instances to be + mapped. + +``nova-manage cell_v2 map_cell_and_hosts [--name ] [--transport-url ] [--verbose]`` + + Create a cell mapping to the database connection and message queue + transport url, and map hosts to that cell. The database connection + comes from the ``[database]/connection`` defined in the configuration + file passed to nova-manage. If a transport_url is not specified, it will + use the one defined by ``[DEFAULT]/transport_url`` in the configuration + file. This command is idempotent (can be run multiple times), and the + verbose option will print out the resulting cell mapping uuid. Returns 0 + on successful completion, and 1 if the transport url is missing. + +``nova-manage cell_v2 verify_instance --uuid [--quiet]`` + + Verify instance mapping to a cell. This command is useful to determine if + the cells v2 environment is properly setup, specifically in terms of the + cell, host, and instance mapping records required. Returns 0 when the + instance is successfully mapped to a cell, 1 if the instance is not + mapped to a cell (see the ``map_instances`` command), and 2 if the cell + mapping is missing (see the ``map_cell_and_hosts`` command if you are + upgrading from a cells v1 environment, and the ``simple_cell_setup`` if + you are upgrading from a non-cells v1 environment). + +``nova-manage cell_v2 create_cell [--name ] [--transport-url ] [--database_connection ] [--verbose]`` + + Create a cell mapping to the database connection and message queue + transport url. If a database_connection is not specified, it will use + the one defined by ``[database]/connection`` in the configuration file + passed to nova-manage. If a transport_url is not specified, it will use + the one defined by ``[DEFAULT]/transport_url`` in the configuration file. + The verbose option will print out the resulting cell mapping uuid. + Returns 0 if the cell mapping was successfully created, 1 if the + transport url or database connection was missing, and 2 if a cell is + already using that transport url and database connection combination. + +``nova-manage cell_v2 discover_hosts [--cell_uuid ] [--verbose] [--strict] [--by-service]`` + + Searches cells, or a single cell, and maps found hosts. This command will + check the database for each cell (or a single one if passed in) and map any + hosts which are not currently mapped. If a host is already mapped nothing + will be done. You need to re-run this command each time you add more + compute hosts to a cell (otherwise the scheduler will never place instances + there and the API will not list the new hosts). If the strict option is + provided the command will only be considered successful if an unmapped host + is discovered (exit code 0). Any other case is considered a failure (exit + code 1). If --by-service is specified, this command will look in the + appropriate cell(s) for any nova-compute services and ensure there are host + mappings for them. This is less efficient and is only necessary when using + compute drivers that may manage zero or more actual compute nodes at any + given time (currently only ironic). + +``nova-manage cell_v2 list_cells [--verbose]`` + + Lists the v2 cells in the deployment. By default only the cell name and + uuid are shown. Use the --verbose option to see transport url and + database connection details. + +``nova-manage cell_v2 delete_cell [--force] --cell_uuid `` + + Delete a cell by the given uuid. Returns 0 if the empty cell is + found and deleted successfully or the cell that has hosts is found and + the cell and the hosts are deleted successfully with ``--force`` option, + 1 if a cell with that uuid could not be found, 2 if host mappings were + found for the cell (cell not empty) without ``--force`` option, and 3 + if there are instances mapped to the cell (cell not empty). + +``nova-manage cell_v2 update_cell --cell_uuid [--name ] [--transport-url ] [--database_connection ]`` + + Updates the properties of a cell by the given uuid. If a + database_connection is not specified, it will attempt to use the one + defined by ``[database]/connection`` in the configuration file. If a + transport_url is not specified, it will attempt to use the one defined + by ``[DEFAULT]/transport_url`` in the configuration file. If the cell + is not found by uuid, this command will return an exit code of 1. If + the properties cannot be set, this will return 2. Otherwise, the exit + code will be 0. + + NOTE: Updating the transport_url or database_connection fields on + a running system will NOT result in all nodes immediately using the + new values. Use caution when changing these values. + +``nova-manage cell_v2 delete_host --cell_uuid --host `` + + Delete a host by the given host name and the given cell uuid. Returns 0 + if the empty host is found and deleted successfully, 1 if a cell with + that uuid could not be found, 2 if a host with that name could not be + found, 3 if a host with that name is not in a cell with that uuid, 4 if + a host with that name has instances (host not empty). + +Nova Logs +~~~~~~~~~ + +.. deprecated:: 16.0.0 + + This will be removed in 17.0.0 (Queens) + +``nova-manage logs errors`` + + Displays nova errors from log files. + +``nova-manage logs syslog `` + + Displays nova alerts from syslog. + +Nova Shell +~~~~~~~~~~ + +.. deprecated:: 16.0.0 + + This will be removed in 17.0.0 (Queens) + +``nova-manage shell bpython`` + + Starts a new bpython shell. + +``nova-manage shell ipython`` + + Starts a new ipython shell. + +``nova-manage shell python`` + + Starts a new python shell. + +``nova-manage shell run`` + + Starts a new shell using python. + +``nova-manage shell script `` + + Runs the named script from the specified path with flags set. + +.. _nova-manage-quota: + +Nova Quota +~~~~~~~~~~ + +.. deprecated:: 16.0.0 + + This will be removed in 17.0.0 (Queens) + +``nova-manage quota refresh`` + + This command has been deprecated and is now a no-op since quota usage is + counted from resources instead of being tracked separately. + +Nova Project +~~~~~~~~~~~~ + +.. deprecated:: 16.0.0 + + Much of this information is available over the API, with the exception of + the ``quota_usage_refresh`` command. Operators should use the `API`_ for + all other operations. + + This command group will be removed in 17.0.0 (Queens). The + ``quota_usage_refresh`` subcommand has been deprecated and is now a no-op + since quota usage is counted from resources instead of being tracked + separately. + +.. _API: https://developer.openstack.org/api-ref/compute/#quota-sets-os-quota-sets + +``nova-manage project quota [--user ] [--key ] [--value ]`` + + Create, update or display quotas for project/user. If a key is + not specified then the current usages are displayed. + +``nova-manage project quota_usage_refresh [--user ] [--key ]`` + + This command has been deprecated and is now a no-op since quota usage is + counted from resources instead of being tracked separately. + +SEE ALSO +======== + +* `OpenStack Nova `__ + +BUGS +==== + +* Nova bugs are managed at Launchpad `Bugs : Nova `__ + + + diff --git a/doc/source/man/nova-network.rst b/doc/source/cli/nova-network.rst similarity index 91% rename from doc/source/man/nova-network.rst rename to doc/source/cli/nova-network.rst index fcda22a423c..80a0ad33a1f 100644 --- a/doc/source/man/nova-network.rst +++ b/doc/source/cli/nova-network.rst @@ -39,7 +39,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/man/nova-novncproxy.rst b/doc/source/cli/nova-novncproxy.rst similarity index 92% rename from doc/source/man/nova-novncproxy.rst rename to doc/source/cli/nova-novncproxy.rst index df758556b15..2f61de42192 100644 --- a/doc/source/man/nova-novncproxy.rst +++ b/doc/source/cli/nova-novncproxy.rst @@ -40,7 +40,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/man/nova-rootwrap.rst b/doc/source/cli/nova-rootwrap.rst similarity index 94% rename from doc/source/man/nova-rootwrap.rst rename to doc/source/cli/nova-rootwrap.rst index ec4fca01207..2de5680bdff 100644 --- a/doc/source/man/nova-rootwrap.rst +++ b/doc/source/cli/nova-rootwrap.rst @@ -50,7 +50,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/man/nova-scheduler.rst b/doc/source/cli/nova-scheduler.rst similarity index 90% rename from doc/source/man/nova-scheduler.rst rename to doc/source/cli/nova-scheduler.rst index d6c6469fa30..e849ad6adeb 100644 --- a/doc/source/man/nova-scheduler.rst +++ b/doc/source/cli/nova-scheduler.rst @@ -39,7 +39,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/man/nova-serialproxy.rst b/doc/source/cli/nova-serialproxy.rst similarity index 81% rename from doc/source/man/nova-serialproxy.rst rename to doc/source/cli/nova-serialproxy.rst index d8ba0df5145..ffacfa160b4 100644 --- a/doc/source/man/nova-serialproxy.rst +++ b/doc/source/cli/nova-serialproxy.rst @@ -40,9 +40,9 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== -* Nova is sourced in Launchpad so you can view current bugs at `OpenStack Nova `__ +* Nova bugs are managed at Launchpad `Bugs : Nova `__ diff --git a/doc/source/man/nova-spicehtml5proxy.rst b/doc/source/cli/nova-spicehtml5proxy.rst similarity index 92% rename from doc/source/man/nova-spicehtml5proxy.rst rename to doc/source/cli/nova-spicehtml5proxy.rst index 2f751a583f7..b1ab3b590ff 100644 --- a/doc/source/man/nova-spicehtml5proxy.rst +++ b/doc/source/cli/nova-spicehtml5proxy.rst @@ -40,7 +40,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/cli/nova-status.rst b/doc/source/cli/nova-status.rst new file mode 100644 index 00000000000..ae92a16db85 --- /dev/null +++ b/doc/source/cli/nova-status.rst @@ -0,0 +1,112 @@ +=========== +nova-status +=========== + +-------------------------------------- +CLI interface for nova status commands +-------------------------------------- + +:Author: openstack@lists.openstack.org +:Date: 2016-12-16 +:Copyright: OpenStack Foundation +:Version: 15.0.0 +:Manual section: 1 +:Manual group: cloud computing + +SYNOPSIS +======== + + nova-status [] + +DESCRIPTION +=========== + +The nova-status command provides routines for checking the status of a Nova +deployment. + +OPTIONS +======= + +The standard pattern for executing a nova-status command is:: + + nova-status [] + +Run without arguments to see a list of available command categories:: + + nova-status + +Categories are: + +* upgrade + +Detailed descriptions are below. + +You can also run with a category argument such as "upgrade" to see a list of +all commands in that category:: + + nova-status upgrade + +These sections describe the available categories and arguments for nova-status. + +Upgrade +~~~~~~~ + +``nova-status upgrade check`` + + Performs a release-specific readiness check before restarting services with + new code. This command expects to have complete configuration and access + to databases and services within a cell. For example, this check may query + the Nova API database and one or more cell databases. It may also make + requests to other services such as the Placement REST API via the Keystone + service catalog. + + **Return Codes** + + .. list-table:: + :widths: 20 80 + :header-rows: 1 + + * - Return code + - Description + * - 0 + - All upgrade readiness checks passed successfully and there is nothing + to do. + * - 1 + - At least one check encountered an issue and requires further + investigation. This is considered a warning but the upgrade may be OK. + * - 2 + - There was an upgrade status check failure that needs to be + investigated. This should be considered something that stops an + upgrade. + * - 255 + - An unexpected error occurred. + + **History of Checks** + + **15.0.0 (Ocata)** + + * Checks are added for cells v2 so ``nova-status upgrade check`` should be + run *after* running the ``nova-manage cell_v2 simple_cell_setup`` + command. + * Checks are added for the Placement API such that there is an endpoint in + the Keystone service catalog, the service is running and the check can + make a successful request to the endpoint. The command also checks to + see that there are compute node resource providers checking in with the + Placement service. More information on the Placement service can be found + at: ``_ + + **16.0.0 (Pike)** + + * Checks for the Placement API are modified to require version 1.4, that + is needed in Pike and further for nova-scheduler to work correctly. + + +SEE ALSO +======== + +* OpenStack Nova Docs: ``_ + +BUGS +==== + +* Nova bugs are managed at Launchpad: ``_ diff --git a/doc/source/man/nova-xvpvncproxy.rst b/doc/source/cli/nova-xvpvncproxy.rst similarity index 91% rename from doc/source/man/nova-xvpvncproxy.rst rename to doc/source/cli/nova-xvpvncproxy.rst index 670ecbb666c..094fb047900 100644 --- a/doc/source/man/nova-xvpvncproxy.rst +++ b/doc/source/cli/nova-xvpvncproxy.rst @@ -39,7 +39,7 @@ FILES SEE ALSO ======== -* `OpenStack Nova `__ +* `OpenStack Nova `__ BUGS ==== diff --git a/doc/source/code-review.rst b/doc/source/code-review.rst deleted file mode 100644 index bd32aecc22d..00000000000 --- a/doc/source/code-review.rst +++ /dev/null @@ -1,276 +0,0 @@ -.. _code-review: - -========================== -Code Review Guide for Nova -========================== - -This is a very terse set of points for reviewers to consider when -looking at nova code. These are things that are important for the -continued smooth operation of Nova, but that tend to be carried as -"tribal knowledge" instead of being written down. It is an attempt to -boil down some of those things into nearly checklist format. Further -explanation about why some of these things are important belongs -elsewhere and should be linked from here. - -Upgrade-Related Concerns -======================== - -RPC API Versions ----------------- - -* If an RPC method is modified, the following needs to happen: - - * The manager-side (example: compute/manager) needs a version bump - * The manager-side method needs to tolerate older calls as well as - newer calls - * Arguments can be added as long as they are optional. Arguments - cannot be removed or changed in an incompatible way. - * The RPC client code (example: compute/rpcapi.py) needs to be able - to honor a pin for the older version (see - self.client.can_send_version() calls). If we are pinned at 1.5, but - the version requirement for a method is 1.7, we need to be able to - formulate the call at version 1.5. - * Methods can drop compatibility with older versions when we bump a - major version. - -* RPC methods can be deprecated by removing the client (example: - compute/rpcapi.py) implementation. However, the manager method must - continue to exist until the major version of the API is bumped. - -Object Versions ---------------- - -* If a tracked attribute (i.e. listed in fields) or remotable method - is added, or a method is changed, the object version must be - bumped. Changes for methods follow the same rules as above for - regular RPC methods. We have tests to try to catch these changes, - which remind you to bump the version and then correct the - version-hash in the tests. -* Field types cannot be changed. If absolutely required, create a - new attribute and deprecate the old one. Ideally, support converting - the old attribute to the new one with an obj_load_attr() - handler. There are some exceptional cases where changing the type - can be allowed, but care must be taken to ensure it does not affect - the wireline API. -* New attributes should be removed from the primitive in - obj_make_compatible() if the attribute was added after the target - version. -* Remotable methods should not return unversioned structures wherever - possible. They should return objects or simple values as the return - types are not (and cannot) be checked by the hash tests. -* Remotable methods should not take complex structures as - arguments. These cannot be verified by the hash tests, and thus are - subject to drift. Either construct an object and pass that, or pass - all the simple values required to make the call. -* Changes to an object as described above will cause a hash to change - in TestObjectVersions. This is a reminder to the developer and the - reviewer that the version needs to be bumped. There are times when - we need to make a change to an object without bumping its version, - but those cases are only where the hash logic detects a change that - is not actually a compatibility issue and must be handled carefully. - -Database Schema ---------------- - -* Changes to the database schema must generally be additive-only. This - means you can add columns, but you can't drop or alter a column. We - have some hacky tests to try to catch these things, but they are - fragile. Extreme reviewer attention to non-online alterations to the - DB schema will help us avoid disaster. -* Dropping things from the schema is a thing we need to be extremely - careful about, making sure that the column has not been used (even - present in one of our models) for at least a release. -* Data migrations must not be present in schema migrations. If data - needs to be converted to another format, or moved from one place to - another, then that must be done while the database server remains - online. Generally, this can and should be hidden within the object - layer so that an object can load from either the old or new - location, and save to the new one. - -REST API -========= - -When making a change to the nova API, we should always follow -`the API WG guidelines `_ -rather than going for "local" consistency. -Developers and reviewers should read all of the guidelines, but they are -very long. So here are some key points: - -* `Terms `_ - - * ``project`` should be used in the REST API instead of ``tenant``. - * ``server`` should be used in the REST API instead of ``instance``. - * ``compute`` should be used in the REST API instead of ``nova``. - -* `Naming Conventions `_ - - * URL should not include underscores; use hyphens ('-') instead. - * The field names contained in a request/response body should - use snake_case style, not CamelCase or Mixed_Case style. - -* `HTTP Response Codes `_ - - * Synchronous resource creation: ``201 Created`` - * Asynchronous resource creation: ``202 Accepted`` - * Synchronous resource deletion: ``204 No Content`` - * For all other successful operations: ``200 OK`` - -Config Options -============== - -Location --------- - -The central place where all config options should reside is the ``/nova/conf/`` -package. Options that are in named sections of ``nova.conf``, such as -``[serial_console]``, should be in their own module. Options that are in the -``[DEFAULT]`` section should be placed in modules that represent a natural -grouping. For example, all of the options that affect the scheduler would be -in the ``scheduler.py`` file, and all the networking options would be moved -to ``network.py``. - -Implementation --------------- - -A config option should be checked for: - -* A short description which explains what it does. If it is a unit - (e.g. timeouts or so) describe the unit which is used (seconds, megabyte, - mebibyte, ...). - -* A long description which shows the impact and scope. The operators should - know the expected change in the behavior of Nova if they tweak this. - -* Hints which services will consume this config option. Operators/Deployers - should not be forced to read the code to know which one of the services will - change its behavior nor should they set this in every ``nova.conf`` file to - be sure. - -* Descriptions/Validations for the possible values. - - * If this is an option with numeric values (int, float), describe the - edge cases (like the min value, max value, 0, -1). - * If this is a DictOpt, describe the allowed keys. - * If this is a StrOpt, list any possible regex validations, or provide a - list of acceptable and/or prohibited values. - -* Interdependencies to other options. If other config options have to be - considered when this config option gets changed, is this described? - -Third Party Tests -================= - -Any change that is not tested well by the Jenkins check jobs must have a -recent +1 vote from an appropriate third party test (or tests) on the latest -patchset, before a core reviewer is allowed to make a +2 vote. - -Virt drivers ------------- - -At a minimum, we must ensure that any technology specific code has a +1 -from the relevant third party test, on the latest patchset, before a +2 vote -can be applied. -Specifically, changes to nova/virt/driver/ need a +1 vote from the -respective third party CI. -For example, if you change something in the XenAPI virt driver, you must wait -for a +1 from the XenServer CI on the latest patchset, before you can give -that patch set a +2 vote. - -This is important to ensure: - -* We keep those drivers stable -* We don't break that third party CI - -Notes ------ - -Please note: - -* Long term, we should ensure that any patch a third party CI is allowed to - vote on, can be blocked from merging by that third party CI. - But we need a lot more work to make something like that feasible, hence the - proposed compromise. -* While its possible to break a virt driver CI system by changing code that is - outside the virt drivers, this policy is not focusing on fixing that. - A third party test failure should always be investigated, but the failure of - a third party test to report in a timely manner should not block others. -* We are only talking about the testing of in-tree code. Please note the only - public API is our REST API, see: :doc:`policies` - -Microversion API -================ - -* If an new microversion API is added, the following needs to happen: - - * A new patch for the microversion API change in python-novaclient side - should be submitted. - -Release Notes -============= - -What is reno ? --------------- - -Nova uses `reno `_ for -providing release notes in-tree. That means that a patch can include a *reno -file* or a series can have a follow-on change containing that file explaining -what the impact is. - -A *reno file* is a YAML file written in the releasenotes/notes tree which is -generated using the reno tool this way: - -.. code-block:: bash - - $ tox -e venv -- reno new - -where usually ```` can be ``bp-`` for a -blueprint or ``bug-XXXXXX`` for a bugfix. - -Refer to the `reno documentation `_ -for the full list of sections. - - -When a release note is needed ------------------------------ - -A release note is required anytime a reno section is needed. Below are some -examples for each section. Any sections that would be blank should be left out -of the note file entirely. If no section is needed, then you know you don't -need to provide a release note :-) - -* ``upgrade`` - * The patch has an `UpgradeImpact `_ tag - * A DB change needs some deployer modification (like a migration) - * A configuration option change (deprecation, removal or modified default) - * some specific changes that have a `DocImpact `_ tag - but require further action from an deployer perspective - * any patch that requires an action from the deployer in general - -* ``security`` - * If the patch fixes a known vulnerability - -* ``features`` - * If the patch has an `APIImpact `_ tag - * For nova-manage and python-novaclient changes, if it adds or changes a - new command, including adding new options to existing commands - * not all blueprints in general, just the ones impacting a `contractual API `_ - * a new virt driver is provided or an existing driver impacts the `HypervisorSupportMatrix `_ - -* ``critical`` - * Bugfixes categorized as Critical in Launchpad *impacting users* - -* ``fixes`` - * No clear definition of such bugfixes. Hairy long-standing bugs with high - importance that have been fixed are good candidates though. - - -Three sections are left intentionally unexplained (``prelude``, ``issues`` and -``other``). Those are targeted to be filled in close to the release time for -providing details about the soon-ish release. Don't use them unless you know -exactly what you are doing. - - -Notifications -============= -* Every new notification type shall use the new versioned notification - infrastructure documented in :doc:`notifications` diff --git a/doc/source/conf.py b/doc/source/conf.py index af23503ad2d..8777167585f 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,20 +1,25 @@ -# -*- 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 # -# nova documentation build configuration file, created by -# sphinx-quickstart on Sat May 1 15:17:47 2010. +# http://www.apache.org/licenses/LICENSE-2.0 # -# This file is execfile()d with the current directory set to -# its containing dir. +# 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. # -# Note that not all possible configuration values are present in this -# autogenerated file. +# nova documentation build configuration file # -# All configuration values have a default; values that are commented out -# serve to show the default. +# Refer to the Sphinx documentation for advice on configuring this file: +# +# http://www.sphinx-doc.org/en/stable/config.html -import subprocess -import sys import os +import sys + +from nova.version import version_info # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -29,26 +34,35 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', - 'ext.nova_todo', + 'sphinx.ext.todo', + 'openstackdocstheme', 'sphinx.ext.coverage', 'sphinx.ext.graphviz', - 'oslosphinx', - "ext.support_matrix", + 'ext.support_matrix', 'oslo_config.sphinxconfiggen', - 'ext.versioned_notifications' + 'oslo_config.sphinxext', + 'oslo_policy.sphinxpolicygen', + 'oslo_policy.sphinxext', + 'ext.versioned_notifications', + 'ext.feature_matrix', ] +# openstackdocstheme options +repository_name = 'openstack/nova' +bug_project = 'nova' +bug_tag = '' + config_generator_config_file = '../../etc/nova/nova-config-generator.conf' sample_config_basename = '_static/nova' +policy_generator_config_file = '../../etc/nova/nova-policy-generator.conf' +sample_policy_basename = '_static/nova' + todo_include_todos = True # The suffix of source filenames. source_suffix = '.rst' -# The encoding of source files. -#source_encoding = 'utf-8' - # The master toctree document. master_doc = 'index' @@ -60,22 +74,11 @@ # |version| and |release|, also used in various other places throughout the # built documents. # -from nova.version import version_info # The full version, including alpha/beta/rc tags. release = version_info.release_string() # The short X.Y version. version = version_info.version_string() -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - # A list of glob-style patterns that should be excluded when looking for # source files. They are matched against the source file names relative to the # source directory, using slashes as directory separators on all platforms. @@ -84,13 +87,6 @@ 'api/nova.tests.*', ] -# The reST default role (used for this markup: `text`) to use -# for all documents. -#default_role = None - -# 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 = False @@ -110,136 +106,56 @@ # Grouping the document tree for man pages. # List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' -man_pages = [ - ('man/nova-all', 'nova-all', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-api-metadata', 'nova-api-metadata', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-api-os-compute', 'nova-api-os-compute', - u'Cloud controller fabric', [u'OpenStack'], 1), - ('man/nova-api', 'nova-api', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-cells', 'nova-cells', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-cert', 'nova-cert', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-compute', 'nova-compute', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-console', 'nova-console', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-consoleauth', 'nova-consoleauth', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-dhcpbridge', 'nova-dhcpbridge', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-idmapshift', 'nova-idmapshift', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-manage', 'nova-manage', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-network', 'nova-network', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-novncproxy', 'nova-novncproxy', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-spicehtml5proxy', 'nova-spicehtml5proxy', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-serialproxy', 'nova-serialproxy', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-rootwrap', 'nova-rootwrap', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-scheduler', 'nova-scheduler', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-xvpvncproxy', 'nova-xvpvncproxy', u'Cloud controller fabric', - [u'OpenStack'], 1), - ('man/nova-conductor', 'nova-conductor', u'Cloud controller fabric', - [u'OpenStack'], 1), +_man_pages = [ + ('nova-api-metadata', u'Cloud controller fabric'), + ('nova-api-os-compute', u'Cloud controller fabric'), + ('nova-api', u'Cloud controller fabric'), + ('nova-cells', u'Cloud controller fabric'), + ('nova-compute', u'Cloud controller fabric'), + ('nova-console', u'Cloud controller fabric'), + ('nova-consoleauth', u'Cloud controller fabric'), + ('nova-dhcpbridge', u'Cloud controller fabric'), + ('nova-idmapshift', u'Cloud controller fabric'), + ('nova-manage', u'Cloud controller fabric'), + ('nova-network', u'Cloud controller fabric'), + ('nova-novncproxy', u'Cloud controller fabric'), + ('nova-spicehtml5proxy', u'Cloud controller fabric'), + ('nova-serialproxy', u'Cloud controller fabric'), + ('nova-rootwrap', u'Cloud controller fabric'), + ('nova-scheduler', u'Cloud controller fabric'), + ('nova-xvpvncproxy', u'Cloud controller fabric'), + ('nova-conductor', u'Cloud controller fabric'), ] +man_pages = [ + ('cli/%s' % name, name, description, [u'OpenStack'], 1) + for name, description in _man_pages] + # -- 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' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None +html_theme = 'openstackdocs' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] +# Add any paths that contain "extra" files, such as .htaccess or +# robots.txt. +html_extra_path = ['_extra'] + # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' -git_cmd = ["git", "log", "--pretty=format:'%ad, commit %h'", "--date=local", - "-n1"] -html_last_updated_fmt = subprocess.Popen( - git_cmd, stdout=subprocess.PIPE).communicate()[0] - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_use_modindex = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'novadoc' +html_last_updated_fmt = '%Y-%m-%d %H:%M' +# Disable smartquotes to ensure all quoted example config options can be copied +# from the docs without later causing unicode errors within Nova. +html_use_smartypants = False # -- Options for LaTeX output ------------------------------------------------- -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). @@ -247,20 +163,3 @@ ('index', 'Nova.tex', u'Nova Documentation', u'OpenStack Foundation', 'manual'), ] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True diff --git a/doc/source/configuration/config.rst b/doc/source/configuration/config.rst new file mode 100644 index 00000000000..8dd6394adc3 --- /dev/null +++ b/doc/source/configuration/config.rst @@ -0,0 +1,9 @@ +===================== +Configuration Options +===================== + +The following is an overview of all available configuration options in Nova. +For a sample configuration file, refer to :doc:`/configuration/sample-config`. + +.. show-options:: + :config-file: etc/nova/nova-config-generator.conf diff --git a/doc/source/configuration/index.rst b/doc/source/configuration/index.rst new file mode 100644 index 00000000000..d0d95620584 --- /dev/null +++ b/doc/source/configuration/index.rst @@ -0,0 +1,33 @@ +=================== +Configuration Guide +=================== + +The static configuration for nova lives in two main files: `nova.conf` and +`policy.json`. These are described below. For a bigger picture view on +configuring nova to solve specific problems, refer to the :doc:`Nova Admin +Guide `. + +Configuration +------------- + +* :doc:`Configuration Guide `: Detailed + configuration guides for various parts of you Nova system. Helpful reference + for setting up specific hypervisor backends. + +* :doc:`Config Reference `: A complete reference of all + configuration options available in the `nova.conf` file. + +* :doc:`Sample Config File `: A sample config + file with inline documentation. + +Policy +------ + +Nova, like most OpenStack projects, uses a policy language to restrict +permissions on REST API actions. + +* :doc:`Policy Reference `: A complete reference of all + policy points in nova and what they impact. + +* :doc:`Sample Policy File `: A sample policy + file with inline documentation. diff --git a/doc/source/configuration/policy.rst b/doc/source/configuration/policy.rst new file mode 100644 index 00000000000..8fea1406e5c --- /dev/null +++ b/doc/source/configuration/policy.rst @@ -0,0 +1,9 @@ +======== +Policies +======== + +The following is an overview of all available policies in Nova. For a sample +configuration file, refer to :doc:`/configuration/sample-policy`. + +.. show-policy:: + :config-file: etc/nova/nova-policy-generator.conf diff --git a/doc/source/configuration/sample-config.rst b/doc/source/configuration/sample-config.rst new file mode 100644 index 00000000000..08ddc14f4b4 --- /dev/null +++ b/doc/source/configuration/sample-config.rst @@ -0,0 +1,18 @@ +========================= +Sample Configuration File +========================= + +The following is a sample nova configuration for adaptation and use. For a +detailed overview of all available configuration options, refer to +:doc:`/configuration/config`. + +The sample configuration can also be viewed in :download:`file form +`. + +.. important:: + + The sample configuration file is auto-generated from nova when this + documentation is built. You must ensure your version of nova matches the + version of this documentation. + +.. literalinclude:: /_static/nova.conf.sample diff --git a/doc/source/configuration/sample-policy.rst b/doc/source/configuration/sample-policy.rst new file mode 100644 index 00000000000..0e4b699af79 --- /dev/null +++ b/doc/source/configuration/sample-policy.rst @@ -0,0 +1,16 @@ +================== +Sample Policy File +================== + +The following is a sample nova policy file for adaptation and use. + +The sample policy can also be viewed in :download:`file form +`. + +.. important:: + + The sample policy file is auto-generated from nova when this documentation + is built. You must ensure your version of nova matches the version of this + documentation. + +.. literalinclude:: /_static/nova.policy.yaml.sample diff --git a/doc/source/contributor/api-2.rst b/doc/source/contributor/api-2.rst new file mode 100644 index 00000000000..6c041ebade4 --- /dev/null +++ b/doc/source/contributor/api-2.rst @@ -0,0 +1,56 @@ +.. + Copyright 2010-2011 OpenStack Foundation + 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. + +.. TODO:: + + This should be merged into contributor/api + +Adding a Method to the OpenStack API +==================================== + +The interface is a mostly RESTful API. REST stands for Representational State Transfer and provides an architecture "style" for distributed systems using HTTP for transport. Figure out a way to express your request and response in terms of resources that are being created, modified, read, or destroyed. + +Routing +------- + +To map URLs to controllers+actions, OpenStack uses the Routes package, a clone of Rails routes for Python implementations. See http://routes.groovie.org/ for more information. + +URLs are mapped to "action" methods on "controller" classes in ``nova/api/openstack/__init__/ApiRouter.__init__`` . + +See http://routes.groovie.org/manual.html for all syntax, but you'll probably just need these two: + - mapper.connect() lets you map a single URL to a single action on a controller. + - mapper.resource() connects many standard URLs to actions on a controller. + +Controllers and actions +----------------------- + +Controllers live in ``nova/api/openstack``, and inherit from nova.wsgi.Controller. + +See ``nova/api/openstack/compute/servers.py`` for an example. + +Action methods take parameters that are sucked out of the URL by mapper.connect() or .resource(). The first two parameters are self and the WebOb request, from which you can get the req.environ, req.body, req.headers, etc. + +Serialization +------------- + +Actions return a dictionary, and wsgi.Controller serializes that to JSON. + +Faults +------ + +If you need to return a non-200, you should +return faults.Fault(webob.exc.HTTPNotFound()) +replacing the exception as appropriate. diff --git a/doc/source/contributor/api.rst b/doc/source/contributor/api.rst new file mode 100644 index 00000000000..696c426c2e3 --- /dev/null +++ b/doc/source/contributor/api.rst @@ -0,0 +1,195 @@ +Extending the API +================= + +Background +---------- + +Nova has v2.1 API frameworks which supports microversions. + +This document covers how to add API for the v2.1 API framework. A +:doc:`microversions specific document ` covers the details +around what is required for the microversions part. + +The v2.1 API framework is under ``nova/api`` and each API is implemented in +``nova/api/openstack/compute``. + +Note that any change to the Nova API to be merged will first require a +spec be approved first. See `here `_ +for the appropriate repository. For guidance on the design of the API +please refer to the `OpenStack API WG +`_ + + +Basic API Controller +-------------------- + +API controller includes the implementation of API methods for a resource. + +A very basic controller of a v2.1 API:: + + """Basic Controller""" + + from nova.api.openstack.compute.schemas import xyz + from nova.api.openstack import extensions + from nova.api.openstack import wsgi + from nova.api import validation + + class BasicController(wsgi.Controller): + + # Define support for GET on a collection + def index(self, req): + data = {'param': 'val'} + return data + + # Define support for POST on a collection + @extensions.expected_errors((400, 409)) + @validation.schema(xyz.create) + @wsgi.response(201) + def create(self, req, body): + write_body_here = ok + return response_body + + # Defining support for other RESTFul methods based on resouce. + + +See `servers.py for ref `_. + +All of the controller modules should live in the ``nova/api/openstack/compute`` directory. + +URL Mapping to API +~~~~~~~~~~~~~~~~~~ + +The URL mapping is based on the plain list which routes the API request to +appropriate controller and method. Each API needs to add its route information +in ``nova/api/openstack/compute/routes.py``. + +A basic skeleton of URL mapping in routers.py:: + + """URL Mapping Router List""" + + import functools + + import nova.api.openstack + from nova.api.openstack.compute import basic_api + + # Create a controller object + basic_controller = functools.partial( + _create_controller, basic_api.BasicController, [], []) + + # Routing list structure: + # ( + # ('Route path': { + # 'HTTP method: [ + # 'Controller', + # 'The method of controller is used to handle this route' + # ], + # ... + # }), + # ... + # ) + ROUTE_LIST = ( + . + . + . + ('/basic', { + 'GET': [basic_controller, 'index'], + 'POST': [basic_controller, 'create'] + }), + . + . + . + ) + +Complete routing list can be found in `routes.py `_. + + +Policy +~~~~~~ + +Policy (permission) is defined ``etc/nova/policy.json``. Implementation of policy +is changing a bit at the moment. Will add more to this document or reference +another one in the future. Also look at the authorize call in controller currently merged. + +Modularity +~~~~~~~~~~ + +The Nova REST API is separated into different controllers in the directory +'nova/api/openstack/compute/' + +Because microversions are supported in the Nova REST API, the API can be +extended without any new controller. But for code readability, the Nova REST API +code still needs modularity. Here are rules for how to separate modules: + +* You are adding a new resource + The new resource should be in standalone module. There isn't any reason to + put different resources in a single module. + +* Add sub-resource for existing resource + To prevent an existing resource module becoming over-inflated, the + sub-resource should be implemented in a separate module. + +* Add extended attributes for existing resource + In normally, the extended attributes is part of existing resource's data + model too. So this can be added into existing resource module directly and + lightly. + To avoid namespace complexity, we should avoid to add extended attributes + in existing extended models. New extended attributes needn't any namespace + prefix anymore. + +JSON-Schema +~~~~~~~~~~~ + +The v2.1 API validates a REST request body with JSON-Schema library. +Valid body formats are defined with JSON-Schema in the directory +'nova/api/openstack/compute/schemas'. Each definition is used at the +corresponding method with the ``validation.schema`` decorator like:: + + @validation.schema(schema.update_something) + def update(self, req, id, body): + .... + +Similarly to controller modularity, JSON-Schema definitions can be added +in same or separate JSON-Schema module. + +The following are the combinations of extensible API and method name +which returns additional JSON-Schema parameters: + +* Create a server API - get_server_create_schema() + +For example, keypairs extension(Keypairs class) contains the method +get_server_create_schema() which returns:: + + { + 'key_name': parameter_types.name, + } + +then the parameter key_name is allowed on Create a server API. + +.. note:: Currently only create schema are implemented in modular way. + Final goal is to merge them all and define the concluded + process in this doc. + +These are essentially hooks into the servers controller which allow other +controller to modify behaviour without having to modify servers.py. In +the past not having this capability led to very large chunks of +unrelated code being added to servers.py which was difficult to +maintain. + + +Unit Tests +---------- + +Should write something more here. But you need to have +both unit and functional tests. + + +Functional tests and API Samples +-------------------------------- + +Should write something here + +Commit message tags +------------------- + +Please ensure you add the ``DocImpact`` tag along with a short +description for any API change. diff --git a/doc/source/blueprints.rst b/doc/source/contributor/blueprints.rst similarity index 81% rename from doc/source/blueprints.rst rename to doc/source/contributor/blueprints.rst index 7c501b6248a..b5e1cc4adde 100644 --- a/doc/source/blueprints.rst +++ b/doc/source/contributor/blueprints.rst @@ -31,12 +31,17 @@ Guidelines for when a feature doesn't need a spec. * If the feature touches code all over the place, it probably should have a design discussion. - * If the feature is big enough that it needs more then one commit, it + * If the feature is big enough that it needs more than one commit, it probably should have a design discussion. * Not an API change. * API changes always require a design discussion. +When a blueprint does not require a spec it still needs to be +approved before the code which implements the blueprint is merged. +Specless blueprints are discussed and potentially approved during +the `Open Discussion` portion of the weekly `nova IRC meeting`_. See +`trivial specifications`_ for more details. Project Priorities =================== @@ -63,3 +68,6 @@ Project Priorities * The blueprint approval team, to help find the right balance of blueprints * Contributors looking for something to work on * People looking for what they can expect in the next release + +.. _nova IRC meeting: http://eavesdrop.openstack.org/#Nova_Team_Meeting +.. _trivial specifications: https://specs.openstack.org/openstack/nova-specs/readme.html#trivial-specifications diff --git a/doc/source/contributor/code-review.rst b/doc/source/contributor/code-review.rst new file mode 100644 index 00000000000..8a8ba514953 --- /dev/null +++ b/doc/source/contributor/code-review.rst @@ -0,0 +1,254 @@ +.. _code-review: + +========================== +Code Review Guide for Nova +========================== + +This is a very terse set of points for reviewers to consider when +looking at nova code. These are things that are important for the +continued smooth operation of Nova, but that tend to be carried as +"tribal knowledge" instead of being written down. It is an attempt to +boil down some of those things into nearly checklist format. Further +explanation about why some of these things are important belongs +elsewhere and should be linked from here. + +Upgrade-Related Concerns +======================== + +RPC API Versions +---------------- + +* If an RPC method is modified, the following needs to happen: + + * The manager-side (example: compute/manager) needs a version bump + * The manager-side method needs to tolerate older calls as well as + newer calls + * Arguments can be added as long as they are optional. Arguments + cannot be removed or changed in an incompatible way. + * The RPC client code (example: compute/rpcapi.py) needs to be able + to honor a pin for the older version (see + self.client.can_send_version() calls). If we are pinned at 1.5, but + the version requirement for a method is 1.7, we need to be able to + formulate the call at version 1.5. + * Methods can drop compatibility with older versions when we bump a + major version. + +* RPC methods can be deprecated by removing the client (example: + compute/rpcapi.py) implementation. However, the manager method must + continue to exist until the major version of the API is bumped. + +Object Versions +--------------- + +* If a tracked attribute (i.e. listed in fields) or remotable method + is added, or a method is changed, the object version must be + bumped. Changes for methods follow the same rules as above for + regular RPC methods. We have tests to try to catch these changes, + which remind you to bump the version and then correct the + version-hash in the tests. +* Field types cannot be changed. If absolutely required, create a + new attribute and deprecate the old one. Ideally, support converting + the old attribute to the new one with an obj_load_attr() + handler. There are some exceptional cases where changing the type + can be allowed, but care must be taken to ensure it does not affect + the wireline API. +* New attributes should be removed from the primitive in + obj_make_compatible() if the attribute was added after the target + version. +* Remotable methods should not return unversioned structures wherever + possible. They should return objects or simple values as the return + types are not (and cannot) be checked by the hash tests. +* Remotable methods should not take complex structures as + arguments. These cannot be verified by the hash tests, and thus are + subject to drift. Either construct an object and pass that, or pass + all the simple values required to make the call. +* Changes to an object as described above will cause a hash to change + in TestObjectVersions. This is a reminder to the developer and the + reviewer that the version needs to be bumped. There are times when + we need to make a change to an object without bumping its version, + but those cases are only where the hash logic detects a change that + is not actually a compatibility issue and must be handled carefully. + +Database Schema +--------------- + +* Changes to the database schema must generally be additive-only. This + means you can add columns, but you can't drop or alter a column. We + have some hacky tests to try to catch these things, but they are + fragile. Extreme reviewer attention to non-online alterations to the + DB schema will help us avoid disaster. +* Dropping things from the schema is a thing we need to be extremely + careful about, making sure that the column has not been used (even + present in one of our models) for at least a release. +* Data migrations must not be present in schema migrations. If data + needs to be converted to another format, or moved from one place to + another, then that must be done while the database server remains + online. Generally, this can and should be hidden within the object + layer so that an object can load from either the old or new + location, and save to the new one. + +REST API +========= + +When making a change to the nova API, we should always follow +`the API WG guidelines `_ +rather than going for "local" consistency. +Developers and reviewers should read all of the guidelines, but they are +very long. So here are some key points: + +* `Terms `_ + + * ``project`` should be used in the REST API instead of ``tenant``. + * ``server`` should be used in the REST API instead of ``instance``. + * ``compute`` should be used in the REST API instead of ``nova``. + +* `Naming Conventions `_ + + * URL should not include underscores; use hyphens ('-') instead. + * The field names contained in a request/response body should + use snake_case style, not CamelCase or Mixed_Case style. + +* `HTTP Response Codes `_ + + * Synchronous resource creation: ``201 Created`` + * Asynchronous resource creation: ``202 Accepted`` + * Synchronous resource deletion: ``204 No Content`` + * For all other successful operations: ``200 OK`` + + +Config Options +============== + +Location +-------- + +The central place where all config options should reside is the ``/nova/conf/`` +package. Options that are in named sections of ``nova.conf``, such as +``[serial_console]``, should be in their own module. Options that are in the +``[DEFAULT]`` section should be placed in modules that represent a natural +grouping. For example, all of the options that affect the scheduler would be +in the ``scheduler.py`` file, and all the networking options would be moved +to ``network.py``. + +Implementation +-------------- + +A config option should be checked for: + +* A short description which explains what it does. If it is a unit + (e.g. timeouts or so) describe the unit which is used (seconds, megabyte, + mebibyte, ...). + +* A long description which explains the impact and scope. The operators should + know the expected change in the behavior of Nova if they tweak this. + +* Descriptions/Validations for the possible values. + + * If this is an option with numeric values (int, float), describe the + edge cases (like the min value, max value, 0, -1). + * If this is a DictOpt, describe the allowed keys. + * If this is a StrOpt, list any possible regex validations, or provide a + list of acceptable and/or prohibited values. + +Previously used sections which explained which services consume a specific +config option and which options are related to each other got dropped +because they are too hard to maintain: +http://lists.openstack.org/pipermail/openstack-dev/2016-May/095538.html + +Third Party Tests +================= + +Any change that is not tested well by the Jenkins check jobs must have a +recent +1 vote from an appropriate third party test (or tests) on the latest +patchset, before a core reviewer is allowed to make a +2 vote. + +Virt drivers +------------ + +At a minimum, we must ensure that any technology specific code has a +1 +from the relevant third party test, on the latest patchset, before a +2 vote +can be applied. +Specifically, changes to nova/virt/driver/ need a +1 vote from the +respective third party CI. +For example, if you change something in the XenAPI virt driver, you must wait +for a +1 from the XenServer CI on the latest patchset, before you can give +that patch set a +2 vote. + +This is important to ensure: + +* We keep those drivers stable +* We don't break that third party CI + +Notes +----- + +Please note: + +* Long term, we should ensure that any patch a third party CI is allowed to + vote on, can be blocked from merging by that third party CI. + But we need a lot more work to make something like that feasible, hence the + proposed compromise. +* While its possible to break a virt driver CI system by changing code that is + outside the virt drivers, this policy is not focusing on fixing that. + A third party test failure should always be investigated, but the failure of + a third party test to report in a timely manner should not block others. +* We are only talking about the testing of in-tree code. Please note the only + public API is our REST API, see: :doc:`policies` + +Should I run the experimental queue jobs on this change? +======================================================== + +Because we can't run all CI jobs in the check and gate pipelines, some +jobs can be executed on demand, thanks to the experimental pipeline. +To run the experimental jobs, you need to comment your Gerrit review +with "check experimental". + +The experimental jobs aim to test specific features, such as LXC containers +or DVR with multiple nodes. Also, it might be useful to run them when +we want to test backward compatibility with tools that deploy OpenStack +outside Devstack (e.g. TripleO, etc). They can produce a non-voting +feedback of whether the system continues to work when we deprecate +or remove some options or features in Nova. + +The experimental queue can also be used to test that new CI jobs are +correct before making them voting. + +Database Schema +=============== + +* Use the ``utf8`` charset only where necessary. Some string fields, such as + hex-stringified UUID values, MD5 fingerprints, SHA1 hashes or base64-encoded + data, are always interpreted using ASCII encoding. A hex-stringified UUID + value in ``latin1`` is 1/3 the size of the same field in ``utf8``, impacting + performance without bringing any benefit. If there are no string type columns + in the table, or the string type columns contain **only** the data described + above, then stick with ``latin1``. + +Microversion API +================ + +If a new microversion API is added, the following needs to happen: + +* A new patch for the microversion API change in python-novaclient side + should be submitted. +* If the microversion changes the response schema, a new schema and test for + the microversion must be added to Tempest. The microversion change in Nova + should not be merged until the Tempest test is submitted and at least + passing; it does not need to be merged yet as long as it is testing the + Nova change via Depends-On. The Nova microversion change commit message + should reference the Change-Id of the Tempest test for reviewers to identify + it. + +Notifications +============= + +* Every new notification type shall use the new versioned notification + infrastructure documented in :doc:`/reference/notifications` + +Release Notes +============= + +A release note is required on changes that have upgrade impact, security +impact, introduce a new feature, fix Critical bugs, or fix long-standing bugs +with high importance. See :doc:`releasenotes` for details on how to create a release +note, each available section and the type of content required. diff --git a/doc/source/contributor/development-environment.rst b/doc/source/contributor/development-environment.rst new file mode 100644 index 00000000000..ec86a6269d7 --- /dev/null +++ b/doc/source/contributor/development-environment.rst @@ -0,0 +1,192 @@ +.. + Copyright 2010-2011 United States Government as represented by the + Administrator of the National Aeronautics and Space Administration. + 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. + +======================= +Development Quickstart +======================= + +This page describes how to setup and use a working Python development +environment that can be used in developing nova on Ubuntu, Fedora or +Mac OS X. These instructions assume you're already familiar with git. + +Following these instructions will allow you to build the documentation +and run the nova unit tests. If you want to be able to run nova (i.e., +launch VM instances), you will also need to --- either manually or by +letting DevStack do it for you --- install libvirt and at least one of +the `supported hypervisors`_. Running nova is currently only supported +on Linux, although you can run the unit tests on Mac OS X. + +.. _supported hypervisors: http://wiki.openstack.org/HypervisorSupportMatrix + + +.. note:: For how to contribute to Nova, see + HowToContribute_. + Nova uses the Gerrit code review system, GerritWorkflow_. + +.. _GerritWorkflow: http://docs.openstack.org/infra/manual/developers.html#development-workflow +.. _HowToContribute: http://docs.openstack.org/infra/manual/developers.html +.. _`docs.openstack.org`: http://docs.openstack.org + +Setup +===== + +There are two ways to create a development environment: using +DevStack, or explicitly installing and cloning just what you need. + + +Using DevStack +-------------- + +See `Devstack`_ Documentation. If you would like to use Vagrant, there is a `Vagrant`_ for DevStack. + +.. _`Devstack`: http://docs.openstack.org/developer/devstack/ +.. _`Vagrant`: https://github.com/openstack-dev/devstack-vagrant/blob/master/README.md + +.. + Until the vagrant markdown documents are rendered somewhere on .openstack.org, linking to github + +Explicit Install/Clone +---------------------- + +DevStack installs a complete OpenStack environment. Alternatively, +you can explicitly install and clone just what you need for Nova +development. + +Getting the code +```````````````` + +Grab the code from git:: + + git clone https://git.openstack.org/openstack/nova + cd nova + + +Linux Systems +````````````` + +The first step of this process is to install the system (not Python) +packages that are required. Following are instructions on how to do +this on Linux and on the Mac. + +.. note:: + + This section is tested for Nova on Ubuntu (14.04-64) and + Fedora-based (RHEL 6.1) distributions. Feel free to add notes and + change according to your experiences or operating system. + +Install the prerequisite packages listed in the ``bindep.txt`` +file. + +On Debian-based distributions (e.g., Debian/Mint/Ubuntu):: + + sudo apt-get install python-pip + sudo pip install tox + tox -e bindep + sudo apt-get install + +On Fedora-based distributions (e.g., Fedora/RHEL/CentOS/Scientific Linux):: + + sudo yum install python-pip + sudo pip install tox + tox -e bindep + sudo yum install + +On openSUSE-based distributions (SLES 12, openSUSE Leap 42.1 or Tumbleweed):: + + sudo zypper in python-pip + sudo pip install tox + tox -e bindep + sudo zypper in + + +Mac OS X Systems +```````````````` + +Install virtualenv:: + + sudo easy_install virtualenv + +Check the version of OpenSSL you have installed:: + + openssl version + +The stock version of OpenSSL that ships with Mac OS X 10.6 (OpenSSL 0.9.8l) +or Mac OS X 10.7 (OpenSSL 0.9.8r) or Mac OS X 10.10.3 (OpenSSL 0.9.8zc) works +fine with nova. OpenSSL versions from brew like OpenSSL 1.0.1k work fine +as well. + + +Building the Documentation +========================== + +Install the prerequisite packages: graphviz + +To do a full documentation build, issue the following command while +the nova directory is current. + +.. code-block:: bash + + tox -edocs + +That will create a Python virtual environment, install the needed +Python prerequisites in that environment, and build all the +documentation in that environment. + +Running unit tests +================== + +See `Running Python Unit Tests`_. + +.. _`Running Python Unit Tests`: http://docs.openstack.org/infra/manual/python.html#running-python-unit-tests + +Note that some unit and functional tests use a database. See the file +``tools/test-setup.sh`` on how the databases are set up in the +OpenStack CI environment and replicate it in your test environment. + +Using a remote debugger +======================= + +Some modern IDE such as pycharm (commercial) or Eclipse (open source) support remote debugging. In order to run nova with remote debugging, start the nova process +with the following parameters +--remote_debug-host +--remote_debug-port + +Before you start your nova process, start the remote debugger using the instructions for that debugger. +For pycharm - http://blog.jetbrains.com/pycharm/2010/12/python-remote-debug-with-pycharm/ +For Eclipse - http://pydev.org/manual_adv_remote_debugger.html + +More detailed instructions are located here - http://novaremotedebug.blogspot.com + +Using fake computes for tests +============================= + +The number of instances supported by fake computes is not limited by physical +constraints. It allows you to perform stress tests on a deployment with few +resources (typically a laptop). But you must avoid using scheduler filters +limiting the number of instances per compute (like RamFilter, DiskFilter, +AggregateCoreFilter), otherwise they will limit the number of instances per +compute. + + +Fake computes can also be used in multi hypervisor-type deployments in order to +take advantage of fake and "real" computes during tests: + +* create many fake instances for stress tests +* create some "real" instances for functional tests + +Fake computes can be used for testing Nova itself but also applications on top +of it. diff --git a/doc/source/contributor/how-to-get-involved.rst b/doc/source/contributor/how-to-get-involved.rst new file mode 100644 index 00000000000..0b266975d86 --- /dev/null +++ b/doc/source/contributor/how-to-get-involved.rst @@ -0,0 +1,377 @@ +.. + 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. + +.. _getting_involved: + +===================================== +How to get (more) involved with Nova +===================================== + +So you want to get more involved with Nova? Or you are new to Nova and +wondering where to start? + +We are working on building easy ways for you to get help and ideas on +how to learn more about Nova and how the Nova community works. + +Any questions, please ask! If you are unsure who to ask, then please +contact the `Mentoring Czar`__. + +__ `Nova People`_ + +How do I get started? +===================== + +There are quite a few global docs on this: + +- http://www.openstack.org/assets/welcome-guide/OpenStackWelcomeGuide.pdf +- https://wiki.openstack.org/wiki/How_To_Contribute +- http://www.openstack.org/community/ + +There is more general info, non Nova specific info here: + +- https://wiki.openstack.org/wiki/Mentors +- https://wiki.openstack.org/wiki/OpenStack_Upstream_Training + +What should I work on? +~~~~~~~~~~~~~~~~~~~~~~ + +So you are starting out your Nova journey, where is a good place to +start? + +If you'd like to learn how Nova works before changing anything (good idea!), we +recommend looking for reviews with -1s and -2s and seeing why they got +downvoted. There is also the :ref:`code-review`. Once you have some +understanding, start reviewing patches. It's OK to ask people to explain things +you don't understand. It's also OK to see some potential problems but put a +0. + +Another way is to look for a subteam you'd like to get involved with and review +their patches. See: +https://etherpad.openstack.org/p/pike-nova-priorities-tracking + +Once you're ready to write code, take a look at some of the work already marked +as low-hanging fruit: + +* https://bugs.launchpad.net/nova/+bugs?field.tag=low-hanging-fruit +* https://etherpad.openstack.org/p/nova-low-hanging-fruit + +How do I get my feature in? +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The best way of getting your feature in is... well it depends. + +First concentrate on solving your problem and/or use case, don't fixate +on getting the code you have working merged. It's likely things will need +significant re-work after you discuss how your needs match up with all +the existing ways Nova is currently being used. The good news, is this +process should leave you with a feature that's more flexible and doesn't +lock you into your current way of thinking. + +A key part of getting code merged, is helping with reviewing other +people's code. Great reviews of others code will help free up more core +reviewer time to look at your own patches. In addition, you will +understand how the review is thinking when they review your code. + +Also, work out if any on going efforts are blocking your feature and +helping out speeding those up. The spec review process should help with +this effort. + +For more details on our process, please see: :ref:`process`. + +What is expected of a good contributor? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TODO - need more info on this + +Top Tips for working with the Nova community +============================================ + +Here are some top tips around engaging with the Nova community: + +- IRC + + - we talk a lot in #openstack-nova + - do ask us questions in there, and we will try to help you + - not sure about asking questions? feel free to listen in around + other people's questions + - we recommend you setup an IRC bouncer: + https://wiki.openstack.org/wiki/IRC + +- Email + + - Use the [nova] tag in the mailing lists + - Filtering on [nova] and [all] can help tame the list + +- Be Open + + - i.e. don't review your teams code in private, do it publicly in + gerrit + - i.e. be ready to talk about openly about problems you are having, + not "theoretical" issues + - that way you can start to gain the trust of the wider community + +- Got a problem? Please ask! + + - Please raise any problems and ask questions early + - we want to help you before you are frustrated or annoyed + - unsure who to ask? Just ask in IRC, or check out the list of `Nova + people`_. + +- Talk about problems first, then solutions + + - Nova is a big project. At first, it can be hard to see the big + picture + - Don't think about "merging your patch", instead think about + "solving your problem" + - conversations are more productive that way + +- It's not the decision that's important, it's the reason behind it that's + important + + - Don't like the way the community is going? + - Please ask why we were going that way, and please engage with the + debate + - If you don't, we are unable to learn from what you have to offer + +- No one will decide, this is stuck, who can help me? + + - it's rare, but it happens + - it's the `Nova PTL`__'s job to help you + - ...but if you don't ask, it's hard for them to help you + +__ `Nova People`_ + +Process +======= + +It can feel like you are faced with a wall of process. We are a big +community, to make sure the right communication happens, we do use a +minimal amount of process. + +If you find something that doesn't make sense, please: + +- ask questions to find out \*why\* it happens +- if you know of a better way to do it, please speak up +- one "better way" might be to remove the process if it no longer helps + +To learn more about Nova's process, please read :ref:`process`. + +Why bother with any process? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Why is it worth creating a bug or blueprint to track your code review? +This may seem like silly process, but there is usually a good reason +behind it. + +We have lots of code to review, and we have tools to try and get to +really important code reviews first. If yours is really important, but +not picked up by our tools, it's possible you just get lost in the bottom +of a big queue. + +If you have a bug fix, you have done loads of work to identify the +issue, and test out your fix, and submit it. By adding a bug report, you +are making it easier for other folks who hit the same problem to find +your work, possibly saving them the hours of pain you went through. With +any luck that gives all those people the time to fix different bugs, all +that might have affected you, if you had not given them the time go fix +it. + +It's similar with blueprints. You have worked out how to scratch your +itch, lets tell others about that great new feature you have added, so +they can use that. Also, it stops someone with a similar idea going +through all the pain of creating a feature only to find you already have +that feature ready and up for review, or merged into the latest release. + +Hopefully this gives you an idea why we have applied a small layer of +process to what we are doing. Having said all this, we need to unlearn +old habits to move forward, there may be better ways to do things, and +we are open to trying them. Please help be part of the solution. + +.. _why_plus1: + +Why do code reviews if I am not in nova-core? +============================================= + +Code reviews are the life blood of the Nova developer community. + +There is a good discussion on how you do good reviews, and how anyone +can be a reviewer: +http://docs.openstack.org/infra/manual/developers.html#peer-review + +In the draft process guide, I discuss how doing reviews can help get +your code merged faster: :ref:`process`. + +Lets look at some of the top reasons why participating with code reviews +really helps you: + +- Doing more reviews, and seeing what other reviewers notice, will help + you better understand what is expected of code that gets merged into + master +- Having more non-core people do great reviews, leaves less review work + for the core reviewers to do, so we are able get more code merged +- Empathy is one of the keys to a happy community. If you are used to + doing code reviews, you will better understand the comments you get + when people review your code. As you do more code reviews, and see + what others notice, you will get a better idea of what people are + looking for when then apply a +2 to your code. +- TODO - needs more detail + +What are the most useful types of code review comments? Well here are a +few to the top ones: + +- Fundamental flaws are the biggest thing to spot. Does the patch break + a whole set of existing users, or an existing feature? +- Consistency of behaviour is really important. Does this bit of code + do things differently to where similar things happen else where in + Nova? +- Is the code easy to maintain, well tested and easy to read? Code is + read order of magnitude times more than it is written, so optimise + for the reader of the code, not the writer. +- TODO - what others should go here? + +Let's look at some problems people hit when starting out doing code +reviews: + +- My +1 doesn't mean anything, why should I bother? + + - So your +1 really does help. Some really useful -1 votes that lead + to a +1 vote helps get code into a position + +- When to use -1 vs 0 vs +1 + + - Please see the guidelines here: + http://docs.openstack.org/infra/manual/developers.html#peer-review + +- I have already reviewed this code internally, no point in adding a +1 + externally? + + - Please talk to your company about doing all code reviews in the + public, that is a much better way to get involved. showing how the + code has evolved upstream, is much better than trying to 'perfect' + code internally, before uploading for public review. You can use + Draft mode, and mark things as WIP if you prefer, but please do + the reviews upstream. + +- Where do I start? What should I review? + + - There are various tools, but a good place to start is: + https://etherpad.openstack.org/p/pike-nova-priorities-tracking + - Depending on the time in the cycle, it's worth looking at + NeedsCodeReview blueprints: + https://blueprints.launchpad.net/nova/ + - Custom Gerrit review dashboards often provide a more manageable view of + the outstanding reviews, and help focus your efforts: + + - Nova Review Inbox: + https://goo.gl/1vTS0Z + - Small Bug Fixes: + http://ow.ly/WAw1J + + - Maybe take a look at things you want to see merged, bug fixes and + features, or little code fixes + - Look for things that have been waiting a long time for a review: + http://5885fef486164bb8596d-41634d3e64ee11f37e8658ed1b4d12ec.r44.cf3.rackcdn.com/nova-openreviews.html + - If you get through the above lists, try other tools, such as: + http://status.openstack.org/reviews + +How to do great code reviews? +============================= + +http://docs.openstack.org/infra/manual/developers.html#peer-review + +For more tips, please see: `Why do code reviews if I am not in nova-core?`_ + +How do I become nova-core? +========================== + +You don't have to be nova-core to be a valued member of the Nova +community. There are many, many ways you can help. Every quality review +that helps someone get their patch closer to being ready to merge helps +everyone get their code merged faster. + +The first step to becoming nova-core is learning how to be an active +member of the Nova community, including learning how to do great code +reviews. For more details see: +https://wiki.openstack.org/wiki/Nova/CoreTeam#Membership_Expectations + +If you feel like you have the time to commit to all the nova-core +membership expectations, reach out to the Nova PTL who will be +able to find you an existing member of nova-core to help mentor you. If +all goes well, and you seem like a good candidate, your mentor will +contact the rest of the nova-core team to ask them to start looking at +your reviews, so they are able to vote for you, if you get nominated for +join nova-core. + +We encourage all mentoring, where possible, to occur on #openstack-nova +so everyone can learn and benefit from your discussions. + +The above mentoring is available to every one who wants to learn how to +better code reviews, even if you don't ever want to commit to becoming +nova-core. If you already have a mentor, that's great, the process is +only there for folks who are still trying to find a mentor. Being +admitted to the mentoring program no way guarantees you will become a +member of nova-core eventually, it's here to help you improve, and help +you have the sort of involvement and conversations that can lead to +becoming a member of nova-core. + +How to do great nova-spec reviews? +================================== + +http://specs.openstack.org/openstack/nova-specs/specs/ocata/template.html + +http://docs.openstack.org/developer/nova/blueprints.html#specs + +Spec reviews are always a step ahead of the normal code reviews. Follow +the above links for some great information on specs/reviews. + +The following could be some important tips: + +1. The specs are published as html documents. Ensure that the author has +a proper render of the same via the .rst file. + +2. More often than not, it's important to know that there are no +overlaps across multiple specs. + +3. Ensure that a proper dependency of the spec is identified. For +example - a user desired feature that requires a proper base enablement +should be a dependent spec. + +4. Ask for clarity on changes that appear ambiguous to you. + +5. Every release nova gets a huge set of spec proposals and that's a +huge task for the limited set of nova cores to complete. Helping the +cores with additional reviews is always a great thing. + +How to do great bug triage? +=========================== + +https://wiki.openstack.org/wiki/Nova/BugTriage + +More details coming soon... + +How to step up into a project leadership role? +============================================== + +There are many ways to help lead the Nova project: + +* Mentoring efforts, and getting started tips: + https://wiki.openstack.org/wiki/Nova/Mentoring +* Info on process, with a focus on how you can go from an idea + to getting code merged Nova: + https://wiki.openstack.org/wiki/Nova/Mitaka_Release_Schedule +* Consider leading an existing `Nova subteam`_ or forming a new one. +* Consider becoming a `Bug tag owner`_. +* Contact the PTL about becoming a Czar `Nova People`_. + +.. _`Nova people`: https://wiki.openstack.org/wiki/Nova#People +.. _`Nova subteam`: https://wiki.openstack.org/wiki/Nova#Nova_subteams +.. _`Bug tag owner`: https://wiki.openstack.org/wiki/Nova/BugTriage#Step_2:_Triage_Tagged_Bugs diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst new file mode 100644 index 00000000000..cb379d3cc1d --- /dev/null +++ b/doc/source/contributor/index.rst @@ -0,0 +1,90 @@ +=========================== + Contributor Documentation +=========================== + +Contributing to nova gives you the power to help add features, fix bugs, +enhance documentation, and increase testing. Contributions of any type are +valuable, and part of what keeps the project going. Here are a list of +resources to get your started. + +Getting Started +=============== + +* :doc:`/contributor/how-to-get-involved`: Overview of engaging in the project +* :doc:`/contributor/development-environment`: Get your computer setup to + contribute + +Nova Process +============ + +The nova community is a large community. We have lots of users, and they all +have a lot of expectations around upgrade and backwards compatibility. For +example, having a good stable API, with discoverable versions and capabilities +is important for maintaining the strong ecosystem around Nova. + +Our process is always evolving, just as nova and the community around Nova +evolves over time. If there are things that seem strange, or you have ideas on +how to improve things, please bring them forward on IRC or the openstack-dev +mailing list, so we continue to improve how the nova community operates. + +This section looks at the processes and why. The main aim behind all the +process is to aid communication between all members of the nova community, +while keeping users happy and keeping developers productive. + +* :doc:`/contributor/project-scope`: The focus is on features and bug fixes + that make nova work better within this scope +* :doc:`/contributor/policies`: General guidelines about what's supported +* :doc:`/contributor/process`: The processes we follow around feature and bug + submission, including how the release calendar works, and the freezes we go + under +* :doc:`/contributor/blueprints`: An overview of our tracking artifacts. + +For Reviewers +============= + +* :doc:`/contributor/releasenotes`: When we need a release note for a + contribution. +* :doc:`/contributor/code-review`: important cheat sheet for what's important + when doing code review in Nova, especially some things that are hard to test + for, but need human eyes. +* :doc:`/reference/i18n`: what we require for i18n in patches + +Testing +======= + +Because Python is a dynamic language, code that is not testing might not even +be python code. All new code needs to be validated somehow. + +* :doc:`/contributor/testing`: An overview of our test taxonomy, and the kinds + of testing we do and expect. + +* **Testing Guides**: There are also specific testing guides for features that are + hard to test in our gate. + + * :doc:`/contributor/testing/libvirt-numa` + + * :doc:`/contributor/testing/serial-console` + + * :doc:`/contributor/testing/zero-downtime-upgrade` + +The Nova API +============ + +Because we have many consumers of our API, we're extremely careful about +changes done to the API, as the impact can be very wide. + +* :doc:`/contributor/api`: How the code is structured inside the API layer +* :doc:`/contributor/api-2`: (needs update) +* :doc:`/contributor/microversions`: we use microversions to version our API, + this walks you through what you need to do when adding an API exposed feature + that needs one. + +Nova Major Subsystems +===================== + +Major subsystems in Nova have different needs, some of those are documented +here. If you are contributing to one of these please read the subsystem guide +before diving in. + +* :doc:`/contributor/placement` +* :doc:`/user/conductor` diff --git a/doc/source/contributor/microversions.rst b/doc/source/contributor/microversions.rst new file mode 100644 index 00000000000..c253c1c3c26 --- /dev/null +++ b/doc/source/contributor/microversions.rst @@ -0,0 +1,396 @@ +API Microversions +================= + +Background +---------- + +Nova uses a framework we call 'API Microversions' for allowing changes +to the API while preserving backward compatibility. The basic idea is +that a user has to explicitly ask for their request to be treated with +a particular version of the API. So breaking changes can be added to +the API without breaking users who don't specifically ask for it. This +is done with an HTTP header ``OpenStack-API-Version`` which has as its +value a string containing the name of the service, ``compute``, and a +monotonically increasing semantic version number starting from ``2.1``. +The full form of the header takes the form:: + + OpenStack-API-Version: compute 2.1 + +If a user makes a request without specifying a version, they will get +the ``DEFAULT_API_VERSION`` as defined in +``nova/api/openstack/wsgi.py``. This value is currently ``2.1`` and +is expected to remain so for quite a long time. + +There is a special value ``latest`` which can be specified, which will +allow a client to always receive the most recent version of API +responses from the server. + +.. warning:: The ``latest`` value is mostly meant for integration testing and + would be dangerous to rely on in client code since Nova microversions are not + following semver and therefore backward compatibility is not guaranteed. + Clients, like python-novaclient, should always require a specific + microversion but limit what is acceptable to the version range that it + understands at the time. + +.. warning:: To maintain compatibility, an earlier form of the microversion + header is acceptable. It takes the form:: + + X-OpenStack-Nova-API-Version: 2.1 + + This form will continue to be supported until the ``DEFAULT_API_VERSION`` + is raised to version ``2.27`` or higher. + + Clients accessing deployments of the Nova API which are not yet + providing microversion ``2.27`` must use the older form. + +For full details please read the `Kilo spec for microversions +`_ +and `Microversion Specification +`_. + +When do I need a new Microversion? +---------------------------------- + +A microversion is needed when the contract to the user is +changed. The user contract covers many kinds of information such as: + +- the Request + + - the list of resource urls which exist on the server + + Example: adding a new servers/{ID}/foo which didn't exist in a + previous version of the code + + - the list of query parameters that are valid on urls + + Example: adding a new parameter ``is_yellow`` servers/{ID}?is_yellow=True + + - the list of query parameter values for non free form fields + + Example: parameter filter_by takes a small set of constants/enums "A", + "B", "C". Adding support for new enum "D". + + - new headers accepted on a request + + - the list of attributes and data structures accepted. + + Example: adding a new attribute 'locked': True/False to the request body + + However, the attribute ``os.scheduler_hints`` of the "create a server" API + is an exception to this. A new scheduler which adds a new attribute + to ``os:scheduler_hints`` doesn't require a new microversion, because + available schedulers depend on cloud environments, and we accept customized + schedulers as a rule. + +- the Response + + - the list of attributes and data structures returned + + Example: adding a new attribute 'locked': True/False to the output + of servers/{ID} + + - the allowed values of non free form fields + + Example: adding a new allowed ``status`` to servers/{ID} + + - the list of status codes allowed for a particular request + + Example: an API previously could return 200, 400, 403, 404 and the + change would make the API now also be allowed to return 409. + + See [#f2]_ for the 400, 403, 404 and 415 cases. + + - changing a status code on a particular response + + Example: changing the return code of an API from 501 to 400. + + .. note:: Fixing a bug so that a 400+ code is returned rather than a 500 or + 503 does not require a microversion change. It's assumed that clients are + not expected to handle a 500 or 503 response and therefore should not + need to opt-in to microversion changes that fixes a 500 or 503 response + from happening. + According to the OpenStack API Working Group, a + **500 Internal Server Error** should **not** be returned to the user for + failures due to user error that can be fixed by changing the request on + the client side. See [#f1]_. + + - new headers returned on a response + +The following flow chart attempts to walk through the process of "do +we need a microversion". + + +.. graphviz:: + + digraph states { + + label="Do I need a microversion?" + + silent_fail[shape="diamond", style="", group=g1, label="Did we silently + fail to do what is asked?"]; + ret_500[shape="diamond", style="", group=g1, label="Did we return a 500 + before?"]; + new_error[shape="diamond", style="", group=g1, label="Are we changing what + status code is returned?"]; + new_attr[shape="diamond", style="", group=g1, label="Did we add or remove an + attribute to a payload?"]; + new_param[shape="diamond", style="", group=g1, label="Did we add or remove + an accepted query string parameter or value?"]; + new_resource[shape="diamond", style="", group=g1, label="Did we add or remove a + resource url?"]; + + + no[shape="box", style=rounded, label="No microversion needed"]; + yes[shape="box", style=rounded, label="Yes, you need a microversion"]; + no2[shape="box", style=rounded, label="No microversion needed, it's + a bug"]; + + silent_fail -> ret_500[label=" no"]; + silent_fail -> no2[label="yes"]; + + ret_500 -> no2[label="yes [1]"]; + ret_500 -> new_error[label=" no"]; + + new_error -> new_attr[label=" no"]; + new_error -> yes[label="yes"]; + + new_attr -> new_param[label=" no"]; + new_attr -> yes[label="yes"]; + + new_param -> new_resource[label=" no"]; + new_param -> yes[label="yes"]; + + new_resource -> no[label=" no"]; + new_resource -> yes[label="yes"]; + + {rank=same; yes new_attr} + {rank=same; no2 ret_500} + {rank=min; silent_fail} + } + + +**Footnotes** + +.. [#f1] When fixing 500 errors that previously caused stack traces, try + to map the new error into the existing set of errors that API call + could previously return (400 if nothing else is appropriate). Changing + the set of allowed status codes from a request is changing the + contract, and should be part of a microversion (except in [#f2]_). + + The reason why we are so strict on contract is that we'd like + application writers to be able to know, for sure, what the contract is + at every microversion in Nova. If they do not, they will need to write + conditional code in their application to handle ambiguities. + + When in doubt, consider application authors. If it would work with no + client side changes on both Nova versions, you probably don't need a + microversion. If, on the other hand, there is any ambiguity, a + microversion is probably needed. + +.. [#f2] The exception to not needing a microversion when returning a + previously unspecified error code is the 400, 403, 404 and 415 cases. This is + considered OK to return even if previously unspecified in the code since + it's implied given keystone authentication can fail with a 403 and API + validation can fail with a 400 for invalid json request body. Request to + url/resource that does not exist always fails with 404. Invalid content types + are handled before API methods are called which results in a 415. + + .. note:: When in doubt about whether or not a microversion is required + for changing an error response code, consult the `Nova API subteam`_. + +.. _Nova API subteam: https://wiki.openstack.org/wiki/Meetings/NovaAPI + + +When a microversion is not needed +--------------------------------- + +A microversion is not needed in the following situation: + +- the response + + - Changing the error message without changing the response code + does not require a new microversion. + + - Removing an inapplicable HTTP header, for example, suppose the Retry-After + HTTP header is being returned with a 4xx code. This header should only be + returned with a 503 or 3xx response, so it may be removed without bumping + the microversion. + +In Code +------- + +In ``nova/api/openstack/wsgi.py`` we define an ``@api_version`` decorator +which is intended to be used on top-level Controller methods. It is +not appropriate for lower-level methods. Some examples: + +Adding a new API method +~~~~~~~~~~~~~~~~~~~~~~~ + +In the controller class:: + + @wsgi.Controller.api_version("2.4") + def my_api_method(self, req, id): + .... + +This method would only be available if the caller had specified an +``OpenStack-API-Version`` of >= ``2.4``. If they had specified a +lower version (or not specified it and received the default of ``2.1``) +the server would respond with ``HTTP/404``. + +Removing an API method +~~~~~~~~~~~~~~~~~~~~~~ + +In the controller class:: + + @wsgi.Controller.api_version("2.1", "2.4") + def my_api_method(self, req, id): + .... + +This method would only be available if the caller had specified an +``OpenStack-API-Version`` of <= ``2.4``. If ``2.5`` or later +is specified the server will respond with ``HTTP/404``. + +Changing a method's behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the controller class:: + + @wsgi.Controller.api_version("2.1", "2.3") + def my_api_method(self, req, id): + .... method_1 ... + + @wsgi.Controller.api_version("2.4") # noqa + def my_api_method(self, req, id): + .... method_2 ... + +If a caller specified ``2.1``, ``2.2`` or ``2.3`` (or received the +default of ``2.1``) they would see the result from ``method_1``, +``2.4`` or later ``method_2``. + +It is vital that the two methods have the same name, so the second of +them will need ``# noqa`` to avoid failing flake8's ``F811`` rule. The +two methods may be different in any kind of semantics (schema +validation, return values, response codes, etc) + +A change in schema only +~~~~~~~~~~~~~~~~~~~~~~~ + +If there is no change to the method, only to the schema that is used for +validation, you can add a version range to the ``validation.schema`` +decorator:: + + @wsgi.Controller.api_version("2.1") + @validation.schema(dummy_schema.dummy, "2.3", "2.8") + @validation.schema(dummy_schema.dummy2, "2.9") + def update(self, req, id, body): + .... + +This method will be available from version ``2.1``, validated according to +``dummy_schema.dummy`` from ``2.3`` to ``2.8``, and validated according to +``dummy_schema.dummy2`` from ``2.9`` onward. + + +When not using decorators +~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you don't want to use the ``@api_version`` decorator on a method +or you want to change behavior within a method (say it leads to +simpler or simply a lot less code) you can directly test for the +requested version with a method as long as you have access to the api +request object (commonly called ``req``). Every API method has an +api_version_request object attached to the req object and that can be +used to modify behavior based on its value:: + + def index(self, req): + + + req_version = req.api_version_request + req1_min = api_version_request.APIVersionRequest("2.1") + req1_max = api_version_request.APIVersionRequest("2.5") + req2_min = api_version_request.APIVersionRequest("2.6") + req2_max = api_version_request.APIVersionRequest("2.10") + + if req_version.matches(req1_min, req1_max): + ....stuff.... + elif req_version.matches(req2min, req2_max): + ....other stuff.... + elif req_version > api_version_request.APIVersionRequest("2.10"): + ....more stuff..... + + + +The first argument to the matches method is the minimum acceptable version +and the second is maximum acceptable version. A specified version can be null:: + + null_version = APIVersionRequest() + +If the minimum version specified is null then there is no restriction on +the minimum version, and likewise if the maximum version is null there +is no restriction the maximum version. Alternatively a one sided comparison +can be used as in the example above. + +Other necessary changes +----------------------- + +If you are adding a patch which adds a new microversion, it is +necessary to add changes to other places which describe your change: + +* Update ``REST_API_VERSION_HISTORY`` in + ``nova/api/openstack/api_version_request.py`` + +* Update ``_MAX_API_VERSION`` in + ``nova/api/openstack/api_version_request.py`` + +* Add a verbose description to + ``nova/api/openstack/compute/rest_api_version_history.rst``. There should + be enough information that it could be used by the docs team for + release notes. + +* Update the expected versions in affected tests, for example in + ``nova/tests/unit/api/openstack/compute/test_versions.py``. + +* Update the get versions api sample file: + ``doc/api_samples/versions/versions-get-resp.json`` and + ``doc/api_samples/versions/v21-version-get-resp.json``. + +* Make a new commit to python-novaclient and update corresponding + files to enable the newly added microversion API. + +* If the microversion changes the response schema, a new schema and test for + the microversion must be added to Tempest. + +Allocating a microversion +------------------------- + +If you are adding a patch which adds a new microversion, it is +necessary to allocate the next microversion number. Except under +extremely unusual circumstances and this would have been mentioned in +the nova spec for the change, the minor number of ``_MAX_API_VERSION`` +will be incremented. This will also be the new microversion number for +the API change. + +It is possible that multiple microversion patches would be proposed in +parallel and the microversions would conflict between patches. This +will cause a merge conflict. We don't reserve a microversion for each +patch in advance as we don't know the final merge order. Developers +may need over time to rebase their patch calculating a new version +number as above based on the updated value of ``_MAX_API_VERSION``. + +Testing Microversioned API Methods +---------------------------------- + +Testing a microversioned API method is very similar to a normal controller +method test, you just need to add the ``OpenStack-API-Version`` +header, for example:: + + req = fakes.HTTPRequest.blank('/testable/url/endpoint') + req.headers = {'OpenStack-API-Version': 'compute 2.28'} + req.api_version_request = api_version.APIVersionRequest('2.6') + + controller = controller.TestableController() + + res = controller.index(req) + ... assertions about the response ... + +For many examples of testing, the canonical examples are in +``nova/tests/unit/api/openstack/compute/test_microversions.py``. diff --git a/doc/source/contributor/placement.rst b/doc/source/contributor/placement.rst new file mode 100644 index 00000000000..fb3a119e87c --- /dev/null +++ b/doc/source/contributor/placement.rst @@ -0,0 +1,377 @@ +.. + 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. + +=============================== + Placement API Developer Notes +=============================== + +Overview +======== + +The Nova project introduced the :doc:`placement service ` as +part of the Newton release. The service provides an HTTP API to manage +inventories of different classes of resources, such as disk or virtual cpus, +made available by entities called resource providers. Information provided +through the placement API is intended to enable more effective accounting of +resources in an OpenStack deployment and better scheduling of various entities +in the cloud. + +The document serves to explain the architecture of the system and to provide +some guidance on how to maintain and extend the code. For more detail on why +the system was created and how it does its job see :doc:`/user/placement`. + +Big Picture +=========== + +The placement service is straightforward: It is a `WSGI`_ application that +sends and receives JSON, using an RDBMS (usually MySQL) for persistence. +As state is managed solely in the DB, scaling the placement service is done by +increasing the number of WSGI application instances and scaling the RDBMS using +traditional database scaling techniques. + +For sake of consistency and because there was initially intent to make the +entities in the placement service available over RPC, `versioned objects`_ are +used to provide the interface between the HTTP application layer and the +SQLAlchemy-driven persistence layer. Even without RPC, these objects provide +useful structuring and separation of the code. + +Though the placement service doesn't aspire to be a `microservice` it does +aspire to continue to be small and minimally complex. This means a relatively +small amount of middleware that is not configurable, and a limited number of +exposed resources where any given resource is represented by one (and only +one) URL that expresses a noun that is a member of the system. Adding +additional resources should be considered a significant change requiring robust +review from many stakeholders. + +The set of HTTP resources represents a concise and constrained grammar for +expressing the management of resource providers, inventories, resource classes +and allocations. If a solution is initially designed to need more resources or +a more complex grammar that may be a sign that we need to give our goals +greater scrutiny. Is there a way to do what we want with what we have already? +Can some other service help? Is a new collaborating service required? + +Minimal Framework +================= + +The API is set up to use a minimal framework that tries to keep the structure +of the application as discoverable as possible and keeps the HTTP interaction +near the surface. The goal of this is to make things easy to trace when +debugging or adding functionality. + +Functionality which is required for every request is handled in raw WSGI +middleware that is composed in the `nova.api.openstack.placement.deploy` +module. Dispatch or routing is handled declaratively via the +``ROUTE_DECLARATIONS`` map defined in the +`nova.api.openstack.placement.handler` module. + +Mapping is by URL plus request method. The destination is a complete WSGI +application, using a subclass of the `wsgify`_ method from `WebOb`_ to provide +a `Request`_ object that provides convenience methods for accessing request +headers, bodies, and query parameters and for generating responses. In the +placement API these mini-applications are called `handlers`. The `wsgify` +subclass is provided in `nova.api.openstack.placement.wsgi_wrapper` as +`PlacementWsgify`. It is used to make sure that JSON formatted error responses +are structured according to the API-WG `errors`_ guideline. + +This division between middleware, dispatch and handlers is supposed to +provide clues on where a particular behavior or functionality should be +implemented. Like most such systems, this doesn't always work but is a useful +tool. + +Gotchas +======= + +This section tries to shed some light on some of the differences between the +placement API and some of the nova APIs or on situations which may be +surprising or unexpected. + +* The placement API is somewhat more strict about `Content-Type` and `Accept` + headers in an effort to follow the HTTP RFCs. + + If a user-agent sends some JSON in a `PUT` or `POST` request without a + `Content-Type` of `application/json` the request will result in an error. + + If a `GET` request is made without an `Accept` header, the response will + default to being `application/json`. + + If a request is made with an explicit `Accept` header that does not include + `application/json` then there will be an error and the error will attempt to + be in the requested format (for example, `text/plain`). + +* If a URL exists, but a request is made using a method that that URL does not + support, the API will respond with a `405` error. Sometimes in the nova APIs + this can be a `404` (which is wrong, but understandable given the constraints + of the code). + +* Because each handler is individually wrapped by the `PlacementWsgify` + decorator any exception that is a subclass of `webob.exc.WSGIHTTPException` + that is raised from within the handler, such as `webob.exc.HTTPBadRequest`, + will be caught by WebOb and turned into a valid `Response`_ containing + headers and body set by WebOb based on the information given when the + exception was raised. It will not be seen as an exception by any of the + middleware in the placement stack. + + In general this is a good thing, but it can lead to some confusion if, for + example, you are trying to add some middleware that operates on exceptions. + + Other exceptions that are not from `WebOb`_ will raise outside the handlers + where they will either be caught in the `__call__` method of the + `PlacementHandler` app that is responsible for dispatch, or by the + `FaultWrap` middleware. + +Microversions +============= + +The placement API makes use of `microversions`_ to allow the release of new +features on an opt in basis. See :doc:`/user/placement` for an up to date +history of the available microversions. + +The rules around when a microversion is needed are the same as for the +:doc:`compute API `. When adding a new microversion +there are a few bits of required housekeeping that must be done in the code: + +* Update the ``VERSIONS`` list in + `nova.api.openstack.placement.microversion` to indicate the new + microversion and give a very brief summary of the added feature. +* Update `nova/api/openstack/placement/rest_api_version_history.rst` + to add a more detailed section describing the new microversion. +* Add a `release note`_ announcing the new or changed feature and + the microversion. +* If the ``version_handler`` decorator (see below) has been used, + increment ``TOTAL_VERSIONED_METHODS`` in + `nova/tests/unit/api/openstack/placement/test_microversion.py`. + This provides a confirmatory check just to make sure you're paying + attention and as a helpful reminder to do the other things in this + list. + +In the placement API, microversions only use the modern form of the +version header:: + + OpenStack-API-Version: placement 1.2 + +If a valid microversion is present in a request it will be placed, +as a ``Version`` object, into the WSGI environment with the +``placement.microversion`` key. Often, accessing this in handler +code directly (to control branching) is the most explicit and +granular way to have different behavior per microversion. A +``Version`` instance can be treated as a tuple of two ints and +compared as such or there is a ``matches`` method. + +In other cases there are some helper methods in the microversion +package: + +* The ``raise_http_status_code_if_not_version`` utility will raise a + http status code if the requested microversion is not within a + described version window. +* The ``version_handler`` decorator makes it possible to have + multiple different handler methods of the same (fully-qualified by + package) name, each available for a different microversion window. + If a request wants a microversion that's not available, a 404 + response is returned. There is a unit test in place which will + fail if there are version intersections. + +Adding a New Handler +==================== + +Adding a new URL or a new method (e.g, ``PATCH``) to an existing URL +requires adding a new handler function. In either case a new microversion and +release note is required. When adding an entirely new route a request for a +lower microversion should return a ``404``. When adding a new method to an +existing URL a request for a lower microversion should return a ``405``. + +In either case, the ``ROUTE_DECLARATIONS`` dictionary in the +`nova.api.openstack.placement.handler` module should be updated to point to a +function within a module that contains handlers for the type of entity +identified by the URL. Collection and individual entity handlers of the same +type should be in the same module. + +As mentioned above, the handler function should be decorated with +``@wsgi_wrapper.PlacementWsgify``, take a single argument ``req`` which is a +WebOb `Request`_ object, and return a WebOb `Response`_. + +For ``PUT`` and ``POST`` methods, request bodies are expected to be JSON +based on a content-type of ``application/json``. This may be enforced by using +a decorator: ``@util.require_content('application/json')``. If the body is not +`JSON`, a ``415`` response status is returned. + +Response bodies are usually `JSON`. A handler can check the `Accept` header +provided in a request using another decorator: +``@util.check_accept('application/json')``. If the header does not allow +`JSON`, a ``406`` response status is returned. + +`JSON` sent in a request should be validated against a JSON Schema. A +``util.extract_json`` method is available. This takes a request body and a +schema. If multiple schema are used for different microversions of the same +request, the caller is responsible for selecting the right one before calling +``extract_json``. + +When a handler needs to read or write the data store it should use methods on +the objects found in the `nova.objects.resource_provider` package. Doing so +requires a context which is provided to the handler method via the WSGI +environment. It can be retrieved as follows:: + + context = req.environ['placement.context'] + +.. note:: If your change requires new methods or new objects in the + `resource_provider` package, after you've made sure that you really + do need those new methods or objects (you may not!) make those + changes in a patch that is separate from and prior to the HTTP API + change. + +Testing of handler code is described in the next section. + +Testing +======= + +Most of the handler code in the placement API is tested using `gabbi`_. Some +utility code is tested with unit tests found in +`nova/tests/unit/api/openstack/placement/`. The back-end objects are tested +with a combination of unit and functional tests found in +`nova/tests/unit/objects/test_resource_provider.py` and +`nova/tests/functional/db`. Adding unit and non-gabbi functional tests is done +in the same way as other aspects of nova. + +Using Gabbi +----------- + +Gabbi was developed in the `telemetry`_ project to provide a declarative way to +test HTTP APIs that preserves visibility of both the request and response of +the HTTP interaction. Tests are written in YAML files where each file is an +ordered suite of tests. Fixtures (such as a database) are set up and torn down +at the beginning and end of each file, not each test. JSON response bodies can +be evaluated with `JSONPath`_. The placement WSGI +application is run via `wsgi-intercept`_, meaning that real HTTP requests are +being made over a file handle that appears to Python to be a socket. + +In the placement API the YAML files (aka "gabbits") can be found in +`nova/tests/functional/api/openstack/placement/gabbits`. Fixture definitions are +in `fixtures.py` in the parent directory. Tests are currently grouped by handlers +(e.g., `resource-provider.yaml` and `inventory.yaml`). This is not a +requirement and as we increase the number of tests it makes sense to have more +YAML files with fewer tests, divided up by the arc of API interaction that they +test. + +The gabbi tests are integrated into the functional tox target, loaded via +`nova/tests/functional/api/openstack/placement/test_placement_api.py`. If you +want to run just the gabbi tests one way to do so is:: + + tox -efunctional test_placement_api + +If you want to run just one yaml file (in this example `inventory.yaml`):: + + tox -efunctional placement_api.inventory + +It is also possible to run just one test from within one file. When you do this +every test prior to the one you asked for will also be run. This is because +the YAML represents a sequence of dependent requests. Select the test by using +the name in the yaml file, replacing space with ``_``:: + + tox -efunctional placement_api.inventory_post_new_ipv4_address_inventory + +.. note:: `.testr.conf` in the nova repository is configured such that each + gabbi YAML is considered a group. Thus, all tests in the file will + be run in the same process when running testr concurrently (the + default). + +Writing More Gabbi Tests +------------------------ + +The docs for `gabbi`_ try to be complete and explain the `syntax`_ in some +depth. Where something is missing or confusing, please log a `bug`_. + +While it is possible to test all aspects of a response (all the response +headers, the status code, every attribute in a JSON structure) in one single +test, doing so will likely make the test harder to read and will certainly make +debugging more challenging. If there are multiple things that need to be +asserted, making multiple requests is reasonable. Since database set up is only +happening once per file (instead of once per test) and since there's no TCP +overhead, the tests run quickly. + +While `fixtures`_ can be used to establish entities that are required for +tests, creating those entities via the HTTP API results in tests which are more +descriptive. For example the `inventory.yaml` file creates the resource +provider to which it will then add inventory. This makes it easy to explore a +sequence of interactions and a variety of responses with the tests: + +* create a resource provider +* confirm it has empty inventory +* add inventory to the resource provider (in a few different ways) +* confirm the resource provider now has inventory +* modify the inventory +* delete the inventory +* confirm the resource provider now has empty inventory + +Nothing special is required to add a new set of tests: create a YAML file with +a unique name in the same directory as the others. The other files can provide +examples. Gabbi can provide a useful way of doing test driven development of a +new handler: create a YAML file that describes the desired URLs and behavior +and write the code to make it pass. + +It's also possible to use gabbi against a running placement service, for +example in devstack. See `gabbi-run`_ to get started. + +Futures +======= + +Since before it was created there has been a long term goal for the placement +service to be extracted to its own repository and operate as its own +independent service. There are many reasons for this, but two main ones are: + +* Multiple projects, not just nova, will eventually need to manage resource + providers using the placement API. +* A separate service helps to maintain and preserve a strong contract between + the placement service and the consumers of the service. + +To lessen the pain of the eventual extraction of placement the service has been +developed in a way to limit dependency on the rest of the nova codebase and be +self-contained: + +* Most code is in `nova/api/openstack/placement` except for oslo versioned + object code in `nova/objects/resource_provider.py`. +* Database query code is kept within the objects. +* The methods on the objects are not remotable, as the only intended caller is + the placement API code. + +There are some exceptions to the self-contained rule (which will have to be +addressed if the extraction ever happens): + +* Exceptions unique to the placement API are still within the `nova.exceptions` + package. +* Code related to a resource class cache is within the `nova.db` package. +* Database models, migrations and tables use the nova api database. +* The nova `FaultWrapper` middleware is being used. +* `nova.i18n` package provides the ``_`` and related functions. +* `nova.conf` is used for configuration. +* Unit and functional tests depend on fixtures and other functionality in base + classes provided by nova. + +When creating new code for the placement service, please be aware of the plan +for an eventual extraction and avoid creating unnecessary interdependencies. + +.. _WSGI: https://www.python.org/dev/peps/pep-3333/ +.. _versioned objects: http://docs.openstack.org/developer/oslo.versionedobjects/ +.. _wsgify: http://docs.webob.org/en/latest/api/dec.html +.. _WebOb: http://docs.webob.org/en/latest/ +.. _Request: http://docs.webob.org/en/latest/reference.html#request +.. _Response: http://docs.webob.org/en/latest/#response +.. _microversions: http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html +.. _release note: http://docs.openstack.org/developer/reno/usage.html +.. _gabbi: https://gabbi.readthedocs.io/ +.. _telemetry: http://specs.openstack.org/openstack/telemetry-specs/specs/kilo/declarative-http-tests.html +.. _wsgi-intercept: http://wsgi-intercept.readthedocs.io/ +.. _syntax: https://gabbi.readthedocs.io/en/latest/format.html +.. _bug: https://github.com/cdent/gabbi/issues +.. _fixtures: http://gabbi.readthedocs.io/en/latest/fixtures.html +.. _JSONPath: http://goessner.net/articles/JsonPath/ +.. _gabbi-run: http://gabbi.readthedocs.io/en/latest/runner.html +.. _errors: http://specs.openstack.org/openstack/api-wg/guidelines/errors.html diff --git a/doc/source/contributor/policies.rst b/doc/source/contributor/policies.rst new file mode 100644 index 00000000000..891c29c59d6 --- /dev/null +++ b/doc/source/contributor/policies.rst @@ -0,0 +1,132 @@ +.. + 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. + +Development policies +-------------------- + +Out Of Tree Support +=================== + +While nova has many entrypoints and other places in the code that allow for +wiring in out of tree code, upstream doesn't actively make any guarantees +about these extensibility points; we don't support them, make any guarantees +about compatibility, stability, etc. + +Furthermore, hooks and extension points in the code impede efforts in Nova to +support interoperability between OpenStack clouds. Therefore an effort is +being made to systematically deprecate and remove hooks, extension points, and +classloading of managers and other services. + +Public Contractual APIs +======================== + +Although nova has many internal APIs, they are not all public contractual +APIs. Below is a link of our public contractual APIs: + +* http://developer.openstack.org/api-ref/compute/ + +Anything not in this list is considered private, not to be used outside of +nova, and should not be considered stable. + +REST APIs +========== + +Follow the guidelines set in: https://wiki.openstack.org/wiki/APIChangeGuidelines + +The canonical source for REST API behavior is the code *not* documentation. +Documentation is manually generated after the code by folks looking at the +code and writing up what they think it does, and it is very easy to get +this wrong. + +This policy is in place to prevent us from making backwards incompatible +changes to REST APIs. + +Patches and Reviews +=================== + +Merging a patch requires a non-trivial amount of reviewer resources. +As a patch author, you should try to offset the reviewer resources +spent on your patch by reviewing other patches. If no one does this, the review +team (cores and otherwise) become spread too thin. + +For review guidelines see: http://docs.openstack.org/infra/manual/developers.html#peer-review + +Reverts for Retrospective Vetos +=============================== + +Sometimes our simple "2 +2s" approval policy will result in errors. +These errors might be a bug that was missed, or equally importantly, +it might be that other cores feel that there is a need for more +discussion on the implementation of a given piece of code. + +Rather than `an enforced time-based solution`_ - for example, a patch +couldn't be merged until it has been up for review for 3 days - we have +chosen an honor-based system where core reviewers would not approve +potentially contentious patches until the proposal had been +sufficiently socialized and everyone had a chance to raise any +concerns. + +Recognising that mistakes can happen, we also have a policy where +contentious patches which were quickly approved should be reverted so +that the discussion around the proposal can continue as if the patch +had never been merged in the first place. In such a situation, the +procedure is: + +0. The commit to be reverted must not have been released. +1. The core team member who has a -2 worthy objection should propose a + revert, stating the specific concerns that they feel need + addressing. +2. Any subsequent patches depending on the to-be-reverted patch may + need to be reverted also. +3. Other core team members should quickly approve the revert. No detailed + debate should be needed at this point. A -2 vote on a revert is + strongly discouraged, because it effectively blocks the right of + cores approving the revert from -2 voting on the original patch. +4. The original patch submitter should re-submit the change, with a + reference to the original patch and the revert. +5. The original reviewers of the patch should restore their votes and + attempt to summarize their previous reasons for their votes. +6. The patch should not be re-approved until the concerns of the people + proposing the revert are worked through. A mailing list discussion or + design spec might be the best way to achieve this. + +.. _`an enforced time-based solution`: https://lists.launchpad.net/openstack/msg08574.html + +Metrics Gathering +================= + +Nova currently has a monitor plugin to gather CPU metrics on compute nodes. +This feeds into the MetricsFilter and MetricsWeigher in the scheduler. The +CPU metrics monitor is only implemented for the libvirt compute driver. +External projects like `Ceilometer`_ and `Watcher`_ consume these metrics. + +Over time people have tried to add new monitor plugins for things like memory +bandwidth. There have also been attempts to expose these monitors over CLI, +the REST API, and notifications. + +At the `Newton midcycle`_ it was decided that Nova does a poor job as a metrics +gathering tool, especially as it's incomplete, not tested, and there are +numerous other tools available to get this information as their primary +function. + +Therefore, there is a freeze on adding new metrics monitoring plugins which +also includes exposing existing monitored metrics outside of Nova, like with +the nova-manage CLI, the REST API, or the notification bus. Long-term, metrics +gathering will likely be deprecated within Nova. Since there is not yet a clear +replacement, the deprecation is open-ended, but serves as a signal that new +deployments should not rely on the metrics that Nova gathers and should instead +focus their efforts on alternative solutions for placement. + +.. _Ceilometer: http://docs.openstack.org/developer/ceilometer/ +.. _Watcher: http://docs.openstack.org/developer/watcher/ +.. _Newton midcycle: http://lists.openstack.org/pipermail/openstack-dev/2016-August/100600.html diff --git a/doc/source/process.rst b/doc/source/contributor/process.rst similarity index 81% rename from doc/source/process.rst rename to doc/source/contributor/process.rst index a34cf005cf8..897022fc777 100644 --- a/doc/source/process.rst +++ b/doc/source/contributor/process.rst @@ -18,9 +18,9 @@ Nova team process ================= Nova is always evolving its processes, but it's important to explain why we -have them: so that we can all work to ensure the interactions we need to -happen do happen. The process we have should always be there to make good -communication between all members of our community easier. +have them: so we can all work to ensure that the interactions we need to +happen do happen. The process exists to make productive communication between +all members of our community easier. OpenStack Wide Patterns ======================= @@ -29,56 +29,56 @@ Nova follows most of the generally adopted norms for OpenStack projects. You can get more details here: * http://docs.openstack.org/infra/manual/developers.html -* http://git.openstack.org/cgit/openstack/project-team-guide +* http://docs.openstack.org/project-team-guide/ If you are new to Nova, please read this first: :ref:`getting_involved`. Dates overview ============== -For Mitaka, please see: -https://wiki.openstack.org/wiki/Nova/Mitaka_Release_Schedule +For Pike, please see: +https://wiki.openstack.org/wiki/Nova/Pike_Release_Schedule -For Liberty, please see: -https://wiki.openstack.org/wiki/Nova/Liberty_Release_Schedule +.. note: Throughout this document any link which references the name of a + release cycle in the link can usually be changed to the name of the + current cycle to get up to date information. Feature Freeze ~~~~~~~~~~~~~~ -This effort is primarily to help the horizontal teams help prepare their -items for release, while at the same time giving developers time to +Feature freeze primarily provides a window of time to help the horizontal +teams prepare their items for release, while giving developers time to focus on stabilising what is currently in master, and encouraging users -and packages to perform tests (automated, and manual) on the release, to +and packagers to perform tests (automated, and manual) on the release, to spot any major bugs. -As such we have the following processes: +The Nova release process is aligned with the `development cycle schedule +`_ +used by many OpenStack projects, including the following steps. -- https://wiki.openstack.org/wiki/FeatureProposalFreeze +- Feature Proposal Freeze - - make sure all code is up for review - - so we can optimise for completed features, not lots of half - completed features + - make sure all code is up for review + - so we can optimise for completed features, not lots of half + completed features -- https://wiki.openstack.org/wiki/FeatureFreeze +- Feature Freeze - - make sure all feature code is merged + - make sure all feature code is merged -- https://wiki.openstack.org/wiki/StringFreeze +- String Freeze - - give translators time to translate all our strings - - Note: debug logs are no longer translated + - give translators time to translate all our strings + - Note: debug logs are no longer translated -- https://wiki.openstack.org/wiki/DepFreeze +- Dependency Freeze - - time to co-ordinate the final list of deps, and give packagers - time to package them - - generally it is also quite destabilising to take upgrades (beyond - bug fixes) this late + - time to co-ordinate the final list of dependencies, and give packagers + time to package them + - generally it is also quite destabilising to take upgrades (beyond + bug fixes) this late -We align with this in Nova and the dates for this release are stated -above. - -As with all processes here, there are exceptions. But the exceptions at +As with all processes here, there are exceptions. The exceptions at this stage need to be discussed with the horizontal teams that might be affected by changes beyond this point, and as such are discussed with one of the OpenStack release managers. @@ -88,24 +88,22 @@ Spec and Blueprint Approval Freeze This is a (mostly) Nova specific process. -Why do we have a Spec Freeze: +Why we have a Spec Freeze: -- specs take a long time to review, keeping it open distracts from code - reviews -- keeping them "open" and being slow at reviewing the specs (or just - ignoring them) really annoys the spec submitters +- specs take a long time to review and reviewing specs throughout the cycle + distracts from code reviews +- keeping specs "open" and being slow at reviewing them (or just + ignoring them) annoys the spec submitters - we generally have more code submitted that we can review, this time - bounding is a way to limit the number of submissions - -By the freeze date, we expect this to also be the complete list of -approved blueprints for liberty: -https://blueprints.launchpad.net/nova/liberty + bounding is a useful way to limit the number of submissions -The date listed above is when we expect all specifications for Liberty -to be merged and displayed here: -http://specs.openstack.org/openstack/nova-specs/specs/liberty/approved/ +By the freeze date, we expect all blueprints that will be approved for the +cycle to be listed on launchpad and all relevant specs to be merged. For Pike, +blueprints can be found at https://blueprints.launchpad.net/nova/pike and +specs at +https://specs.openstack.org/openstack/nova-specs/specs/pike/index.html -New in Liberty, we will keep the backlog open for submission at all +Starting with Liberty, we are keeping a backlog open for submission at all times. Note: the focus is on accepting and agreeing problem statements as being in scope, rather than queueing up work items for the next release. We are still working on a new lightweight process to get out of @@ -113,16 +111,13 @@ the backlog and approved for a particular release. For more details on backlog specs, please see: http://specs.openstack.org/openstack/nova-specs/specs/backlog/index.html -Also new in Liberty, we will allow people to submit Mitaka specs from -liberty-2 (rather than liberty-3 as normal). - There can be exceptions, usually it's an urgent feature request that comes up after the initial deadline. These will generally be discussed at the weekly Nova meeting, by adding the spec or blueprint to discuss in the appropriate place in the meeting agenda here (ideally make yourself available to discuss the blueprint, or alternatively make your case on the ML before the meeting): -https://wiki.openstack.org/wiki/Meetings/Nova +https://wiki.openstack.org/wiki/Meetings/Nova#Agenda_for_next_meeting Non-priority Feature Freeze ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -130,50 +125,43 @@ Non-priority Feature Freeze This is a Nova specific process. This only applies to low priority blueprints in this list: -https://blueprints.launchpad.net/nova/liberty +https://blueprints.launchpad.net/nova/pike We currently have a very finite amount of review bandwidth. In order to make code review time for the agreed community wide priorities, we have -to not do some other things. To this end, we are reserving liberty-3 for -priority features and bug fixes. As such, we intend not to merge any -non-priority things during liberty-3, so around liberty-2 is the -"Feature Freeze" for blueprints that are not a priority for liberty. - -For liberty, we are not aligning the Non-priority Feature Freeze with -the tagging of liberty-2. That means the liberty-2 tag will not include -some features that merge later in the week. This means, we only require -the code to be approved before the end of July 30th, we don't require it -to be merged by that date. This should help stop any gate issues -disrupting our ability to merge all the code that we have managed to get -reviewed in time. Ideally all code should be merged by the end of July -31st, but the state of the gate will determine how possible that is. +to not do some other things. In each cycle, milestones are used to bound +when certain types of work will be active and reviewed and to avoid crushing +the gate with too much code near the end of the cycle. + +For example, in the Liberty cycle, we reserved the liberty-3 milestone for +priority features and bug fixes and did not merge any non-priority things +during liberty-3. This meant that liberty-2 was the "Feature Freeze" for +blueprints that were not a priority for the Liberty cycle. You can see the list of priorities for each release: http://specs.openstack.org/openstack/nova-specs/#priorities -For things that are very close to merging, it's possible it might get an +For things that are very close to merging, it's possible to request an exception for one week after the freeze date, given the patches get enough +2s from the core team to get the code merged. But we expect this list to be zero, if everything goes to plan (no massive gate failures, -etc). For details, process see: +etc). For history of the process see: http://lists.openstack.org/pipermail/openstack-dev/2015-July/070920.html Exception process: - Please add request in here: - https://etherpad.openstack.org/p/liberty-nova-non-priority-feature-freeze + https://etherpad.openstack.org/p/pike-nova-non-priority-feature-freeze (ideally with core reviewers to sponsor your patch, normally the folks who have already viewed those patches) -- make sure you make your request before the end of Wednesday 5th - August -- nova-drivers will meet to decide what gets an exception (just like - they did last release: +- make sure you make your request before the end of the feature freeze + exception period +- nova-drivers will meet to decide what gets an exception (for some history + see: http://lists.openstack.org/pipermail/openstack-dev/2015-February/056208.html) - an initial list of exceptions (probably just a PTL compiled list at - that point) will be available for discussion during the Nova meeting - on Thursday 6th August -- the aim is to merge the code for all exceptions by the end of Monday - 10th August + that point) will be available for discussion during the next Nova meeting +- the aim is to merge the code for all exceptions early in the following week Alternatives: @@ -187,72 +175,12 @@ Alternatives: String Freeze ~~~~~~~~~~~~~ -NOTE: this is still a provisional idea - -There are general guidelines here: -https://wiki.openstack.org/wiki/StringFreeze - -But below is an experiment for Nova during liberty, to trial a new -process. There are four views onto this process. - -First, the user point of view: - -- Would like to see untranslated strings, rather than hiding - error/info/warn log messages as debug - -Second, the translators: - -- Translators will start translation without string freeze, just after - feature freeze. -- Then we have a strict string freeze date (around RC1 date) -- After at least 10 days to finish up the translations before the final - release - -Third, the docs team: - -- Config string updates often mean there is a DocImpact and docs need - updating -- best to avoid those during feature freeze, where possible - -Fourth, the developer point of view: - -- Add any translated strings before Feature Freeze -- Post Feature Freeze, allow string changes where an untranslated - string is better than no string - - - i.e. allow new log message strings, until the hard freeze - -- Post Feature Freeze, have a soft string freeze, try not to change - existing strings, where possible - - - Note: moving a string and re-using a existing string is fine, as - the tooling deals with that automatically - -- Post Hard String Freeze, there should be no extra strings to - translate - - - Assume any added strings will not be translated - - Send email about the string freeze exception in this case only, - but there should be zero of these - -So, what has changed from https://wiki.openstack.org/wiki/StringFreeze, -well: - -- no need to block new strings until much later in the cycle - - - should stop the need to rework bug fixes to remove useful log - messages - -- instead, just accept the idea of untranslated strings being better - than no strings in those cases - -So for Liberty, 21st September, so we will call 21st September the hard -freeze date, as we expect RC1 to be cut sometime after 21st September. -Note the date is fixed, it's not aligned with the cutting of RC1. - -This means we must cut another tarball (RC2 or higher) at some point -after 5th October to include new translations, even if there are no more -bug fixes, to give time before the final release on 13th-16th October. +String Freeze provides an opportunity for translators to translate user-visible +messages to a variety of languages. By not changing strings after the date of +the string freeze, the job of the translators is made a bit easier. For more +information on string and other OpenStack-wide release processes see `the +release management docs +`_. How do I get my code merged? ============================ @@ -269,7 +197,7 @@ But let's put a Nova specific twist on things... Overview ~~~~~~~~ -.. image:: ./images/Nova_spec_process.svg +.. image:: /_static/images/nova-spec-process.svg :alt: Flow chart showing the Nova bug/feature process Where do you track bugs? @@ -291,7 +219,7 @@ When do I need a blueprint vs a spec? For more details see: -- http://docs.openstack.org/developer/nova/devref/kilo.blueprints.html#when-is-a-blueprint-needed +- http://docs.openstack.org/developer/nova/blueprints.html To understand this question, we need to understand why blueprints and specs are useful. @@ -318,8 +246,6 @@ So you need your blueprint approved? Here is how: - be sure your blueprint description has enough context for the review in that meeting. - - As of Mitaka, this list is stored in an etherpad: - https://etherpad.openstack.org/p/mitaka-nova-spec-review-tracking - if you need a spec, then please submit a nova-spec for review, see: http://docs.openstack.org/infra/manual/developers.html @@ -358,7 +284,7 @@ patch: however tiny, requires a deprecation warning be issued for a cycle. - Code must be maintainable, that is it must adhere to coding standards and be as readable as possible for an average OpenStack developer - (acknowledging this person is ill-defined). + (we acknowledge that this person is not easy to define). - Patches must respect the direction of the project, for example they should not make approved specs substantially more difficult to implement. @@ -431,8 +357,6 @@ It helps to apply correct tracking information. someone else find your fix. - Make sure the bug has the correct priority and tag set: https://wiki.openstack.org/wiki/Nova/BugTriage#Step_2:_Triage_Tagged_Bugs -- If it's a trivial fix (<100 lines as a rule of thumb), add it to: - https://etherpad.openstack.org/p/liberty-nova-priorities-tracking Features ^^^^^^^^ @@ -452,7 +376,7 @@ For blueprint and spec features, do everything for blueprint-only features and also: - If it's a project or subteam priority, add it to: - https://etherpad.openstack.org/p/liberty-nova-priorities-tracking + https://etherpad.openstack.org/p/pike-nova-priorities-tracking - Ensure your spec is approved for the current release cycle. If your code is a project or subteam priority, the cores interested in @@ -560,10 +484,10 @@ to resolve in the past (currently in no particular order). We must: - ensure we get sufficient focus on the core of Nova so that we can maintain or improve the stability and flexibility of the overall codebase -- support any API we release approximately for ever. We currently - release every commit, so we're motivate to get the API right first +- support any API we release approximately forever. We currently + release every commit, so we're motivated to get the API right the first time -- avoid low priority blueprints slowing work on high priority work, +- avoid low priority blueprints that slow work on high priority work, without blocking those forever - focus on a consistent experience for our users, rather than ease of development @@ -675,7 +599,7 @@ they want to get landed. This is a key part of being an Open community. Why is there a Feature Freeze (and String Freeze) in Nova? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The main reason Nova has a feature freeze is that it gives people +The main reason Nova has a feature freeze is that it allows people working on docs and translations to sync up with the latest code. Traditionally this happens at the same time across multiple projects, so the docs are synced between what used to be called the "integrated @@ -729,9 +653,10 @@ Why do you still use Launchpad? We are actively looking for an alternative to Launchpad's bugs and blueprints. -Originally the idea was to create Storyboard. However the development -has stalled. A more likely front runner is this: -http://phabricator.org/applications/projects/ +Originally the idea was to create Storyboard. However development +stalled for a while so interest waned. The project has become more active +recently so it may be worth looking again: +https://storyboard.openstack.org/#!/page/about When should I submit my spec? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -769,7 +694,7 @@ Thirdly, is it in merge conflict with master or are any of the CI tests failing? Particularly any third-party CI tests that are relevant to the code you are changing. If you're fixing something that only occasionally failed before, maybe recheck a few times to prove the tests stay -passing. Without green tests, reviews tend to move on and look at the +passing. Without green tests, reviewers tend to move on and look at the other patches that have the tests passing. OK, so you have followed all the process (i.e. your patches are getting @@ -851,13 +776,13 @@ Subteam recommendation as a +2 There are groups of people with great knowledge of particular bits of the code base. It may be a good idea to give their recommendation of a -merge. In addition, having the subteam focus review efforts on a subset -of patches should help concentrate the nova-core reviews they get, and -increase the velocity of getting code merged. +merge greater strength. In addition, having the subteam focus review efforts +on a subset of patches should help concentrate the nova-core reviews they +get, and increase the velocity of getting code merged. The first part is for subgroups to show they can do a great job of recommending patches. This is starting in here: -https://etherpad.openstack.org/p/liberty-nova-priorities-tracking +https://etherpad.openstack.org/p/pike-nova-priorities-tracking Ideally this would be done with gerrit user "tags" rather than an etherpad. There are some investigations by sdague in how feasible it @@ -913,7 +838,7 @@ Runways ~~~~~~~ Runways are a form of Kanban, where we look at optimising the flow -through the system, by ensure we focus our efforts on reviewing a +through the system, by ensuring we focus our efforts on reviewing a specific subset of patches. The idea goes something like this: @@ -1004,5 +929,3 @@ Main benefits: are added - allows a way to add experimental things into Nova, and track either their removal or maturation - -* https://wiki.openstack.org/wiki/Nova/Mitaka_Release_Schedule diff --git a/doc/source/project_scope.rst b/doc/source/contributor/project-scope.rst similarity index 100% rename from doc/source/project_scope.rst rename to doc/source/contributor/project-scope.rst diff --git a/doc/source/contributor/releasenotes.rst b/doc/source/contributor/releasenotes.rst new file mode 100644 index 00000000000..db40ad8b4a9 --- /dev/null +++ b/doc/source/contributor/releasenotes.rst @@ -0,0 +1,65 @@ +.. _releasenotes: + +Release Notes +============= + +What is reno ? +-------------- + +Nova uses `reno `_ for +providing release notes in-tree. That means that a patch can include a *reno +file* or a series can have a follow-on change containing that file explaining +what the impact is. + +A *reno file* is a YAML file written in the releasenotes/notes tree which is +generated using the reno tool this way: + +.. code-block:: bash + + $ tox -e venv -- reno new + +where usually ```` can be ``bp-`` for a +blueprint or ``bug-XXXXXX`` for a bugfix. + +Refer to the `reno documentation `_ +for the full list of sections. + + +When a release note is needed +----------------------------- + +A release note is required anytime a reno section is needed. Below are some +examples for each section. Any sections that would be blank should be left out +of the note file entirely. If no section is needed, then you know you don't +need to provide a release note :-) + +* ``upgrade`` + * The patch has an `UpgradeImpact `_ tag + * A DB change needs some deployer modification (like a migration) + * A configuration option change (deprecation, removal or modified default) + * some specific changes that have a `DocImpact `_ tag + but require further action from an deployer perspective + * any patch that requires an action from the deployer in general + +* ``security`` + * If the patch fixes a known vulnerability + +* ``features`` + * If the patch has an `APIImpact `_ tag + * For nova-manage and python-novaclient changes, if it adds or changes a + new command, including adding new options to existing commands + * not all blueprints in general, just the ones impacting a `contractual API `_ + * a new virt driver is provided or an existing driver impacts the `HypervisorSupportMatrix `_ + +* ``critical`` + * Bugfixes categorized as Critical in Launchpad *impacting users* + +* ``fixes`` + * No clear definition of such bugfixes. Hairy long-standing bugs with high + importance that have been fixed are good candidates though. + + +Three sections are left intentionally unexplained (``prelude``, ``issues`` and +``other``). Those are targeted to be filled in close to the release time for +providing details about the soon-ish release. Don't use them unless you know +exactly what you are doing. diff --git a/doc/source/contributor/testing.rst b/doc/source/contributor/testing.rst new file mode 100644 index 00000000000..1258a7df623 --- /dev/null +++ b/doc/source/contributor/testing.rst @@ -0,0 +1,110 @@ +.. + 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. + +============== +Test Strategy +============== + +A key part of the "four opens" is ensuring the OpenStack delivers well-tested +and usable software. For more details see: +http://docs.openstack.org/project-team-guide/introduction.html#the-four-opens + +Experience has shown that untested features are frequently broken, in part +due to the velocity of upstream changes. As we aim to ensure we keep all +features working across upgrades, we must aim to test all features. + +Reporting Test Coverage +======================= + +For details on plans to report the current test coverage, refer to +:doc:`/user/feature-classification`. + +Running tests and reporting results +=================================== + +Voting in Gerrit +---------------- + +On every review in gerrit, check tests are run on very patch set, and are +able to report a +1 or -1 vote. +For more details, please see: +http://docs.openstack.org/infra/manual/developers.html#automated-testing + +Before merging any code, there is an integrate gate test queue, to ensure +master is always passing all tests. +For more details, please see: +http://docs.openstack.org/infra/zuul/gating.html + +Infra vs Third-Party +-------------------- + +Tests that use fully open source components are generally run by the +OpenStack Infra teams. Test setups that use non-open technology must +be run outside of that infrastructure, but should still report their +results upstream. + +For more details, please see: +http://docs.openstack.org/infra/system-config/third_party.html + +Ad-hoc testing +-------------- + +It is particularly common for people to run ad-hoc tests on each released +milestone, such as RC1, to stop regressions. +While these efforts can help stabilize the release, as a community we have a +much stronger preference for continuous integration testing. Partly this is +because we encourage users to deploy master, and we generally have to assume +that any upstream commit may already been deployed in production. + +Types of tests +============== + +Unit tests +---------- + +Unit tests help document and enforce the contract for each component. +Without good unit test coverage it is hard to continue to quickly evolve the +codebase. +The correct level of unit test coverage is very subjective, and as such we are +not aiming for a particular percentage of coverage, rather we are aiming for +good coverage. +Generally, every code change should have a related unit test: +http://docs.openstack.org/developer/hacking/#creating-unit-tests + +Integration tests +----------------- + +Today, our integration tests involve running the Tempest test suite on a +variety of Nova deployment scenarios. + +In addition, we have third parties running the tests on their preferred Nova +deployment scenario. + +Functional tests +---------------- + +Nova has a set of in-tree functional tests that focus on things that are out +of scope for tempest testing and unit testing. +Tempest tests run against a full live OpenStack deployment, generally deployed +using devstack. At the other extreme, unit tests typically use mock to test a +unit of code in isolation. +Functional tests don't run an entire stack, they are isolated to nova code, +and have no reliance on external services. They do have a WSGI app, nova +services and a database, with minimal stubbing of nova internals. + +Interoperability tests +----------------------- + +The DefCore committee maintains a list that contains a subset of Tempest tests. +These are used to verify if a particular Nova deployment's API responds as +expected. For more details, see: https://github.com/openstack/defcore diff --git a/doc/source/contributor/testing/libvirt-numa.rst b/doc/source/contributor/testing/libvirt-numa.rst new file mode 100644 index 00000000000..ac91689bca9 --- /dev/null +++ b/doc/source/contributor/testing/libvirt-numa.rst @@ -0,0 +1,696 @@ +================================================ +Testing NUMA related hardware setup with libvirt +================================================ + +This page describes how to test the libvirt driver's handling of the NUMA +placement, large page allocation and CPU pinning features. It relies on setting +up a virtual machine as the test environment and requires support for nested +virtualization since plain QEMU is not sufficiently functional. The virtual +machine will itself be given NUMA topology, so it can then act as a virtual +"host" for testing purposes. + +------------------------------------------ +Provisioning a virtual machine for testing +------------------------------------------ + +The entire test process will take place inside a large virtual machine running +Fedora 24. The instructions should work for any other Linux distribution which +includes libvirt >= 1.2.9 and QEMU >= 2.1.2 + +The tests will require support for nested KVM, which is not enabled by default +on hypervisor hosts. It must be explicitly turned on in the host when loading +the kvm-intel/kvm-amd kernel modules. + +On Intel hosts verify it with + +.. code-block:: bash + + # cat /sys/module/kvm_intel/parameters/nested + N + + # rmmod kvm-intel + # echo "options kvm-intel nested=y" > /etc/modprobe.d/dist.conf + # modprobe kvm-intel + + # cat /sys/module/kvm_intel/parameters/nested + Y + +While on AMD hosts verify it with + +.. code-block:: bash + + # cat /sys/module/kvm_amd/parameters/nested + 0 + + # rmmod kvm-amd + # echo "options kvm-amd nested=1" > /etc/modprobe.d/dist.conf + # modprobe kvm-amd + + # cat /sys/module/kvm_amd/parameters/nested + 1 + +The virt-install command below shows how to provision a basic Fedora 24 x86_64 +guest with 8 virtual CPUs, 8 GB of RAM and 20 GB of disk space: + +.. code-block:: bash + + # cd /var/lib/libvirt/images + # wget https://download.fedoraproject.org/pub/fedora/linux/releases/24/Server/x86_64/iso/Fedora-Server-netinst-x86_64-24-1.2.iso + + # virt-install \ + --name f24x86_64 \ + --ram 8000 \ + --vcpus 8 \ + --file /var/lib/libvirt/images/f24x86_64.img \ + --file-size 20 + --cdrom /var/lib/libvirt/images/Fedora-Server-netinst-x86_64-24-1.2.iso \ + --os-variant fedora23 + +When the virt-viewer application displays the installer, follow the defaults +for the installation with a couple of exceptions: + +* The automatic disk partition setup can be optionally tweaked to reduce the + swap space allocated. No more than 500MB is required, free'ing up an extra + 1.5 GB for the root disk + +* Select "Minimal install" when asked for the installation type since a desktop + environment is not required + +* When creating a user account be sure to select the option "Make this user + administrator" so it gets 'sudo' rights + +Once the installation process has completed, the virtual machine will reboot +into the final operating system. It is now ready to deploy an OpenStack +development environment. + +--------------------------------- +Setting up a devstack environment +--------------------------------- + +For later ease of use, copy your SSH public key into the virtual machine: + +.. code-block:: bash + + # ssh-copy-id + +Now login to the virtual machine: + +.. code-block:: bash + + # ssh + +The Fedora minimal install does not contain git. Install git and clone the +devstack repo: + +.. code-block:: bash + + $ sudo dnf -y install git + $ git clone git://github.com/openstack-dev/devstack.git + $ cd devstack + +At this point a fairly standard devstack setup can be done with one exception: +we should enable the ``NUMATopologyFilter`` filter, which we will use later. +For example: + +.. code-block:: bash + + $ cat >>local.conf < select numa_topology from compute_nodes; + +----------------------------------------------------------------------------+ + | numa_topology | + +----------------------------------------------------------------------------+ + | { + | "nova_object.name": "NUMATopology", + | "nova_object.data": { + | "cells": [{ + | "nova_object.name": "NUMACell", + | "nova_object.data": { + | "cpu_usage": 0, + | "memory_usage": 0, + | "cpuset": [0, 1, 2, 3, 4, 5, 6, 7], + | "pinned_cpus": [], + | "siblings": [], + | "memory": 7793, + | "mempages": [ + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 987430, + | "reserved":0, + | "size_kb": 4 + | }, + | }, + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 0, + | "reserved":0, + | "size_kb": 2048 + | }, + | }, + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 0, + | "reserved": 0, + | "size_kb": 1048576 + | }, + | } + | ], + | "id": 0 + | }, + | }, + | ] + | }, + | } + +----------------------------------------------------------------------------+ + +Meanwhile, the guest instance should not have any NUMA configuration recorded: + +.. code-block:: bash + + MariaDB [nova]> select numa_topology from instance_extra; + +---------------+ + | numa_topology | + +---------------+ + | NULL | + +---------------+ + +----------------------------------------------------- +Reconfiguring the test instance to have NUMA topology +----------------------------------------------------- + +Now that devstack is proved operational, it is time to configure some NUMA +topology for the test VM, so that it can be used to verify the OpenStack NUMA +support. To do the changes, the VM instance that is running devstack must be +shut down: + +.. code-block:: bash + + $ sudo shutdown -h now + +And now back on the physical host edit the guest config as root: + +.. code-block:: bash + + $ sudo virsh edit f21x86_64 + +The first thing is to change the `` block to do passthrough of the host +CPU. In particular this exposes the "SVM" or "VMX" feature bits to the guest so +that "Nested KVM" can work. At the same time we want to define the NUMA +topology of the guest. To make things interesting we're going to give the guest +an asymmetric topology with 4 CPUS and 4 GBs of RAM in the first NUMA node and +2 CPUs and 2 GB of RAM in the second and third NUMA nodes. So modify the guest +XML to include the following CPU XML: + +.. code-block:: xml + + + + + + + + + +Now start the guest again: + +.. code-block:: bash + + # virsh start f24x86_64 + +...and login back in: + +.. code-block:: bash + + # ssh + +Before starting OpenStack services again, it is necessary to explicitly set the +libvirt virtualization type to KVM, so that guests can take advantage of nested +KVM: + +.. code-block:: bash + + $ sudo sed -i 's/virt_type = qemu/virt_type = kvm/g' /etc/nova/nova.conf + +With that done, OpenStack can be started again: + +.. code-block:: bash + + $ cd devstack + $ ./stack.sh + +The first thing is to check that the compute node picked up the new NUMA +topology setup for the guest: + +.. code-block:: bash + + $ mysql -u root -p123456 nova + MariaDB [nova]> select numa_topology from compute_nodes; + +----------------------------------------------------------------------------+ + | numa_topology | + +----------------------------------------------------------------------------+ + | { + | "nova_object.name": "NUMATopology", + | "nova_object.data": { + | "cells": [ + | { + | "nova_object.name": "NUMACell", + | "nova_object.data": { + | "cpu_usage": 0, + | "memory_usage": 0, + | "cpuset": [0, 1, 2, 3], + | "pinned_cpus": [], + | "siblings": [], + | "memory": 3856, + | "mempages": [ + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 987231, + | "reserved": 0, + | "size_kb": 4 + | }, + | }, + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 0, + | "reserved": 0, + | "size_kb": 2048 + | }, + | }, + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 0, + | "reserved": 0, + | "size_kb": 1048576 + | }, + | } + | ], + | "id": 0 + | }, + | }, + | { + | "nova_object.name": "NUMACell", + | "nova_object.data": { + | "cpu_usage": 0, + | "memory_usage": 0, + | "cpuset": [4, 5], + | "pinned_cpus": [], + | "siblings": [], + | "memory": 1969, + | "mempages": [ + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 504202, + | "reserved": 0, + | "size_kb": 4 + | }, + | }, + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 0, + | "reserved": 0, + | "size_kb": 2048 + | }, + | }, + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 0, + | "reserved": 0, + | "size_kb": 1048576 + | }, + | } + | ], + | "id": 1 + | }, + | }, + | { + | "nova_object.name": "NUMACell", + | "nova_object.data": { + | "cpu_usage": 0, + | "memory_usage": 0, + | "cpuset": [6, 7], + | "pinned_cpus": [], + | "siblings": [], + | "memory": 1967, + | "mempages": [ + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 503565, + | "reserved": 0, + | "size_kb": 4 + | }, + | }, + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 0, + | "reserved": 0, + | "size_kb": 2048 + | }, + | }, + | { + | "nova_object.name": "NUMAPagesTopology", + | "nova_object.data": { + | "used": 0, + | "total": 0, + | "reserved": 0, + | "size_kb": 1048576 + | }, + | } + | ], + | "id": 2 + | }, + | } + | ] + | }, + | } + +----------------------------------------------------------------------------+ + +This indeed shows that there are now 3 NUMA nodes for the "host" machine, the +first with 4 GB of RAM and 4 CPUs, and others with 2 GB of RAM and 2 CPUs each. + +----------------------------------------------------- +Testing instance boot with no NUMA topology requested +----------------------------------------------------- + +For the sake of backwards compatibility, if the NUMA filter is enabled, but the +flavor/image does not have any NUMA settings requested, it should be assumed +that the guest will have a single NUMA node. The guest should be locked to a +single host NUMA node too. Boot a guest with the `m1.tiny` flavor to test this +condition: + +.. code-block:: bash + + $ . openrc admin admin + $ openstack server create --image cirros-0.3.4-x86_64-uec --flavor m1.tiny \ + cirros1 + +Now look at the libvirt guest XML: + +.. code-block:: bash + + $ sudo virsh list + Id Name State + ---------------------------------------------------- + 1 instance-00000001 running + $ sudo virsh dumpxml instance-00000001 + ... + 1 + ... + +This example shows that there is no explicit NUMA topology listed in the guest +XML. + +------------------------------------------------ +Testing instance boot with 1 NUMA cell requested +------------------------------------------------ + +Moving forward a little, explicitly tell nova that the NUMA topology for the +guest should have a single NUMA node. This should operate in an identical +manner to the default behavior where no NUMA policy is set. To define the +topology we will create a new flavor: + +.. code-block:: bash + + $ openstack flavor create --ram 1024 --disk 1 --vcpus 4 m1.numa + $ openstack flavor set --property hw:numa_nodes=1 m1.numa + $ openstack flavor show m1.numa + +Now boot the guest using this new flavor: + +.. code-block:: bash + + $ openstack server create --image cirros-0.3.4-x86_64-uec --flavor m1.numa \ + cirros2 + +Looking at the resulting guest XML from libvirt: + +.. code-block:: bash + + $ sudo virsh list + Id Name State + ---------------------------------------------------- + 1 instance-00000001 running + 2 instance-00000002 running + $ sudo virsh dumpxml instance-00000002 + ... + 4 + + + + + + + + ... + + + + + + + ... + + + + + +The XML shows: + +* Each guest CPU has been pinned to the physical CPUs associated with a + particular NUMA node + +* The emulator threads have been pinned to the union of all physical CPUs in + the host NUMA node that the guest is placed on + +* The guest has been given a virtual NUMA topology with a single node holding + all RAM and CPUs + +* The guest NUMA node has been strictly pinned to a host NUMA node. + +As a further sanity test, check what nova recorded for the instance in the +database. This should match the ```` information: + +.. code-block:: bash + + $ mysql -u root -p123456 nova + MariaDB [nova]> select numa_topology from instance_extra; + +----------------------------------------------------------------------------+ + | numa_topology | + +----------------------------------------------------------------------------+ + | { + | "nova_object.name": "InstanceNUMATopology", + | "nova_object.data": { + | "cells": [ + | { + | "nova_object.name": "InstanceNUMACell", + | "nova_object.data": { + | "pagesize": null, + | "cpu_topology": null, + | "cpuset": [0, 1, 2, 3], + | "cpu_policy": null, + | "memory": 1024, + | "cpu_pinning_raw": null, + | "id": 0, + | "cpu_thread_policy": null + | }, + | } + | ] + | }, + | } + +----------------------------------------------------------------------------+ + +Delete this instance: + +.. code-block:: bash + + $ openstack server delete cirros2 + +------------------------------------------------- +Testing instance boot with 2 NUMA cells requested +------------------------------------------------- + +Now getting more advanced we tell nova that the guest will have two NUMA nodes. +To define the topology we will change the previously defined flavor: + +.. code-block:: bash + + $ openstack flavor set --property hw:numa_nodes=2 m1.numa + $ openstack flavor show m1.numa + +Now boot the guest using this changed flavor: + +.. code-block:: bash + + $ openstack server create --image cirros-0.3.4-x86_64-uec --flavor m1.numa \ + cirros2 + +Looking at the resulting guest XML from libvirt: + +.. code-block:: bash + + $ sudo virsh list + Id Name State + ---------------------------------------------------- + 1 instance-00000001 running + 3 instance-00000003 running + $ sudo virsh dumpxml instance-00000003 + ... + 4 + + + + + + + + ... + + + + + + + + ... + + + + + + +The XML shows: + +* Each guest CPU has been pinned to the physical CPUs associated with + particular NUMA nodes + +* The emulator threads have been pinned to the union of all physical CPUs in + the host NUMA nodes that the guest is placed on + +* The guest has been given a virtual NUMA topology with two nodes, each holding + half the RAM and CPUs + +* The guest NUMA nodes have been strictly pinned to different host NUMA node + +As a further sanity test, check what nova recorded for the instance in the +database. This should match the ```` information: + +.. code-block:: bash + + MariaDB [nova]> select numa_topology from instance_extra; + +----------------------------------------------------------------------------+ + | numa_topology | + +----------------------------------------------------------------------------+ + | { + | "nova_object.name": "InstanceNUMATopology", + | "nova_object.data": { + | "cells": [ + | { + | "nova_object.name": "InstanceNUMACell", + | "nova_object.data": { + | "pagesize": null, + | "cpu_topology": null, + | "cpuset": [0, 1], + | "cpu_policy": null, + | "memory": 512, + | "cpu_pinning_raw": null, + | "id": 0, + | "cpu_thread_policy": null + | }, + | }, + | { + | "nova_object.name": "InstanceNUMACell", + | "nova_object.data": { + | "pagesize": null, + | "cpu_topology": null, + | "cpuset": [2, 3], + | "cpu_policy": null, + | "memory": 512, + | "cpu_pinning_raw": null, + | "id": 1, + | "cpu_thread_policy": null + | }, + | } + | ] + | }, + | } + +----------------------------------------------------------------------------+ diff --git a/doc/source/testing/serial-console.rst b/doc/source/contributor/testing/serial-console.rst similarity index 100% rename from doc/source/testing/serial-console.rst rename to doc/source/contributor/testing/serial-console.rst diff --git a/doc/source/contributor/testing/zero-downtime-upgrade.rst b/doc/source/contributor/testing/zero-downtime-upgrade.rst new file mode 100644 index 00000000000..097d72a6ba6 --- /dev/null +++ b/doc/source/contributor/testing/zero-downtime-upgrade.rst @@ -0,0 +1,221 @@ +===================================== +Testing Zero Downtime Upgrade Process +===================================== + +Zero Downtime upgrade eliminates any disruption to nova API service +during upgrade. + +Nova API services are upgraded at the end. The basic idea of the zero downtime +upgrade process is to have the connections drain from the old API before +being upgraded. In this process, new connections go to the new API nodes +while old connections slowly drain from the old nodes. This ensures that the +user sees the max_supported API version as a monotonically increasing number. +There might be some performance degradation during the process due to slow +HTTP responses and delayed request handling, but there is no API downtime. + +This page describes how to test the zero downtime upgrade process. + +----------- +Environment +----------- + +* Multinode devstack environment with 2 nodes: + * controller - All services (N release) + * compute-api - Only n-cpu and n-api services (N release) + +* Highly available load balancer (HAProxy) on top of the n-api services. + This is required for zero downtime upgrade as it allows one n-api service + to run while we upgrade the other. See instructions to setup HAProxy below. + +----------------------------- +Instructions to setup HAProxy +----------------------------- + + +Install HAProxy and Keepalived on both nodes. + +.. code-block:: bash + + # apt-get install haproxy keepalived + +Let the kernel know that we intend to bind additional IP addresses that +won't be defined in the interfaces file. To do this, edit ``/etc/sysctl.conf`` +and add the following line: + +.. code-block:: INI + + net.ipv4.ip_nonlocal_bind=1 + +Make this take effect without rebooting. + +.. code-block:: bash + + # sysctl -p + +Configure HAProxy to add backend servers and assign virtual IP to the frontend. +On both nodes add the below HAProxy config: + +.. code-block:: bash + + # cd /etc/haproxy + # cat >> haproxy.cfg <> + default_backend nova-api + + backend nova-api + balance roundrobin + option tcplog + server controller 192.168.0.88:8774 check + server apicomp 192.168.0.89:8774 check + + EOF + +.. note:: + Just change the IP for log in the global section on each node. + +On both nodes add ``keepalived.conf``: + +.. code-block:: bash + + # cd /etc/keepalived + # cat >> keepalived.conf <> 0" | sudo socat /var/run/haproxy.sock stdio + +* OR disable service using: + + .. code-block:: bash + + # echo "disable server nova-api/<>" | sudo socat /var/run/haproxy.sock stdio + +* This allows the current node to complete all the pending requests. When this + is being upgraded, other api node serves the requests. This way we can + achieve zero downtime. +* Restart n-api service and enable n-api using the command: + + .. code-block:: bash + + # echo "enable server nova-api/<>" | sudo socat /var/run/haproxy.sock stdio + +* Drain connections from other old api node in the same way and upgrade. +* No tempest tests should fail since there is no API downtime. + +After maintenance window +''''''''''''''''''''''''' + +* Follow the steps from general rolling upgrade process to clear any cached + service version data and complete all online data migrations. diff --git a/doc/source/development.environment.rst b/doc/source/development.environment.rst deleted file mode 100644 index ce5d044b001..00000000000 --- a/doc/source/development.environment.rst +++ /dev/null @@ -1,182 +0,0 @@ -.. - Copyright 2010-2011 United States Government as represented by the - Administrator of the National Aeronautics and Space Administration. - 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. - -======================= -Development Quickstart -======================= - -This page describes how to setup and use a working Python development -environment that can be used in developing nova on Ubuntu, Fedora or -Mac OS X. These instructions assume you're already familiar with git. - -Following these instructions will allow you to build the documentation -and run the nova unit tests. If you want to be able to run nova (i.e., -launch VM instances), you will also need to --- either manually or by -letting DevStack do it for you --- install libvirt and at least one of -the `supported hypervisors`_. Running nova is currently only supported -on Linux, although you can run the unit tests on Mac OS X. - -.. _supported hypervisors: http://wiki.openstack.org/HypervisorSupportMatrix - - -.. note:: For how to contribute to Nova, see - HowToContribute_. - Nova uses the Gerrit code review system, GerritWorkflow_. - -.. _GerritWorkflow: http://docs.openstack.org/infra/manual/developers.html#development-workflow -.. _HowToContribute: http://docs.openstack.org/infra/manual/developers.html -.. _`docs.openstack.org`: http://docs.openstack.org - -Setup -===== - -There are two ways to create a development environment: using -DevStack, or explicitly installing and cloning just what you need. - - -Using DevStack --------------- - -See `Devstack`_ Documentation. If you would like to use Vagrant, there is a `Vagrant`_ for DevStack. - -.. _`Devstack`: http://docs.openstack.org/developer/devstack/ -.. _`Vagrant`: https://github.com/openstack-dev/devstack-vagrant/blob/master/README.md - -.. - Until the vagrant markdown documents are rendered somewhere on .openstack.org, linking to github - -Explicit Install/Clone ----------------------- - -DevStack installs a complete OpenStack environment. Alternatively, -you can explicitly install and clone just what you need for Nova -development. - -The first step of this process is to install the system (not Python) -packages that are required. Following are instructions on how to do -this on Linux and on the Mac. - -Linux Systems -````````````` - -.. note:: - - This section is tested for Nova on Ubuntu (14.04-64) and - Fedora-based (RHEL 6.1) distributions. Feel free to add notes and - change according to your experiences or operating system. - -Install the prerequisite packages. - -On Ubuntu:: - - sudo apt-get install python-dev libssl-dev python-pip git-core libxml2-dev libxslt-dev pkg-config libffi-dev libpq-dev libmysqlclient-dev graphviz libsqlite3-dev python-tox python3-dev python3 gettext - -On Fedora-based distributions (e.g., Fedora/RHEL/CentOS/Scientific Linux):: - - sudo yum install python-devel openssl-devel python-pip git gcc libxslt-devel mysql-devel postgresql-devel libffi-devel libvirt-devel graphviz sqlite-devel python3-devel python3 gettext - sudo pip-python install tox - -On openSUSE-based distributions (SLES 12, openSUSE 13.1, Factory or Tumbleweed):: - - sudo zypper in gcc git libffi-devel libmysqlclient-devel libvirt-devel libxslt-devel postgresql-devel python-devel python-pip python-tox python-virtualenv python3-devel python3 gettext-runtime - - -Mac OS X Systems -```````````````` - -Install virtualenv:: - - sudo easy_install virtualenv - -Check the version of OpenSSL you have installed:: - - openssl version - -The stock version of OpenSSL that ships with Mac OS X 10.6 (OpenSSL 0.9.8l) -or Mac OS X 10.7 (OpenSSL 0.9.8r) or Mac OS X 10.10.3 (OpenSSL 0.9.8zc) works -fine with nova. OpenSSL versions from brew like OpenSSL 1.0.1k work fine -as well. - - -Getting the code -```````````````` - -Once you have the prerequisite system packages installed, the next -step is to clone the code. - -Grab the code from git:: - - git clone https://git.openstack.org/openstack/nova - cd nova - - -Building the Documentation -========================== - -Install the prerequisite packages: graphviz - -To do a full documentation build, issue the following command while -the nova directory is current. - -.. code-block:: bash - - tox -edocs - -That will create a Python virtual environment, install the needed -Python prerequisites in that environment, and build all the -documentation in that environment. - -Running unit tests -================== - -See `Running Python Unit Tests`_. - -.. _`Running Python Unit Tests`: http://docs.openstack.org/infra/manual/python.html#running-python-unit-tests - -Using a remote debugger -======================= - -Some modern IDE such as pycharm (commercial) or Eclipse (open source) support remote debugging. In order to run nova with remote debugging, start the nova process -with the following parameters ---remote_debug-host ---remote_debug-port - -Before you start your nova process, start the remote debugger using the instructions for that debugger. -For pycharm - http://blog.jetbrains.com/pycharm/2010/12/python-remote-debug-with-pycharm/ -For Eclipse - http://pydev.org/manual_adv_remote_debugger.html - -More detailed instructions are located here - http://novaremotedebug.blogspot.com - -Using fake computes for tests -============================= - -The number of instances supported by fake computes is not limited by physical -constraints. It allows you to perform stress tests on a deployment with few -resources (typically a laptop). But you must avoid using scheduler filters -limiting the number of instances per compute (like RamFilter, DiskFilter, -AggregateCoreFilter), otherwise they will limit the number of instances per -compute. - - -Fake computes can also be used in multi hypervisor-type deployments in order to -take advantage of fake and "real" computes during tests: - -* create many fake instances for stress tests -* create some "real" instances for functional tests - -Fake computes can be used for testing Nova itself but also applications on top -of it. diff --git a/doc/source/feature_classification.rst b/doc/source/feature_classification.rst deleted file mode 100644 index d51dadc054c..00000000000 --- a/doc/source/feature_classification.rst +++ /dev/null @@ -1,172 +0,0 @@ -.. - 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. - -====================== -Feature Classification -====================== - -This document aims to define how we describe features listed in the -:doc:`support-matrix`. - -Aims -==== - -Our users want the features they rely on to be reliable and always continue -to solve for their use case. -When things break, users request that we solve their issues quickly. -It would be better if we never had those regressions in the first place. - -We are taking a two-pronged approach: - -* Tell our users what features are complete, well-documented, and are kept - stable by good tests. They will get a good experience if they stick to - using those features. - Please note that the tests are specific to particular combinations of - technologies. A deployment's choice of storage, networking and - hypervisor makes a big difference to what features will work. - -* Get help for the features that are not in the above state, and warn our - users about the risks of using those features before they are ready. - It should make it much clearer how to help improve the feature. - -Concepts -======== - -Some definitions to help understand the later part of the document. - -Users ------ - -These are the users we will talk about in this document: - -* application deployer: creates/deletes servers, directly or indirect via API -* application developer: creates images and apps that run on the cloud -* cloud operator: administers the cloud -* self service administrator: both runs and uses the cloud - -Now in reality the picture is way more complex. Specifically, there are -likely to be different roles for observer, creator and admin roles for -the application developer. Similarly, there are likely to be various -levels of cloud operator permissions, some read only, see a subset of -tenants, etc. - -Note: this is not attempting to be an exhaustive set of personas that consider -various facets of the different users, but instead aims to be a minimal set of -users, such that we use a consistent terminology throughout this document. - -Feature Group -------------- - -To reduce the size of the matrix, we organize the features into groups. -Each group maps to a set of user stories, that can be validated by a set -of scenarios, tests. Typically, this means a set of tempest tests. - -This list focuses on API concepts like attach and detach volumes, rather -than deployment specific concepts like attach iSCSI volume to KVM based VM. - -Deployment ----------- - -A deployment maps to a specific test environment. A full description of the -environment should be provided, so its possible to reproduce the test results -that are reported for each of the Feature Groups. - -Note: this description includes all aspects of the deployment: -the hypervisor, the number of nova-compute services, the storage being used, -the network driver being used, the types of images being tested, etc. - -Feature Group Maturity ------------------------ - -The Feature Group Maturity rating is specific to the API concepts, rather than -specific to a particular deployment. That detail is covered in the deployment -rating for each feature group. - -We are starting out these Feature Group ratings: - -* Incomplete -* Experimental -* Complete -* Complete and Required -* Deprecated (scheduled to be removed in a future release) - -Incomplete features are those that don't have enough functionality to satisfy -real world use cases. - -Experimental features should be used with extreme caution. -They are likely to have little or no upstream testing. -With little testing there are likely to be many unknown bugs. - -For a feature to be considered complete, we must have: - -* Complete API docs (concept and REST call definition) -* Complete Administrator docs -* Tempest tests that define if the feature works correctly -* Has enough functionality, and works reliably enough to be useful - in real world scenarios -* Unlikely to ever have a reason to drop support for the feature - -There are various reasons why a feature, once complete, becomes required, but -currently its largely when a feature is supported by all drivers. Note that -any new drivers need to prove they support all required features before it -would be allowed in upstream Nova. -Please note that this list is technically unrelated to the DefCore -effort, despite there being obvious parallels that could be drawn. - -Required features are those that any new technology must support before -being allowed into tree. The larger the list, the more features can be -expected to be available on all Nova based clouds. - -Deprecated features are those that are scheduled to be removed in a future -major release of Nova. If a feature is marked as complete, it should -never be deprecated. -If a feature is incomplete or experimental for several releases, -it runs the risk of being deprecated, and later removed from the code base. - -Deployment Rating for a Feature Group --------------------------------------- - -The deployment rating is purely about the state of the tests for each -Feature Group on a particular deployment. - -There will the following ratings: - -* unknown -* not implemented -* implemented: self declare the tempest tests pass -* regularly tested: tested by third party CI -* checked: Tested as part of the check or gate queue - -The eventual goal is to automate this list from some third party CI reporting -system, but so we can make progress, this will be a manual inspection that is -documented by an hand written ini file. Ideally, this will be reviewed every -milestone. - -Feature Group Definitions -========================= - -This is a look at features targeted at application developers, and the current -state of each feature, independent of the specific deployment. - -Please note: this is still a work in progress! - -Key TODOs: - -* use new API docs as a template for the feature groups, into ini file -* add lists of tempest UUIDs for each group -* link from hypervisor support matrix into feature group maturity ratings -* add maturity rating into the feature groups, with a justification, which - is likely to include lints to API docs, etc -* replace tick and cross in support matrix with "deployment ratings" -* eventually generate the tick and cross from live, historical, CI results - diff --git a/doc/source/figures/bb-cinder-fig1.png b/doc/source/figures/bb-cinder-fig1.png new file mode 100644 index 00000000000..022d3652a17 Binary files /dev/null and b/doc/source/figures/bb-cinder-fig1.png differ diff --git a/doc/source/figures/ceph-architecture.png b/doc/source/figures/ceph-architecture.png new file mode 100644 index 00000000000..ec408118507 Binary files /dev/null and b/doc/source/figures/ceph-architecture.png differ diff --git a/doc/source/figures/emc-enabler.png b/doc/source/figures/emc-enabler.png new file mode 100644 index 00000000000..b969b817141 Binary files /dev/null and b/doc/source/figures/emc-enabler.png differ diff --git a/doc/source/figures/filteringWorkflow1.png b/doc/source/figures/filteringWorkflow1.png new file mode 100644 index 00000000000..58da979d793 Binary files /dev/null and b/doc/source/figures/filteringWorkflow1.png differ diff --git a/doc/source/figures/filteringWorkflow2.png b/doc/source/figures/filteringWorkflow2.png new file mode 100644 index 00000000000..e0fe66acfe2 Binary files /dev/null and b/doc/source/figures/filteringWorkflow2.png differ diff --git a/doc/source/figures/hds_network.jpg b/doc/source/figures/hds_network.jpg new file mode 100644 index 00000000000..bfd9d2bb7c5 Binary files /dev/null and b/doc/source/figures/hds_network.jpg differ diff --git a/doc/source/figures/hsp_network.png b/doc/source/figures/hsp_network.png new file mode 100644 index 00000000000..024ddd8c9e2 Binary files /dev/null and b/doc/source/figures/hsp_network.png differ diff --git a/doc/source/figures/nova-conf-kvm-flat.png b/doc/source/figures/nova-conf-kvm-flat.png new file mode 100644 index 00000000000..ac68d4d331b Binary files /dev/null and b/doc/source/figures/nova-conf-kvm-flat.png differ diff --git a/doc/source/figures/nova-conf-kvm-flat.svg b/doc/source/figures/nova-conf-kvm-flat.svg new file mode 100644 index 00000000000..079a56f92c0 --- /dev/null +++ b/doc/source/figures/nova-conf-kvm-flat.svg @@ -0,0 +1,1833 @@ + + + + + Schéma Réseau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VBackground-1 + + + Solid + + + + + + + + + + None + + + + + + + + + + + Page-1 + + + + + + Oblique connector.466 + + + + + + + + + + + + + + + + + + Oblique connector.527 + + + + + + + + + + + + + + + + + + Oblique connector.528 + + + + + + + + + + + + + + + + + + Oblique connector.530 + + + + + + + + + + + + + + + + + + Oblique connector.531 + + + + + + + + + + + + + + + + + + Oblique connector.532 + + + + + + + + + + + + + + + + + + Oblique connector.533 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Server.1 + CINDER-VOLUME + + Sheet.2 + + + + Sheet.3 + + + + + Sheet.4 + + + + + Sheet.5 + + + + Sheet.6 + + + + Sheet.7 + + + + Sheet.8 + + + + + + Sheet.9 + + Sheet.10 + + + + Sheet.11 + + + + Sheet.12 + + + + Sheet.13 + + + + Sheet.14 + + + + Sheet.15 + + + + + + + NOVA-VOLUME--iscsi_ip_prefix=nnn.nnn.nnn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + File server.505 + GLANCE --image_service=nova.image.glance.GlanceImageService -... + + Sheet.506 + + + + + Sheet.507 + + + + + Sheet.508 + + + + + Sheet.509 + + + + Sheet.510 + + + + Sheet.511 + + + + Sheet.512 + + + + + + Sheet.513 + + Sheet.514 + + + + Sheet.515 + + + + Sheet.516 + + + + Sheet.517 + + + + Sheet.518 + + + + Sheet.519 + + + + + + + + Sheet.520 + + Sheet.521 + + + + Sheet.522 + + + + Sheet.523 + + + + Sheet.524 + + + + Sheet.525 + + + + + + + GLANCE--image_service=nova.image.glance.GlanceImageService--glance_api_servers=$nova_glance_host--s3_host=$nova_glance_host + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Server.51 + NOVA-SCHEDULER --rabbit_host=$nova_rabbit_host + + Sheet.52 + + + + Sheet.54 + + + + + Sheet.56 + + + + + Sheet.89 + + + + Sheet.90 + + + + Sheet.91 + + + + Sheet.92 + + + + + + Sheet.93 + + Sheet.94 + + + + Sheet.95 + + + + Sheet.96 + + + + Sheet.97 + + + + Sheet.98 + + + + Sheet.99 + + + + + + + NOVA-SCHEDULER--rabbit_host=$nova_rabbit_host + + + + + + Document + Nova.conf + + Sheet.24 + + + + Sheet.25 + + + + Sheet.26 + + + + Sheet.27 + + + + Sheet.28 + + + + Sheet.29 + + + + Sheet.30 + + + + Sheet.31 + + + + Sheet.32 + + + + Sheet.33 + + + + Sheet.34 + + + + Sheet.35 + + + + + + Nova.conf + + + + + + + + + + + + + + + + + + Data + + Sheet.38 + + + + Sheet.39 + + + + Sheet.40 + + + + + Sheet.41 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Web server + NOVA-API --ec2_url=http://$nova_api_host:8773/services/Cloud + + Sheet.42 + + + + Sheet.43 + + + + + Sheet.44 + + + + + Sheet.45 + + + + Sheet.46 + + + + Sheet.47 + + + + Sheet.48 + + + + + + Sheet.49 + + Sheet.50 + + + + Sheet.53 + + + + Sheet.55 + + + + Sheet.57 + + + + Sheet.58 + + + + Sheet.59 + + + + + + + + Sheet.60 + + Sheet.61 + + Sheet.62 + + + + + Sheet.63 + + + + + Sheet.64 + + + + + + + + NOVA-API--ec2_url=http://$nova_api_host:8773/services/Cloud + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Application server.57 + NOVA-COMPUTE --libvirt_type=kvm --ajax_console_proxy_url=$nov... + + Sheet.88 + + + + Sheet.104 + + Sheet.105 + + + + + Sheet.106 + + + + + Sheet.107 + + + + Sheet.119 + + + + Sheet.120 + + + + Sheet.121 + + + + + + Sheet.122 + + Sheet.123 + + + + Sheet.124 + + + + Sheet.125 + + + + Sheet.126 + + + + Sheet.127 + + + + Sheet.128 + + + + + + Sheet.129 + + Sheet.130 + + + + + Sheet.131 + + + + + Sheet.132 + + + + Sheet.133 + + + + Sheet.134 + + + + Sheet.135 + + + + + + Sheet.136 + + Sheet.137 + + + + Sheet.138 + + + + Sheet.139 + + + + Sheet.140 + + + + Sheet.141 + + + + Sheet.142 + + + + + + Sheet.143 + + Sheet.144 + + + + + Sheet.145 + + + + + Sheet.146 + + + + Sheet.147 + + + + Sheet.148 + + + + Sheet.149 + + + + + + Sheet.150 + + Sheet.151 + + + + Sheet.152 + + + + Sheet.153 + + + + Sheet.154 + + + + Sheet.155 + + + + Sheet.156 + + + + + + Sheet.157 + + Sheet.158 + + + + + Sheet.159 + + + + + Sheet.160 + + + + Sheet.161 + + + + Sheet.162 + + + + Sheet.163 + + + + + + Sheet.164 + + Sheet.165 + + + + Sheet.166 + + + + Sheet.167 + + + + Sheet.168 + + + + Sheet.169 + + + + Sheet.170 + + + + + + + + NOVA-COMPUTE--libvirt_type=kvm--ajax_console_proxy_url=$nova_ajax_proxy_url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Database server + MySQL --sql_connection=mysql+pymysql://$nova_db_user:$nova_db_pass@$n... + + Sheet.172 + + + + Sheet.173 + + + + + Sheet.174 + + + + + Sheet.175 + + + + Sheet.176 + + + + Sheet.177 + + + + Sheet.178 + + + + + + Sheet.179 + + Sheet.180 + + + + Sheet.181 + + + + Sheet.182 + + + + Sheet.183 + + + + Sheet.184 + + + + Sheet.185 + + + + + + + + Sheet.186 + + Sheet.187 + + + + Sheet.188 + + + + Sheet.189 + + + + + + + MySQL--sql_connection=mysql://$nova_db_user:$nova_db_pass@$nova_db_host/$nova_db_name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Proxy server + NOVA-NETWORK --dhcpbridge_flagfile=/etc/nova/nova.conf --dhcp... + + Sheet.191 + + + + Sheet.192 + + + + + Sheet.193 + + + + + Sheet.194 + + + + Sheet.195 + + + + Sheet.196 + + + + Sheet.197 + + + + + + Sheet.198 + + Sheet.199 + + + + Sheet.200 + + + + Sheet.201 + + + + Sheet.202 + + + + Sheet.203 + + + + Sheet.204 + + + + + + + + Sheet.205 + + Sheet.206 + + + + Sheet.207 + + + + + + + + + + + + + + + + + Sheet.208 + + + + + + + NOVA-NETWORK--dhcpbridge_flagfile=/etc/nova/nova.conf--dhcpbridge=/usr/bin/nova-dhcpbridge--flat_network_bridge=br100--network_manager=nova.network.manager.FlatManager + + + diff --git a/doc/source/figures/nova-conf-kvm-flat.vsd b/doc/source/figures/nova-conf-kvm-flat.vsd new file mode 100644 index 00000000000..f1cff06926d Binary files /dev/null and b/doc/source/figures/nova-conf-kvm-flat.vsd differ diff --git a/doc/source/figures/nova-conf-xen-flat.png b/doc/source/figures/nova-conf-xen-flat.png new file mode 100644 index 00000000000..3e2ad991291 Binary files /dev/null and b/doc/source/figures/nova-conf-xen-flat.png differ diff --git a/doc/source/figures/nova-conf-xen-flat.svg b/doc/source/figures/nova-conf-xen-flat.svg new file mode 100644 index 00000000000..73c27a29290 --- /dev/null +++ b/doc/source/figures/nova-conf-xen-flat.svg @@ -0,0 +1,1453 @@ + + + + + Schéma Réseau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VBackground-1 + + + Solid + + + + + + + + + + None + + + + + + + + + + + Page-1 + + + + + + Oblique connector.466 + + + + + + + + + + + + + + + + + + Oblique connector.527 + + + + + + + + + + + + + + + + + + Oblique connector.528 + + + + + + + + + + + + + + + + + + Oblique connector.532 + + + + + + + + + + + + + + + + + + Oblique connector.533 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + File server.505 + GLANCE --image_service=nova.image.glance.GlanceImageService -... + + Sheet.506 + + + + + Sheet.507 + + + + + Sheet.508 + + + + + Sheet.509 + + + + Sheet.510 + + + + Sheet.511 + + + + Sheet.512 + + + + + + Sheet.513 + + Sheet.514 + + + + Sheet.515 + + + + Sheet.516 + + + + Sheet.517 + + + + Sheet.518 + + + + Sheet.519 + + + + + + + + Sheet.520 + + Sheet.521 + + + + Sheet.522 + + + + Sheet.523 + + + + Sheet.524 + + + + Sheet.525 + + + + + + + GLANCE--image_service=nova.image.glance.GlanceImageService--glance_api_servers=$nova_glance_host--s3_host=$nova_glance_host + + + + + + Document + Nova.conf + + Sheet.24 + + + + Sheet.25 + + + + Sheet.26 + + + + Sheet.27 + + + + Sheet.28 + + + + Sheet.29 + + + + Sheet.30 + + + + Sheet.31 + + + + Sheet.32 + + + + Sheet.33 + + + + Sheet.34 + + + + Sheet.35 + + + + + + Nova.conf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Web server + NOVA-API --ec2_url=http://$nova_api_host:8773/services/Cloud ... + + Sheet.42 + + + + Sheet.43 + + + + + Sheet.44 + + + + + Sheet.45 + + + + Sheet.46 + + + + Sheet.47 + + + + Sheet.48 + + + + + + Sheet.49 + + Sheet.50 + + + + Sheet.53 + + + + Sheet.55 + + + + Sheet.57 + + + + Sheet.58 + + + + Sheet.59 + + + + + + + + Sheet.60 + + Sheet.61 + + Sheet.62 + + + + + Sheet.63 + + + + + Sheet.64 + + + + + + + + NOVA-API--ec2_url=http://$nova_api_host:8773/services/Cloud--allow_admin_api=true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Application server.57 + NOVA-COMPUTE --compute_driver=xenapi.XenAPIDriver --xenapi_connection_url... + + Sheet.88 + + + + Sheet.104 + + Sheet.105 + + + + + Sheet.106 + + + + + Sheet.107 + + + + Sheet.119 + + + + Sheet.120 + + + + Sheet.121 + + + + + + Sheet.122 + + Sheet.123 + + + + Sheet.124 + + + + Sheet.125 + + + + Sheet.126 + + + + Sheet.127 + + + + Sheet.128 + + + + + + Sheet.129 + + Sheet.130 + + + + + Sheet.131 + + + + + Sheet.132 + + + + Sheet.133 + + + + Sheet.134 + + + + Sheet.135 + + + + + + Sheet.136 + + Sheet.137 + + + + Sheet.138 + + + + Sheet.139 + + + + Sheet.140 + + + + Sheet.141 + + + + Sheet.142 + + + + + + Sheet.143 + + Sheet.144 + + + + + Sheet.145 + + + + + Sheet.146 + + + + Sheet.147 + + + + Sheet.148 + + + + Sheet.149 + + + + + + Sheet.150 + + Sheet.151 + + + + Sheet.152 + + + + Sheet.153 + + + + Sheet.154 + + + + Sheet.155 + + + + Sheet.156 + + + + + + Sheet.157 + + Sheet.158 + + + + + Sheet.159 + + + + + Sheet.160 + + + + Sheet.161 + + + + Sheet.162 + + + + Sheet.163 + + + + + + Sheet.164 + + Sheet.165 + + + + Sheet.166 + + + + Sheet.167 + + + + Sheet.168 + + + + Sheet.169 + + + + Sheet.170 + + + + + + + + NOVA-COMPUTE--connection_type=xenapi--xenapi_connection_url=https://<XenServer IP>--xenapi_connection_username=root--xenapi_connection_password=supersecret--rescue_timeout=86400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Database server + MySQL --sql_connection=mysql+pymysql://$nova_db_user:$nova_db_pass@$n... + + Sheet.172 + + + + Sheet.173 + + + + + Sheet.174 + + + + + Sheet.175 + + + + Sheet.176 + + + + Sheet.177 + + + + Sheet.178 + + + + + + Sheet.179 + + Sheet.180 + + + + Sheet.181 + + + + Sheet.182 + + + + Sheet.183 + + + + Sheet.184 + + + + Sheet.185 + + + + + + + + Sheet.186 + + Sheet.187 + + + + Sheet.188 + + + + Sheet.189 + + + + + + + MySQL--sql_connection=mysql://$nova_db_user:$nova_db_pass@$nova_db_host/$nova_db_name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Proxy server + NOVA-NETWORK --network_manager=nova.network.manager.FlatManag... + + Sheet.191 + + + + Sheet.192 + + + + + Sheet.193 + + + + + Sheet.194 + + + + Sheet.195 + + + + Sheet.196 + + + + Sheet.197 + + + + + + Sheet.198 + + Sheet.199 + + + + Sheet.200 + + + + Sheet.201 + + + + Sheet.202 + + + + Sheet.203 + + + + Sheet.204 + + + + + + + + Sheet.205 + + Sheet.206 + + + + Sheet.207 + + + + + + + + + + + + + + + + + Sheet.208 + + + + + + + NOVA-NETWORK--network_manager=nova.network.manager.FlatManager--flat_network_bridge=xenbr0--flat_injected=true--ipv6_backend=account_identifier + + + diff --git a/doc/source/figures/nova-conf-xen-flat.vsd b/doc/source/figures/nova-conf-xen-flat.vsd new file mode 100644 index 00000000000..9bbb05bd35c Binary files /dev/null and b/doc/source/figures/nova-conf-xen-flat.vsd differ diff --git a/doc/source/figures/nova-weighting-hosts.png b/doc/source/figures/nova-weighting-hosts.png new file mode 100644 index 00000000000..e0fe66acfe2 Binary files /dev/null and b/doc/source/figures/nova-weighting-hosts.png differ diff --git a/doc/source/figures/vmware-nova-driver-architecture.jpg b/doc/source/figures/vmware-nova-driver-architecture.jpg new file mode 100644 index 00000000000..f2b17264e73 Binary files /dev/null and b/doc/source/figures/vmware-nova-driver-architecture.jpg differ diff --git a/doc/source/figures/xenserver_architecture.png b/doc/source/figures/xenserver_architecture.png new file mode 100644 index 00000000000..a1c63706d6e Binary files /dev/null and b/doc/source/figures/xenserver_architecture.png differ diff --git a/doc/source/figures/xenserver_architecture.svg b/doc/source/figures/xenserver_architecture.svg new file mode 100644 index 00000000000..1aa43463896 --- /dev/null +++ b/doc/source/figures/xenserver_architecture.svg @@ -0,0 +1,1035 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + xapi plug-ins + nova-compute + nova.virt.xenapi + + + + + XenAPI + nova-network + dhcpd + Xen + Domain 0 + OpenStack VM + xapi + Tenant VM + + OpenStack add-ons + OpenStack + + + eth0 + + + + eth1 + + + + eth2 + + + + eth0 + + + + xenbr0 + + + + + + + Networks connected to physical interfaces according to the selected configuration. + + + + Storage Repository + Physical Host + + + + + + + + + + + + + + + + + Virtual block devices, usually on local disk. + + + + OpenStack is using the XenAPI Python module to communicate with dom0 through the management network. + + + + + Management Network + Public Network + Tenant Network + + + ... + + diff --git a/doc/source/filter_scheduler.rst b/doc/source/filter_scheduler.rst deleted file mode 100644 index e69e4a1d6ac..00000000000 --- a/doc/source/filter_scheduler.rst +++ /dev/null @@ -1,461 +0,0 @@ -Filter Scheduler -================ - -The **Filter Scheduler** supports `filtering` and `weighting` to make informed -decisions on where a new instance should be created. This Scheduler supports -working with Compute Nodes only. - -Filtering ---------- - -.. image:: ./images/filteringWorkflow1.png - -During its work Filter Scheduler iterates over all found compute nodes, -evaluating each against a set of filters. The list of resulting hosts is -ordered by weighers. The Scheduler then chooses hosts for the requested -number of instances, choosing the most weighted hosts. For a specific -filter to succeed for a specific host, the filter matches the user -request against the state of the host plus some extra magic as defined -by each filter (described in more detail below). - -If the Scheduler cannot find candidates for the next instance, it means that -there are no appropriate hosts where that instance can be scheduled. - -The Filter Scheduler has to be quite flexible to support the required variety -of `filtering` and `weighting` strategies. If this flexibility is insufficient -you can implement `your own filtering algorithm`. - -There are many standard filter classes which may be used -(:mod:`nova.scheduler.filters`): - -* |AllHostsFilter| - does no filtering. It passes all the available hosts. -* |ImagePropertiesFilter| - filters hosts based on properties defined - on the instance's image. It passes hosts that can support the properties - specified on the image used by the instance. -* |AvailabilityZoneFilter| - filters hosts by availability zone. It passes - hosts matching the availability zone specified in the instance properties. - Use a comma to specify multiple zones. The filter will then ensure it matches - any zone specified. -* |ComputeCapabilitiesFilter| - checks that the capabilities provided by the - host compute service satisfy any extra specifications associated with the - instance type. It passes hosts that can create the specified instance type. - - If an extra specs key contains a colon (:), anything before the colon is - treated as a namespace and anything after the colon is treated as the key to - be matched. If a namespace is present and is not ``capabilities``, the filter - ignores the namespace. For example ``capabilities:cpu_info:features`` is - a valid scope format. For backward compatibility, the filter also treats the - extra specs key as the key to be matched if no namespace is present; this - action is highly discouraged because it conflicts with - AggregateInstanceExtraSpecsFilter filter when you enable both filters - - The extra specifications can have an operator at the beginning of the value - string of a key/value pair. If there is no operator specified, then a - default operator of ``s==`` is used. Valid operators are: - -:: - - * = (equal to or greater than as a number; same as vcpus case) - * == (equal to as a number) - * != (not equal to as a number) - * >= (greater than or equal to as a number) - * <= (less than or equal to as a number) - * s== (equal to as a string) - * s!= (not equal to as a string) - * s>= (greater than or equal to as a string) - * s> (greater than as a string) - * s<= (less than or equal to as a string) - * s< (less than as a string) - * (substring) - * (all elements contained in collection) - * (find one of these) - - Examples are: ">= 5", "s== 2.1.0", " gcc", " aes mmx", and " fpu gpu" - -* |AggregateInstanceExtraSpecsFilter| - checks that the aggregate metadata - satisfies any extra specifications associated with the instance type (that - have no scope or are scoped with ``aggregate_instance_extra_specs``). - It passes hosts that can create the specified instance type. - The extra specifications can have the same operators as - |ComputeCapabilitiesFilter|. To specify multiple values for the same key - use a comma. E.g., "value1,value2" -* |ComputeFilter| - passes all hosts that are operational and enabled. -* |CoreFilter| - filters based on CPU core utilization. It passes hosts with - sufficient number of CPU cores. -* |AggregateCoreFilter| - filters hosts by CPU core number with per-aggregate - ``cpu_allocation_ratio`` setting. If no per-aggregate value is found, it will - fall back to the global default ``cpu_allocation_ratio``. If more than one value - is found for a host (meaning the host is in two different aggregates with - different ratio settings), the minimum value will be used. -* |IsolatedHostsFilter| - filter based on ``image_isolated``, ``host_isolated`` - and ``restrict_isolated_hosts_to_isolated_images`` flags. -* |JsonFilter| - allows simple JSON-based grammar for selecting hosts. -* |RamFilter| - filters hosts by their RAM. Only hosts with sufficient RAM - to host the instance are passed. -* |AggregateRamFilter| - filters hosts by RAM with per-aggregate - ``ram_allocation_ratio`` setting. If no per-aggregate value is found, it will - fall back to the global default ``ram_allocation_ratio``. If more than one value - is found for a host (meaning the host is in two different aggregates with - different ratio settings), the minimum value will be used. -* |DiskFilter| - filters hosts by their disk allocation. Only hosts with sufficient - disk space to host the instance are passed. - ``disk_allocation_ratio`` setting. The virtual disk to physical disk - allocation ratio, 1.0 by default. The total allowed allocated disk size will - be physical disk multiplied this ratio. -* |AggregateDiskFilter| - filters hosts by disk allocation with per-aggregate - ``disk_allocation_ratio`` setting. If no per-aggregate value is found, it will - fall back to the global default ``disk_allocation_ratio``. If more than one value - is found for a host (meaning the host is in two or more different aggregates with - different ratio settings), the minimum value will be used. -* |NumInstancesFilter| - filters compute nodes by number of running instances. Nodes - with too many instances will be filtered. - ``max_instances_per_host`` setting. Maximum number of instances allowed to run on - this host. The host will be ignored by the scheduler if more than ``max_instances_per_host`` - already exist on the host. -* |AggregateNumInstancesFilter| - filters hosts by number of instances with - per-aggregate ``max_instances_per_host`` setting. If no per-aggregate value - is found, it will fall back to the global default ``max_instances_per_host``. - If more than one value is found for a host (meaning the host is in two or more - different aggregates with different max instances per host settings), - the minimum value will be used. -* |IoOpsFilter| - filters hosts by concurrent I/O operations on it. - hosts with too many concurrent I/O operations will be filtered. - ``max_io_ops_per_host`` setting. Maximum number of I/O intensive instances allowed to - run on this host, the host will be ignored by scheduler if more than ``max_io_ops_per_host`` - instances such as build/resize/snapshot etc are running on it. -* |AggregateIoOpsFilter| - filters hosts by I/O operations with per-aggregate - ``max_io_ops_per_host`` setting. If no per-aggregate value is found, it will - fall back to the global default ``max_io_ops_per_host``. If more than - one value is found for a host (meaning the host is in two or more different - aggregates with different max io operations settings), the minimum value - will be used. -* |PciPassthroughFilter| - Filter that schedules instances on a host if the host - has devices to meet the device requests in the 'extra_specs' for the flavor. -* |SimpleCIDRAffinityFilter| - allows a new instance on a host within - the same IP block. -* |DifferentHostFilter| - allows the instance on a different host from a - set of instances. -* |SameHostFilter| - puts the instance on the same host as another instance in - a set of instances. -* |RetryFilter| - filters hosts that have been attempted for scheduling. - Only passes hosts that have not been previously attempted. -* |TrustedFilter| (EXPERIMENTAL) - filters hosts based on their trust. Only passes hosts - that meet the trust requirements specified in the instance properties. -* |TypeAffinityFilter| - Only passes hosts that are not already running an - instance of the requested type. -* |AggregateTypeAffinityFilter| - limits instance_type by aggregate. - This filter passes hosts if no instance_type key is set or - the instance_type aggregate metadata value contains the name of the - instance_type requested. The value of the instance_type metadata entry is - a string that may contain either a single instance_type name or a comma - separated list of instance_type names. e.g. 'm1.nano' or "m1.nano,m1.small" -* |ServerGroupAntiAffinityFilter| - This filter implements anti-affinity for a - server group. First you must create a server group with a policy of - 'anti-affinity' via the server groups API. Then, when you boot a new server, - provide a scheduler hint of 'group=' where is the UUID of the - server group you created. This will result in the server getting added to the - group. When the server gets scheduled, anti-affinity will be enforced among - all servers in that group. -* |ServerGroupAffinityFilter| - This filter works the same way as - ServerGroupAntiAffinityFilter. The difference is that when you create the server - group, you should specify a policy of 'affinity'. -* |AggregateMultiTenancyIsolation| - isolate tenants in specific aggregates. - To specify multiple tenants use a comma. Eg. "tenant1,tenant2" -* |AggregateImagePropertiesIsolation| - isolates hosts based on image - properties and aggregate metadata. Use a comma to specify multiple values for the - same property. The filter will then ensure at least one value matches. -* |MetricsFilter| - filters hosts based on metrics weight_setting. Only hosts with - the available metrics are passed. -* |NUMATopologyFilter| - filters hosts based on the NUMA topology requested by the - instance, if any. - -Now we can focus on these standard filter classes in some detail. We'll skip the -simplest ones, such as |AllHostsFilter|, |CoreFilter| and |RamFilter|, -because their functionality is relatively simple and can be understood from the -code. For example class |RamFilter| has the next realization: - -:: - - class RamFilter(filters.BaseHostFilter): - """Ram Filter with over subscription flag""" - - def host_passes(self, host_state, filter_properties): - """Only return hosts with sufficient available RAM.""" - instance_type = filter_properties.get('instance_type') - requested_ram = instance_type['memory_mb'] - free_ram_mb = host_state.free_ram_mb - total_usable_ram_mb = host_state.total_usable_ram_mb - used_ram_mb = total_usable_ram_mb - free_ram_mb - return total_usable_ram_mb * FLAGS.ram_allocation_ratio - used_ram_mb >= requested_ram - -Here ``ram_allocation_ratio`` means the virtual RAM to physical RAM allocation -ratio (it is ``1.5`` by default). - -The |AvailabilityZoneFilter| looks at the availability zone of compute node -and availability zone from the properties of the request. Each compute service -has its own availability zone. So deployment engineers have an option to run -scheduler with availability zones support and can configure availability zones -on each compute host. This class's method ``host_passes`` returns ``True`` if -availability zone mentioned in request is the same on the current compute host. - -The |ImagePropertiesFilter| filters hosts based on the architecture, -hypervisor type and virtual machine mode specified in the -instance. For example, an instance might require a host that supports the ARM -architecture on a qemu compute host. The |ImagePropertiesFilter| will only -pass hosts that can satisfy this request. These instance -properties are populated from properties defined on the instance's image. -E.g. an image can be decorated with these properties using -``glance image-update img-uuid --property architecture=arm --property -hypervisor_type=qemu`` -Only hosts that satisfy these requirements will pass the -|ImagePropertiesFilter|. - -|ComputeCapabilitiesFilter| checks if the host satisfies any ``extra_specs`` -specified on the instance type. The ``extra_specs`` can contain key/value pairs. -The key for the filter is either non-scope format (i.e. no ``:`` contained), or -scope format in capabilities scope (i.e. ``capabilities:xxx:yyy``). One example -of capabilities scope is ``capabilities:cpu_info:features``, which will match -host's cpu features capabilities. The |ComputeCapabilitiesFilter| will only -pass hosts whose capabilities satisfy the requested specifications. All hosts -are passed if no ``extra_specs`` are specified. - -|ComputeFilter| is quite simple and passes any host whose compute service is -enabled and operational. - -Now we are going to |IsolatedHostsFilter|. There can be some special hosts -reserved for specific images. These hosts are called **isolated**. So the -images to run on the isolated hosts are also called isolated. The filter -checks if ``image_isolated`` flag named in instance specifications is the same -as the host. Isolated hosts can run non isolated images if the flag -``restrict_isolated_hosts_to_isolated_images`` is set to false. - -|DifferentHostFilter| - method ``host_passes`` returns ``True`` if the host to -place an instance on is different from all the hosts used by a set of instances. - -|SameHostFilter| does the opposite to what |DifferentHostFilter| does. -``host_passes`` returns ``True`` if the host we want to place an instance on is -one of the hosts used by a set of instances. - -|SimpleCIDRAffinityFilter| looks at the subnet mask and investigates if -the network address of the current host is in the same sub network as it was -defined in the request. - -|JsonFilter| - this filter provides the opportunity to write complicated -queries for the hosts capabilities filtering, based on simple JSON-like syntax. -There can be used the following operations for the host states properties: -``=``, ``<``, ``>``, ``in``, ``<=``, ``>=``, that can be combined with the following -logical operations: ``not``, ``or``, ``and``. For example, the following query can be -found in tests: - -:: - - ['and', - ['>=', '$free_ram_mb', 1024], - ['>=', '$free_disk_mb', 200 * 1024] - ] - -This query will filter all hosts with free RAM greater or equal than 1024 MB -and at the same time with free disk space greater or equal than 200 GB. - -Many filters use data from ``scheduler_hints``, that is defined in the moment of -creation of the new server for the user. The only exception for this rule is -|JsonFilter|, that takes data from the schedulers ``HostState`` data structure -directly. Variable naming, such as the ``$free_ram_mb`` example above, should -be based on those attributes. - -The |RetryFilter| filters hosts that have already been attempted for -scheduling. It only passes hosts that have not been previously attempted. If a -compute node is raising an exception when spawning an instance, then the -compute manager will reschedule it by adding the failing host to a retry -dictionary so that the RetryFilter will not accept it as a possible -destination. That means that if all of your compute nodes are failing, then the -RetryFilter will return 0 hosts and the scheduler will raise a NoValidHost -exception even if the problem is related to 1:N compute nodes. If you see that -case in the scheduler logs, then your problem is most likely related to a -compute problem and you should check the compute logs. - -The |TrustedFilter| filters hosts based on their trust. Only passes hosts -that match the trust requested in the ``extra_specs`` for the flavor. The key -for this filter must be scope format as ``trust:trusted_host``, where ``trust`` -is the scope of the key and ``trusted_host`` is the actual key value. -The value of this pair (``trusted``/``untrusted``) must match the -integrity of a host (obtained from the Attestation service) before it is -passed by the |TrustedFilter|. - -The |NUMATopologyFilter| considers the NUMA topology that was specified for the instance -through the use of flavor extra_specs in combination with the image properties, as -described in detail in the related nova-spec document: - -* http://git.openstack.org/cgit/openstack/nova-specs/tree/specs/juno/virt-driver-numa-placement.rst - -and try to match it with the topology exposed by the host, accounting for the -``ram_allocation_ratio`` and ``cpu_allocation_ratio`` for over-subscription. The -filtering is done in the following manner: - -* Filter will attempt to pack instance cells onto host cells. -* It will consider the standard over-subscription limits for each host NUMA cell, - and provide limits to the compute host accordingly (as mentioned above). -* If instance has no topology defined, it will be considered for any host. -* If instance has a topology defined, it will be considered only for NUMA - capable hosts. - -To use filters you specify two settings: - -* ``scheduler_available_filters`` - Defines filter classes made available to the - scheduler. This setting can be used multiple times. -* ``scheduler_default_filters`` - Of the available filters, defines those that - the scheduler uses by default. - -The default values for these settings in nova.conf are: - -:: - - --scheduler_available_filters=nova.scheduler.filters.all_filters - --scheduler_default_filters=RamFilter,ComputeFilter,AvailabilityZoneFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,ServerGroupAntiAffinityFilter,ServerGroupAffinityFilter' - -With this configuration, all filters in ``nova.scheduler.filters`` -would be available, and by default the |RamFilter|, |ComputeFilter|, -|AvailabilityZoneFilter|, |ComputeCapabilitiesFilter|, -|ImagePropertiesFilter|, |ServerGroupAntiAffinityFilter|, -and |ServerGroupAffinityFilter| would be used. - -To create **your own filter** you must inherit from -|BaseHostFilter| and implement one method: -``host_passes``. This method should return ``True`` if a host passes the filter. It -takes ``host_state`` (describing the host) and ``filter_properties`` dictionary as the -parameters. - -As an example, nova.conf could contain the following scheduler-related -settings: - -:: - - --scheduler_driver=nova.scheduler.FilterScheduler - --scheduler_available_filters=nova.scheduler.filters.all_filters - --scheduler_available_filters=myfilter.MyFilter - --scheduler_default_filters=RamFilter,ComputeFilter,MyFilter - -With these settings, nova will use the ``FilterScheduler`` for the scheduler -driver. The standard nova filters and MyFilter are available to the -FilterScheduler. The RamFilter, ComputeFilter, and MyFilter are used by -default when no filters are specified in the request. - -Each filter selects hosts in a different way and has different costs. The order of -``scheduler_default_filters`` affects scheduling performance. The general suggestion -is to filter out invalid hosts as soon as possible to avoid unnecessary costs. -We can sort ``scheduler_default_filters`` items by their costs in reverse order. -For example, ComputeFilter is better before any resource calculating filters -like RamFilter, CoreFilter. - -In medium/large environments having AvailabilityZoneFilter before any capability or -resource calculating filters can be useful. - -Weights -------- - -Filter Scheduler uses the so-called **weights** during its work. A weigher is a -way to select the best suitable host from a group of valid hosts by giving -weights to all the hosts in the list. - -In order to prioritize one weigher against another, all the weighers have to -define a multiplier that will be applied before computing the weight for a node. -All the weights are normalized beforehand so that the multiplier can be applied -easily. Therefore the final weight for the object will be:: - - weight = w1_multiplier * norm(w1) + w2_multiplier * norm(w2) + ... - -A weigher should be a subclass of ``weights.BaseHostWeigher`` and they can implement -both the ``weight_multiplier`` and ``_weight_object`` methods or just implement the -``weight_objects`` method. ``weight_objects`` method is overridden only if you need -access to all objects in order to calculate weights, and it just return a list of weights, -and not modify the weight of the object directly, since final weights are normalized -and computed by ``weight.BaseWeightHandler``. - -The Filter Scheduler weighs hosts based on the config option -`scheduler_weight_classes`, this defaults to -`nova.scheduler.weights.all_weighers`, which selects the following weighers: - -* |RAMWeigher| Compute weight based on available RAM on the compute node. - Sort with the largest weight winning. If the multiplier is negative, the - host with least RAM available will win (useful for stacking hosts, instead - of spreading). -* |DiskWeigher| Hosts are weighted and sorted by free disk space with the largest - weight winning. If the multiplier is negative, the host with less disk space available - will win (useful for stacking hosts, instead of spreading). -* |MetricsWeigher| This weigher can compute the weight based on the compute node - host's various metrics. The to-be weighed metrics and their weighing ratio - are specified in the configuration file as the followings:: - - metrics_weight_setting = name1=1.0, name2=-1.0 - -* |IoOpsWeigher| The weigher can compute the weight based on the compute node - host's workload. The default is to preferably choose light workload compute - hosts. If the multiplier is positive, the weigher prefer choosing heavy - workload compute hosts, the weighing has the opposite effect of the default. - -* |ServerGroupSoftAffinityWeigher| The weigher can compute the weight based - on the number of instances that run on the same server group. The largest - weight defines the preferred host for the new instance. For the multiplier - only a positive value is meaningful for the calculation as a negative value - would mean that the affinity weigher would prefer non collocating placement. - -* |ServerGroupSoftAntiAffinityWeigher| The weigher can compute the weight based - on the number of instances that run on the same server group as a negative - value. The largest weight defines the preferred host for the new instance. - For the multiplier only a positive value is meaningful for the calculation as - a negative value would mean that the anti-affinity weigher would prefer - collocating placement. - -Filter Scheduler makes a local list of acceptable hosts by repeated filtering and -weighing. Each time it chooses a host, it virtually consumes resources on it, -so subsequent selections can adjust accordingly. It is useful if the customer -asks for a large block of instances, because weight is computed for -each instance requested. - -.. image:: ./images/filteringWorkflow2.png - -At the end Filter Scheduler sorts selected hosts by their weight and attempts -to provision instances on the chosen hosts. - -P.S.: you can find more examples of using Filter Scheduler and standard filters -in :mod:`nova.tests.scheduler`. - -.. |AllHostsFilter| replace:: :class:`AllHostsFilter ` -.. |ImagePropertiesFilter| replace:: :class:`ImagePropertiesFilter ` -.. |AvailabilityZoneFilter| replace:: :class:`AvailabilityZoneFilter ` -.. |BaseHostFilter| replace:: :class:`BaseHostFilter ` -.. |ComputeCapabilitiesFilter| replace:: :class:`ComputeCapabilitiesFilter ` -.. |ComputeFilter| replace:: :class:`ComputeFilter ` -.. |CoreFilter| replace:: :class:`CoreFilter ` -.. |AggregateCoreFilter| replace:: :class:`AggregateCoreFilter ` -.. |IsolatedHostsFilter| replace:: :class:`IsolatedHostsFilter ` -.. |JsonFilter| replace:: :class:`JsonFilter ` -.. |RamFilter| replace:: :class:`RamFilter ` -.. |AggregateRamFilter| replace:: :class:`AggregateRamFilter ` -.. |DiskFilter| replace:: :class:`DiskFilter ` -.. |AggregateDiskFilter| replace:: :class:`AggregateDiskFilter ` -.. |NumInstancesFilter| replace:: :class:`NumInstancesFilter ` -.. |AggregateNumInstancesFilter| replace:: :class:`AggregateNumInstancesFilter ` -.. |IoOpsFilter| replace:: :class:`IoOpsFilter ` -.. |AggregateIoOpsFilter| replace:: :class:`AggregateIoOpsFilter ` -.. |PciPassthroughFilter| replace:: :class:`PciPassthroughFilter ` -.. |SimpleCIDRAffinityFilter| replace:: :class:`SimpleCIDRAffinityFilter ` -.. |DifferentHostFilter| replace:: :class:`DifferentHostFilter ` -.. |SameHostFilter| replace:: :class:`SameHostFilter ` -.. |RetryFilter| replace:: :class:`RetryFilter ` -.. |TrustedFilter| replace:: :class:`TrustedFilter ` -.. |TypeAffinityFilter| replace:: :class:`TypeAffinityFilter ` -.. |AggregateTypeAffinityFilter| replace:: :class:`AggregateTypeAffinityFilter ` -.. |ServerGroupAntiAffinityFilter| replace:: :class:`ServerGroupAntiAffinityFilter ` -.. |ServerGroupAffinityFilter| replace:: :class:`ServerGroupAffinityFilter ` -.. |AggregateInstanceExtraSpecsFilter| replace:: :class:`AggregateInstanceExtraSpecsFilter ` -.. |AggregateMultiTenancyIsolation| replace:: :class:`AggregateMultiTenancyIsolation ` -.. |NUMATopologyFilter| replace:: :class:`NUMATopologyFilter ` -.. |RAMWeigher| replace:: :class:`RAMWeigher ` -.. |AggregateImagePropertiesIsolation| replace:: :class:`AggregateImagePropertiesIsolation ` -.. |MetricsFilter| replace:: :class:`MetricsFilter ` -.. |MetricsWeigher| replace:: :class:`MetricsWeigher ` -.. |IoOpsWeigher| replace:: :class:`IoOpsWeigher ` -.. |ServerGroupSoftAffinityWeigher| replace:: :class:`ServerGroupSoftAffinityWeigher ` -.. |ServerGroupSoftAntiAffinityWeigher| replace:: :class:`ServerGroupSoftAntiAffinityWeigher ` -.. |DiskWeigher| replace:: :class:`DiskWeigher ` diff --git a/doc/source/how_to_get_involved.rst b/doc/source/how_to_get_involved.rst deleted file mode 100644 index 6c86c709bac..00000000000 --- a/doc/source/how_to_get_involved.rst +++ /dev/null @@ -1,371 +0,0 @@ -.. - 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. - -.. _getting_involved: - -===================================== -How to get (more) involved with Nova -===================================== - -So you want to get more involved with Nova? Or you are new to Nova and -wondering where to start? - -We are working on building easy ways for you to get help and ideas on -how to learn more about Nova and how the Nova community works. - -Any questions, please ask! If you are unsure who to ask, then please -contact the `Mentoring Czar`__. - -__ `Nova People`_ - -How do I get started? -===================== - -There are quite a few global docs on this: - -- http://www.openstack.org/assets/welcome-guide/OpenStackWelcomeGuide.pdf -- https://wiki.openstack.org/wiki/How_To_Contribute -- http://www.openstack.org/community/ - -There is more general info, non Nova specific info here: - -- https://wiki.openstack.org/wiki/Mentors -- https://wiki.openstack.org/wiki/OpenStack_Upstream_Training - -What should I work on? -~~~~~~~~~~~~~~~~~~~~~~ - -So you are starting out your Nova journey, where is a good place to -start? - -If you'd like to learn how Nova works before changing anything (good idea!), we -recommend looking for reviews with -1s and -2s and seeing why they got -downvoted. There is also the :ref:`code-review`. Once you have some -understanding, start reviewing patches. It's OK to ask people to explain things -you don't understand. It's also OK to see some potential problems but put a +0. - -Another way is to look for a subteam you'd like to get involved with and review -their patches. See: -https://etherpad.openstack.org/p/mitaka-nova-priorities-tracking - -Once you're ready to write code, take a look at some of the work already marked -as low-hanging fruit: - -* https://bugs.launchpad.net/nova/+bugs?field.tag=low-hanging-fruit -* https://etherpad.openstack.org/p/nova-low-hanging-fruit - -How do I get my feature in? -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The best way of getting your feature in is... well it depends. - -First concentrate on solving your problem and/or use case, don't fixate -on getting the code you have working merged. Its likely things will need -significant re-work after you discuss how your needs match up with all -the existing ways Nova is currently being used. The good news, is this -process should leave you with a feature thats more flexible and doesn't -lock you into your current way of thinking. - -A key part of getting code merged, is helping with reviewing other -people's code. Great reviews of others code will help free up more core -reviewer time to look at your own patches. In addition, you will -understand how the review is thinking when they review your code. - -Also, work out if any on going efforts are blocking your feature and -helping out speeding those up. The spec review process should help with -this effort. - -For more details on our process, please see: :ref:`process`. - -What is expected of a good contributor? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -TODO - need more info on this - -Top Tips for working with the Nova community -============================================ - -Here are some top tips around engaging with the Nova community: - -- IRC - - - we talk a lot in #openstack-nova - - do ask us questions in there, and we will try to help you - - not sure about asking questions? feel free to listen in around - other people's questions - - we recommend you setup an IRC bouncer: - https://wiki.openstack.org/wiki/IRC - -- Email - - - Use the [nova] tag in the mailing lists - - Filtering on [nova] and [all] can help tame the list - -- Be Open - - - i.e. don't review your teams code in private, do it publicly in - gerrit - - i.e. be ready to talk about openly about problems you are having, - not "theoretical" issues - - that way you can start to gain the trust of the wider community - -- Got a problem? Please ask! - - - Please raise any problems and ask questions early - - we want to help you before you are frustrated or annoyed - - unsure who to ask? Just ask in IRC, or check out the list of `Nova - people`_. - -- Talk about problems first, then solutions - - - Nova is a big project. At first, it can be hard to see the big - picture - - Don't think about "merging your patch", instead think about - "solving your problem" - - conversations are more productive that way - -- Its not the decision thats important, it's the reason behind it thats - important - - - Don't like the way the community is going? - - Please ask why we ware going that way, and please engage with the - debate - - If you don't, we are unable to learn from what you have to offer - -- No one will decide, this is stuck, who can help me? - - - it's rare, but it happens - - it's the `Nova PTL`__'s job to help you - - ...but if you don't ask, it's hard for them to help you - -__ `Nova People`_ - -Process -======= - -It can feel like you are faced with a wall of process. We are a big -community, to make sure the right communication happens, we do use a -minimal amount of process. - -If you find something that doesn't make sense, please: - -- ask questions to find out \*why\* it happens -- if you know of a better way to do it, please speak up -- one "better way" might be to remove the process if it no longer helps - -To learn more about Nova's process, please read :ref:`process`. - -Why bother with any process? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Why is it worth creating a bug or blueprint to track your code review? -This may seem like silly process, but there is usually a good reason -behind it. - -We have lots of code to review, and we have tools to try and get to -really important code reviews first. If yours is really important, but -not picked up by our tools, it's possible you just get lost in the bottom -of a big queue. - -If you have a bug fix, you have done loads of work to identify the -issue, and test out your fix, and submit it. By adding a bug report, you -are making it easier for other folks who hit the same problem to find -your work, possibly saving them the hours of pain you went through. With -any luck that gives all those people the time to fix different bugs, all -that might have affected you, if you had not given them the time go fix -it. - -Its similar with blueprints. You have worked out how to scratch your -itch, lets tell others about that great new feature you have added, so -they can use that. Also, it stops someone with a similar idea going -through all the pain of creating a feature only to find you already have -that feature ready and up for review, or merged into the latest release. - -Hopefully this gives you an idea why we have applied a small layer of -process to what we are doing. Having said all this, we need to unlearn -old habits to move forward, there may be better ways to do things, and -we are open to trying them. Please help be part of the solution. - -.. _why_plus1: - -Why do code reviews if I am not in nova-core? -============================================= - -Code reviews are the life blood of the Nova developer community. - -There is a good discussion on how you do good reviews, and how anyone -can be a reviewer: -http://docs.openstack.org/infra/manual/developers.html#peer-review - -In the draft process guide, I discuss how doing reviews can help get -your code merged faster: :ref:`process`. - -Lets look at some of the top reasons why participating with code reviews -really helps you: - -- Doing more reviews, and seeing what other reviewers notice, will help - you better understand what is expected of code that gets merged into - master -- Having more non-core people do great reviews, leaves less review work - for the core reviewers to do, so we are able get more code merged -- Empathy is one of the keys to a happy community. If you are used to - doing code reviews, you will better understand the comments you get - when people review your code. As you do more code reviews, and see - what others notice, you will get a better idea of what people are - looking for when then apply a +2 to your code. -- TODO - needs more detail - -What are the most useful types of code review comments? Well here are a -few to the top ones: - -- Fundamental flaws are the biggest thing to spot. Does the patch break - a whole set of existing users, or an existing feature? -- Consistency of behaviour is really important. Does this bit of code - do things differently to where similar things happen else where in - Nova? -- Is the code easy to maintain, well tested and easy to read? Code is - read order of magnitude times more than it is written, so optimise - for the reader of the code, not the writer. -- TODO - what others should go here? - -Let's look at some problems people hit when starting out doing code -reviews: - -- My +1 doesn't mean anything, why should I bother? - - - So your +1 really does help. Some really useful -1 votes that lead - to a +1 vote helps get code into a position - -- When to use -1 vs 0 vs +1 - - - Please see the guidelines here: - http://docs.openstack.org/infra/manual/developers.html#peer-review - -- I have already reviewed this code internally, no point in adding a +1 - externally? - - - Please talk to your company about doing all code reviews in the - public, that is a much better way to get involved. showing how the - code has evolved upstream, is much better than trying to 'perfect' - code internally, before uploading for public review. You can use - Draft mode, and mark things as WIP if you prefer, but please do - the reviews upstream. - -- Where do I start? What should I review? - - - There are various tools, but a good place to start is: - https://etherpad.openstack.org/p/ -nova-priorities-tracking - - Depending on the time in the cycle, it's worth looking at - NeedsCodeReview blueprints: - https://blueprints.launchpad.net/nova/ - - Maybe take a look at things you want to see merged, bug fixes and - features, or little code fixes - - Look for things that have been waiting a long time for a review: - http://5885fef486164bb8596d-41634d3e64ee11f37e8658ed1b4d12ec.r44.cf3.rackcdn.com/nova-openreviews.html - - If you get through the above lists, try other tools, such as: - http://status.openstack.org/reviews - -- TODO - I think there is more to add here - -How to do great code reviews? -============================= - -http://docs.openstack.org/infra/manual/developers.html#peer-review - -For more tips, please see: `Why do code reviews if I am not in nova-core?`_ - -How do I become nova-core? -========================== - -You don't have to be nova-core to be a valued member of the Nova -community. There are many, many ways you can help. Every quality review -that helps someone get their patch closer to being ready to merge helps -everyone get their code merged faster. - -The first step to becoming nova-core is learning how to be an active -member of the Nova community, including learning how to do great code -reviews. For more details see: -https://wiki.openstack.org/wiki/Nova/CoreTeam#Membership_Expectations - -If you feel like you have the time to commit to all the nova-core -membership expectations, reach out to the Nova PTL who will be -able to find you an existing member of nova-core to help mentor you. If -all goes well, and you seem like a good candidate, your mentor will -contact the rest of the nova-core team to ask them to start looking at -your reviews, so they are able to vote for you, if you get nominated for -join nova-core. - -We encourage all mentoring, where possible, to occur on #openstack-nova -so everyone can learn and benefit from your discussions. - -The above mentoring is available to every one who wants to learn how to -better code reviews, even if you don't ever want to commit to becoming -nova-core. If you already have a mentor, that's great, the process is -only there for folks who are still trying to find a mentor. Being -admitted to the mentoring program no way guarantees you will become a -member of nova-core eventually, it's here to help you improve, and help -you have the sort of involvement and conversations that can lead to -becoming a member of nova-core. - -How to do great nova-spec reviews? -================================== - -http://specs.openstack.org/openstack/nova-specs/specs/mitaka/template.html - -http://docs.openstack.org/developer/nova/devref/kilo.blueprints.html#when-is-a-blueprint-needed - -Spec reviews are always a step ahead of the normal code reviews. Follow -the above links for some great information on specs/reviews. - -The following could be some important tips: - -1. The specs are published as html documents. Ensure that the author has -a proper render of the same via the .rst file. - -2. More often than not, it's important to know that there are no -overlaps across multiple specs. - -3. Ensure that a proper dependency of the spec is identified. For -example - a user desired feature that requires a proper base enablement -should be a dependent spec. - -4. Ask for clarity on changes that appear ambiguous to you. - -5. Every release nova gets a huge set of spec proposals and that's a -huge task for the limited set of nova cores to complete. Helping the -cores with additional reviews is always a great thing. - -How to do great bug triage? -=========================== - -https://wiki.openstack.org/wiki/Nova/BugTriage - -More details coming soon... - -How to step up into a project leadership role? -============================================== - -There are many ways to help lead the Nova project: - -* Mentoring efforts, and getting started tips: - https://wiki.openstack.org/wiki/Nova/Mentoring -* Info on process, with a focus on how you can go from an idea - to getting code merged Nova: - https://wiki.openstack.org/wiki/Nova/Mitaka_Release_Schedule -* Consider leading an existing `Nova subteam`_ or forming a new one. -* Consider becoming a `Bug tag owner`_. -* Contact the PTL about becoming a Czar `Nova People`_. - -.. _`Nova people`: https://wiki.openstack.org/wiki/Nova#People -.. _`Nova subteam`: https://wiki.openstack.org/wiki/Nova#Nova_subteams -.. _`Bug tag owner`: https://wiki.openstack.org/wiki/Nova/BugTriage#Step_2:_Triage_Tagged_Bugs diff --git a/doc/source/i18n.rst b/doc/source/i18n.rst deleted file mode 100644 index d794ef42505..00000000000 --- a/doc/source/i18n.rst +++ /dev/null @@ -1,49 +0,0 @@ -Internationalization -==================== - -Nova uses the `oslo.i18n library -`_ to support -internationalization. The oslo.i18n library is built on top of `gettext -`_ and provides functions that are -used to enable user-facing strings such as log messages to appear in the -appropriate language in different locales. - -Nova exposes the oslo.i18n library support via the ``nova/i18n.py`` integration -module. This module provides the functions needed to wrap translatable strings. -It provides the ``_()`` wrapper for general user-facing messages and specific -wrappers for messages used only for logging. DEBUG level messages do not need -translation but CRITICAL, ERROR, WARNING and INFO messages should be wrapped -with ``_LC()``, ``_LE()``, ``_LW()`` or ``_LI()`` respectively. - -For example:: - - LOG.debug("block_device_mapping %(mapping)s", - {'mapping': block_device_mapping}) - -or:: - - LOG.warn(_LW('Unknown base file %(img)s'), {'img': img}) - -You should use the basic wrapper ``_()`` for strings which are not log -messages:: - - raise nova.SomeException(_('Invalid service catalogue')) - -Do not use ``locals()`` for formatting messages because: -1. It is not as clear as using explicit dicts. -2. It could produce hidden errors during refactoring. -3. Changing the name of a variable causes a change in the message. -4. It creates a lot of otherwise unused variables. - -If you do not follow the project conventions, your code may cause hacking -checks to fail. - -The ``_()``, ``_LC()``, ``_LE()``, ``_LW()`` and ``_LI()`` functions can be -imported with:: - - from nova.i18n import _ - from nova.i18n import _LC - from nova.i18n import _LE - from nova.i18n import _LW - from nova.i18n import _LI - diff --git a/doc/source/image_src/PowerStates.odp b/doc/source/image_src/PowerStates.odp deleted file mode 100644 index e6704b6b957..00000000000 Binary files a/doc/source/image_src/PowerStates.odp and /dev/null differ diff --git a/doc/source/images/PowerStates1.png b/doc/source/images/PowerStates1.png deleted file mode 100644 index ff69d5a6137..00000000000 Binary files a/doc/source/images/PowerStates1.png and /dev/null differ diff --git a/doc/source/images/PowerStates2.png b/doc/source/images/PowerStates2.png deleted file mode 100644 index 0b343e69225..00000000000 Binary files a/doc/source/images/PowerStates2.png and /dev/null differ diff --git a/doc/source/images/run_instance_walkthrough.png b/doc/source/images/run_instance_walkthrough.png deleted file mode 100644 index b9e15143ed8..00000000000 Binary files a/doc/source/images/run_instance_walkthrough.png and /dev/null differ diff --git a/doc/source/index.rst b/doc/source/index.rst index 4e630a5ee4d..dbacb6c53ae 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -15,204 +15,203 @@ License for the specific language governing permissions and limitations under the License. -========================================== -Welcome to Nova's developer documentation! -========================================== +======================== +OpenStack Compute (nova) +======================== -Nova is an OpenStack project designed to provide power massively scalable, on -demand, self service access to compute resources. +What is nova? +============= -The developer documentation provided here is continually kept up-to-date -based on the latest code, and may not represent the state of the project at -any specific prior release. +Nova is the OpenStack project that provides a way to provision compute +instances (aka virtual servers). Nova supports creating virtual machines, +baremetal servers (through the use of ironic), and has limited support for +system containers. Nova runs as a set of daemons on top of existing Linux +servers to provide that service. -.. note:: This is documentation for developers, if you are looking for more - general documentation including API, install, operator and user - guides see `docs.openstack.org`_ +It requires the following additional OpenStack services for basic function: -.. _`docs.openstack.org`: http://docs.openstack.org +* `Keystone `__: This provides + identity and authentication for all OpenStack services. +* `Glance `__: This provides the + compute image repository. All compute instances launch from glance images. +* `Neutron `__: This is + responsible for provisioning the virtual or physical networks that compute + instances connect to on boot. -This documentation is intended to help explain what the Nova developers think -is the current scope of the Nova project, as well as the architectural -decisions we have made in order to support that scope. We also document our -plans for evolving our architecture over time. Finally, we documented our -current development process and policies. +It can also integrate with other services to include: persistent block +storage, encrypted disks, and baremetal compute instances. -Compute API References -====================== +For End Users +============= -Nova has had a v2 API for a long time. We are currently in the process of -moving to a new implementation of that API, which we have called v2.1. v2.1 -started life as an API called v3, but that name should never be used any more. -We are currently in the process of transitioning users over to the v2.1 -implementation, at which point the v2 code will be deleted. +As an end user of nova, you'll use nova to create and manage servers with +either tools or the API directly. -* `v2.1 (CURRENT)`_ -* `v2 (SUPPORTED)`_ and `v2 extensions (SUPPORTED)`_ (Will be deprecated in - the near future.) +Tools for using Nova +-------------------- -Changes to the Compute API post v2.1 are made using microversions. You can see a history of our microversions here: +* `Horizon + `_: The + official web ui for the OpenStack Project. +* `OpenStack Client + `_: The official + CLI for OpenStack Projects. You should use this as your CLI for most things, + it includes not just nova commands but also commands for most of the projects + in OpenStack. +* `Nova Client + `_: For + some very advanced features (or administrative commands) of nova you may need + to use nova client. It is still supported, but the ``openstack`` cli is + recommended. + +Writing to the API +------------------ + +All end user (and some administrative) features of nova are exposed via a REST +API, which can be used to build more complicated logic or automation with +nova. This can be consumed directly, or via various SDKs. The following +resources will help you get started with consuming the API directly. + +* `Compute API Guide: `_: The + concept guide for the API. This helps lay out the concepts behind the API to + make consuming the API reference easier. +* `Compute API Reference `_: + The complete reference for the API, including all methods and request / + response parameters and their meaning. +* :doc:`API Microversion History `: The + compute API evolves over time through `Microversions + `_. This + provides the history of all those changes. Consider it a "what's new" in the + compute API. +* :doc:`Block Device Mapping `: One of the trickier + parts to understand is the Block Device Mapping parameters used to connect + specific block devices to computes. This deserves its own deep dive. +* :doc:`Configuration drive `: Provide information to the + guest instance when it is created. + +For Operators +============= + +Architecture Overview +--------------------- + +* :doc:`Nova architecture `: An overview of how all the parts in + nova fit together. + +Installation +------------ + +.. TODO(sdague): links to all the rest of the install guide pieces. + +The detailed install guide for nova. A functioning nova will also require +having installed `keystone +`__, `glance +`__, and `neutron +`__. Please ensure that you +follow their install guides first. .. toctree:: - :maxdepth: 1 - - api_microversion_history - -We also publish end-user API docs as an API Guide. - -* `Compute API Guide`_ + :maxdepth: 2 -.. _`v2.1 (CURRENT)`: http://developer.openstack.org/api-ref-compute-v2.1.html -.. _`v2 (SUPPORTED)`: http://developer.openstack.org/api-ref-compute-v2.html -.. _`v2 extensions (SUPPORTED)`: http://developer.openstack.org/api-ref-compute-v2-ext.html -.. _`Compute API Guide`: http://developer.openstack.org/api-guide/compute/ - -There was a session on the v2.1 API at the Liberty summit which you can watch -`here `_. + install/index +Deployment Considerations +------------------------- +There is information you might want to consider before doing your deployment, +especially if it is going to be a larger deployment. For smaller deployments +the defaults from the :doc:`install guide ` will be sufficient. -Feature Status -============== +* **Compute Driver Features Supported**: While the majority of nova deployments use + libvirt/kvm, you can use nova with other compute drivers. Nova attempts to + provide a unified feature set across these, however, not all features are + implemented on all backends, and not all features are equally well tested. -Nova aims to have a single compute API that works the same across -all deployments of Nova. -While many features are well-tested, well-documented, support live upgrade, -and are ready for production, some are not. Also the choice of underlying -technology affects the list of features that are ready for production. + * :doc:`Feature Support by Use Case `: A view of + what features each driver supports based on what's important to some large + use cases (General Purpose Cloud, NFV Cloud, HPC Cloud). + * :doc:`Feature Support full list `: A detailed dive through + features in each compute driver backend. -Our first attempt to communicate this is the feature support matrix -(previously called the hypervisor support matrix). -Over time we hope to evolve that to include a classification of each feature's -maturity and exactly what technology combinations are covered by current -integration testing efforts. +* :doc:`Cells v2 Planning `: For large deployments, Cells v2 + allows sharding of your compute environment. Upfront planning is key to a + successful Cells v2 layout. +* :doc:`Placement service `: Overview of the placement + service, including how it fits in with the rest of nova. +* :doc:`Running nova-api on wsgi `: Considerations for using a real + WSGI container instead of the baked-in eventlet web server. -.. toctree:: - :maxdepth: 1 +Maintenance +----------- - test_strategy - feature_classification - support-matrix +Once you are running nova, the following information is extremely useful. -Developer Guide -=============== +* :doc:`Admin Guide `: A collection of guides for administrating + nova. -If you are new to Nova, this should help you start to understand what Nova -actually does, and why. + .. warning:: -.. toctree:: - :maxdepth: 1 + This guide was imported during the Pike cycle and is a bit out of + date. It will be updated during Queens to be more accurate. - how_to_get_involved - process - architecture - project_scope - development.environment +* :doc:`Upgrades `: How nova is designed to be upgraded for minimal + service impact, and the order you should do them in. +* :doc:`Quotas `: Managing project quotas in nova. +* :doc:`Aggregates `: Aggregates are a useful way of grouping + hosts together for scheduling purposes. +* :doc:`Filter Scheduler `: How the filter scheduler is + configured, and how that will impact where compute instances land in your + environment. If you are seeing unexpected distribution of compute instances + in your hosts, you'll want to dive into this configuration. +* :doc:`Exposing custom metadata to compute instances `: How and + when you might want to extend the basic metadata exposed to compute instances + (either via metadata server or config drive) for your specific purposes. -Development Policies --------------------- +Reference Material +------------------ -The Nova community is a large community. We have lots of users, and they all -have a lot of expectations around upgrade and backwards compatibility. -For example, having a good stable API, with discoverable versions and -capabilities is important for maintaining the strong ecosystem around Nova. +* :doc:`Nova CLI Command References `: the complete command reference + for all the daemons and admin tools that come with nova. -Our process is always evolving, just as Nova and the community around Nova -evolves over time. If there are things that seem strange, or you have -ideas on how to improve things, please engage in that debate, so we -continue to improve how the Nova community operates. +* **Configuration**: -This section looks at the processes and why. The main aim behind all the -process is to aid good communication between all members of the Nova -community, while keeping users happy and keeping developers productive. + * :doc:`Configuration Guide `: detailed + configuration guides for various parts of you Nova system. Helpful + reference for setting up specific hypervisor backends. -.. toctree:: - :maxdepth: 1 + * :doc:`Config Reference `: a complete reference of all + configuration options available in the nova.conf file. - process - blueprints - policies + * :doc:`Sample Config File `: a sample config file + with inline documentation. -Architecture Concepts ----------------------- +* **Policy**: nova, like most OpenStack projects, uses a policy language to + restrict permissions on REST API actions. -This follows on for the discussion in the introduction, and digs into -details on specific parts of the Nova architecture. + * :doc:`Policy Reference `: a complete reference of all + policy points in nova and what they impact. -We find it important to document the reasons behind our architectural -decisions, so its easier for people to engage in the debates about -the future of Nova's architecture. This is all part of Open Design and -Open Development. + * :doc:`Sample Policy File `: a sample policy file + with inline documentation. -.. toctree:: - :maxdepth: 1 +For Contributors +================ - aggregates - threading - vmstates - i18n - filter_scheduler - rpc - block_device_mapping - addmethod.openstackapi - conductor - notifications - -Architecture Evolution Plans ------------------------------ - -The following section includes documents that describe the overall plan behind -groups of nova-specs. Most of these cover items relating to the evolution of -various parts of Nova's architecture. Once the work is complete, -these documents will move into the "Concepts" section. -If you want to get involved in shaping the future of Nova's architecture, -these are a great place to start reading up on the current plans. +If you are new to Nova, this should help you start to understand what Nova +actually does, and why. .. toctree:: :maxdepth: 1 - cells - upgrade - api_plugins - api_microversion_dev - policy_enforcement - stable_api - code-review - scheduler_evolution + contributor/index -Advanced testing and guides ----------------------------- +There are also a number of technical references on both current and future +looking parts of our architecture. These are collected below. -.. toctree:: - :maxdepth: 1 - - gmr - testing/libvirt-numa - testing/serial-console - -Sample Configuration File -------------------------- - -.. toctree:: - :maxdepth: 1 - - sample_config - - -Man Pages ----------- - -.. toctree:: - :maxdepth: 1 - - man/index - -Module Reference ----------------- .. toctree:: :maxdepth: 1 - services + reference/index .. # NOTE(mriedem): This is the section where we hide things that we don't @@ -222,11 +221,62 @@ Module Reference .. toctree:: :hidden: - api/autoindex + admin/index + admin/configuration/index + cli/index + configuration/index + configuration/config + configuration/sample-config + configuration/policy + configuration/sample-policy + contributor/development-environment + contributor/api + contributor/api-2 + contributor/blueprints + contributor/code-review + contributor/microversions + contributor/placement.rst + contributor/policies.rst + contributor/releasenotes + contributor/testing + contributor/testing/libvirt-numa + contributor/testing/serial-console + contributor/testing/zero-downtime-upgrade + contributor/how-to-get-involved + contributor/process + contributor/project-scope + reference/api-microversion-history.rst + reference/gmr + reference/i18n + reference/notifications + reference/policy-enforcement + reference/rpc + reference/scheduler-evolution + reference/services + reference/stable-api + reference/threading + reference/vm-states + user/index + user/aggregates + user/architecture + user/block-device-mapping + user/cells + user/cellsv2_layout + user/conductor + user/config-drive + user/feature-classification + user/filter-scheduler + user/manage-ip-addresses + user/placement + user/quotas + user/support-matrix + user/upgrade + user/user-data + user/vendordata + user/wsgi + Indices and tables ================== -* :ref:`genindex` -* :ref:`modindex` * :ref:`search` diff --git a/doc/source/install/compute-install-obs.rst b/doc/source/install/compute-install-obs.rst new file mode 100644 index 00000000000..59508203eb7 --- /dev/null +++ b/doc/source/install/compute-install-obs.rst @@ -0,0 +1,288 @@ +Install and configure a compute node for openSUSE and SUSE Linux Enterprise +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to install and configure the Compute service on a +compute node. The service supports several hypervisors to deploy instances or +virtual machines (VMs). For simplicity, this configuration uses the Quick +EMUlator (QEMU) hypervisor with the kernel-based VM (KVM) extension on compute +nodes that support hardware acceleration for virtual machines. On legacy +hardware, this configuration uses the generic QEMU hypervisor. You can follow +these instructions with minor modifications to horizontally scale your +environment with additional compute nodes. + +.. note:: + + This section assumes that you are following the instructions in this guide + step-by-step to configure the first compute node. If you want to configure + additional compute nodes, prepare them in a similar fashion to the first + compute node in the :ref:`example architectures + ` section. Each additional compute node + requires a unique IP address. + +Install and configure components +-------------------------------- + +.. include:: shared/note_configuration_vary_by_distribution.rst + +#. Install the packages: + + .. code-block:: console + + # zypper install openstack-nova-compute genisoimage qemu-kvm libvirt + +#. Edit the ``/etc/nova/nova.conf`` file and complete the following actions: + + * In the ``[DEFAULT]`` section, enable only the compute and metadata APIs: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + enabled_apis = osapi_compute,metadata + + * In the ``[DEFAULT]`` section, set the ``compute_driver``: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + compute_driver = libvirt.LibvirtDriver + + * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + transport_url = rabbit://openstack:RABBIT_PASS@controller + + Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` + account in ``RabbitMQ``. + + * In the ``[api]`` and ``[keystone_authtoken]`` sections, configure Identity + service access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [api] + # ... + auth_strategy = keystone + + [keystone_authtoken] + # ... + auth_uri = http://controller:5000 + auth_url = http://controller:35357 + memcached_servers = controller:11211 + auth_type = password + project_domain_name = default + user_domain_name = default + project_name = service + username = nova + password = NOVA_PASS + + Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in + the Identity service. + + .. note:: + + Comment out or remove any other options in the ``[keystone_authtoken]`` + section. + + * In the ``[DEFAULT]`` section, configure the ``my_ip`` option: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + my_ip = MANAGEMENT_INTERFACE_IP_ADDRESS + + Replace ``MANAGEMENT_INTERFACE_IP_ADDRESS`` with the IP address of the + management network interface on your compute node, typically ``10.0.0.31`` + for the first node in the :ref:`example architecture + `. + + * In the ``[DEFAULT]`` section, enable support for the Networking service: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + use_neutron = True + firewall_driver = nova.virt.firewall.NoopFirewallDriver + + .. note:: + + By default, Compute uses an internal firewall service. Since + Networking includes a firewall service, you must disable the Compute + firewall service by using the + ``nova.virt.firewall.NoopFirewallDriver`` firewall driver. + + * In the ``[vnc]`` section, enable and configure remote console access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [vnc] + # ... + enabled = True + vncserver_listen = 0.0.0.0 + vncserver_proxyclient_address = $my_ip + novncproxy_base_url = http://controller:6080/vnc_auto.html + + The server component listens on all IP addresses and the proxy + component only listens on the management interface IP address of + the compute node. The base URL indicates the location where you + can use a web browser to access remote consoles of instances + on this compute node. + + .. note:: + + If the web browser to access remote consoles resides on + a host that cannot resolve the ``controller`` hostname, + you must replace ``controller`` with the management + interface IP address of the controller node. + + * In the ``[glance]`` section, configure the location of the Image service + API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [glance] + # ... + api_servers = http://controller:9292 + + * In the ``[oslo_concurrency]`` section, configure the lock path: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [oslo_concurrency] + # ... + lock_path = /var/run/nova + + * In the ``[placement]`` section, configure the Placement API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [placement] + # ... + os_region_name = RegionOne + project_domain_name = Default + project_name = service + auth_type = password + user_domain_name = Default + auth_url = http://controller:35357/v3 + username = placement + password = PLACEMENT_PASS + + Replace ``PLACEMENT_PASS`` with the password you choose for the + ``placement`` user in the Identity service. Comment out any other options + in the ``[placement]`` section. + +#. Ensure the kernel module ``nbd`` is loaded. + + .. code-block:: console + + # modprobe nbd + +#. Ensure the module loads on every boot by adding ``nbd`` to the + ``/etc/modules-load.d/nbd.conf`` file. + +Finalize installation +--------------------- + +#. Determine whether your compute node supports hardware acceleration for + virtual machines: + + .. code-block:: console + + $ egrep -c '(vmx|svm)' /proc/cpuinfo + + If this command returns a value of ``one or greater``, your compute node + supports hardware acceleration which typically requires no additional + configuration. + + If this command returns a value of ``zero``, your compute node does not + support hardware acceleration and you must configure ``libvirt`` to use QEMU + instead of KVM. + + * Edit the ``[libvirt]`` section in the ``/etc/nova/nova.conf`` file as + follows: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [libvirt] + # ... + virt_type = qemu + +#. Start the Compute service including its dependencies and configure them to + start automatically when the system boots: + + .. code-block:: console + + # systemctl enable libvirtd.service openstack-nova-compute.service + # systemctl start libvirtd.service openstack-nova-compute.service + +.. note:: + + If the ``nova-compute`` service fails to start, check + ``/var/log/nova/nova-compute.log``. The error message ``AMQP server on + controller:5672 is unreachable`` likely indicates that the firewall on the + controller node is preventing access to port 5672. Configure the firewall + to open port 5672 on the controller node and restart ``nova-compute`` + service on the compute node. + +Add the compute node to the cell database +----------------------------------------- + +.. important:: + + Run the following commands on the **controller** node. + +#. Source the admin credentials to enable admin-only CLI commands, then confirm + there are compute hosts in the database: + + .. code-block:: console + + $ . admin-openrc + + $ openstack compute service list --service nova-compute + +----+-------+--------------+------+-------+---------+----------------------------+ + | ID | Host | Binary | Zone | State | Status | Updated At | + +----+-------+--------------+------+-------+---------+----------------------------+ + | 1 | node1 | nova-compute | nova | up | enabled | 2017-04-14T15:30:44.000000 | + +----+-------+--------------+------+-------+---------+----------------------------+ + +#. Discover compute hosts: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage cell_v2 discover_hosts --verbose" nova + + Found 2 cell mappings. + Skipping cell0 since it does not contain hosts. + Getting compute nodes from cell 'cell1': ad5a5985-a719-4567-98d8-8d148aaae4bc + Found 1 computes in cell: ad5a5985-a719-4567-98d8-8d148aaae4bc + Checking host mapping for compute host 'compute': fe58ddc1-1d65-4f87-9456-bc040dc106b3 + Creating host mapping for compute host 'compute': fe58ddc1-1d65-4f87-9456-bc040dc106b3 + + .. note:: + + When you add new compute nodes, you must run ``nova-manage cell_v2 + discover_hosts`` on the controller node to register those new compute + nodes. Alternatively, you can set an appropriate interval in + ``/etc/nova/nova.conf``: + + .. code-block:: ini + + [scheduler] + discover_hosts_in_cells_interval = 300 diff --git a/doc/source/install/compute-install-rdo.rst b/doc/source/install/compute-install-rdo.rst new file mode 100644 index 00000000000..5d5b0a6085c --- /dev/null +++ b/doc/source/install/compute-install-rdo.rst @@ -0,0 +1,270 @@ +Install and configure a compute node for Red Hat Enterprise Linux and CentOS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to install and configure the Compute service on a +compute node. The service supports several hypervisors to deploy instances or +virtual machines (VMs). For simplicity, this configuration uses the Quick +EMUlator (QEMU) hypervisor with the kernel-based VM (KVM) extension on compute +nodes that support hardware acceleration for virtual machines. On legacy +hardware, this configuration uses the generic QEMU hypervisor. You can follow +these instructions with minor modifications to horizontally scale your +environment with additional compute nodes. + +.. note:: + + This section assumes that you are following the instructions in this guide + step-by-step to configure the first compute node. If you want to configure + additional compute nodes, prepare them in a similar fashion to the first + compute node in the :ref:`example architectures + ` section. Each additional compute node + requires a unique IP address. + +Install and configure components +-------------------------------- + +.. include:: shared/note_configuration_vary_by_distribution.rst + +#. Install the packages: + + .. code-block:: console + + # yum install openstack-nova-compute + +#. Edit the ``/etc/nova/nova.conf`` file and complete the following actions: + + * In the ``[DEFAULT]`` section, enable only the compute and + metadata APIs: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + enabled_apis = osapi_compute,metadata + + * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + transport_url = rabbit://openstack:RABBIT_PASS@controller + + Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` + account in ``RabbitMQ``. + + * In the ``[api]`` and ``[keystone_authtoken]`` sections, configure Identity + service access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [api] + # ... + auth_strategy = keystone + + [keystone_authtoken] + # ... + auth_uri = http://controller:5000 + auth_url = http://controller:35357 + memcached_servers = controller:11211 + auth_type = password + project_domain_name = default + user_domain_name = default + project_name = service + username = nova + password = NOVA_PASS + + Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in + the Identity service. + + .. note:: + + Comment out or remove any other options in the ``[keystone_authtoken]`` + section. + + * In the ``[DEFAULT]`` section, configure the ``my_ip`` option: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + my_ip = MANAGEMENT_INTERFACE_IP_ADDRESS + + Replace ``MANAGEMENT_INTERFACE_IP_ADDRESS`` with the IP address of the + management network interface on your compute node, typically 10.0.0.31 for + the first node in the :ref:`example architecture + `. + + * In the ``[DEFAULT]`` section, enable support for the Networking service: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + use_neutron = True + firewall_driver = nova.virt.firewall.NoopFirewallDriver + + .. note:: + + By default, Compute uses an internal firewall service. Since Networking + includes a firewall service, you must disable the Compute firewall + service by using the ``nova.virt.firewall.NoopFirewallDriver`` firewall + driver. + + * In the ``[vnc]`` section, enable and configure remote console access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [vnc] + # ... + enabled = True + vncserver_listen = 0.0.0.0 + vncserver_proxyclient_address = $my_ip + novncproxy_base_url = http://controller:6080/vnc_auto.html + + The server component listens on all IP addresses and the proxy component + only listens on the management interface IP address of the compute node. + The base URL indicates the location where you can use a web browser to + access remote consoles of instances on this compute node. + + .. note:: + + If the web browser to access remote consoles resides on a host that + cannot resolve the ``controller`` hostname, you must replace + ``controller`` with the management interface IP address of the + controller node. + + * In the ``[glance]`` section, configure the location of the Image service + API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [glance] + # ... + api_servers = http://controller:9292 + + * In the ``[oslo_concurrency]`` section, configure the lock path: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [oslo_concurrency] + # ... + lock_path = /var/lib/nova/tmp + + * In the ``[placement]`` section, configure the Placement API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [placement] + # ... + os_region_name = RegionOne + project_domain_name = Default + project_name = service + auth_type = password + user_domain_name = Default + auth_url = http://controller:35357/v3 + username = placement + password = PLACEMENT_PASS + + Replace ``PLACEMENT_PASS`` with the password you choose for the + ``placement`` user in the Identity service. Comment out any other options + in the ``[placement]`` section. + +Finalize installation +--------------------- + +#. Determine whether your compute node supports hardware acceleration for + virtual machines: + + .. code-block:: console + + $ egrep -c '(vmx|svm)' /proc/cpuinfo + + If this command returns a value of ``one or greater``, your compute node + supports hardware acceleration which typically requires no additional + configuration. + + If this command returns a value of ``zero``, your compute node does not + support hardware acceleration and you must configure ``libvirt`` to use QEMU + instead of KVM. + + * Edit the ``[libvirt]`` section in the ``/etc/nova/nova.conf`` file as + follows: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [libvirt] + # ... + virt_type = qemu + +#. Start the Compute service including its dependencies and configure them to + start automatically when the system boots: + + .. code-block:: console + + # systemctl enable libvirtd.service openstack-nova-compute.service + # systemctl start libvirtd.service openstack-nova-compute.service + +.. note:: + + If the ``nova-compute`` service fails to start, check + ``/var/log/nova/nova-compute.log``. The error message ``AMQP server on + controller:5672 is unreachable`` likely indicates that the firewall on the + controller node is preventing access to port 5672. Configure the firewall + to open port 5672 on the controller node and restart ``nova-compute`` + service on the compute node. + +Add the compute node to the cell database +----------------------------------------- + +.. important:: + + Run the following commands on the **controller** node. + +#. Source the admin credentials to enable admin-only CLI commands, then confirm + there are compute hosts in the database: + + .. code-block:: console + + $ . admin-openrc + + $ openstack compute service list --service nova-compute + +----+-------+--------------+------+-------+---------+----------------------------+ + | ID | Host | Binary | Zone | State | Status | Updated At | + +----+-------+--------------+------+-------+---------+----------------------------+ + | 1 | node1 | nova-compute | nova | up | enabled | 2017-04-14T15:30:44.000000 | + +----+-------+--------------+------+-------+---------+----------------------------+ + +#. Discover compute hosts: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage cell_v2 discover_hosts --verbose" nova + + Found 2 cell mappings. + Skipping cell0 since it does not contain hosts. + Getting compute nodes from cell 'cell1': ad5a5985-a719-4567-98d8-8d148aaae4bc + Found 1 computes in cell: ad5a5985-a719-4567-98d8-8d148aaae4bc + Checking host mapping for compute host 'compute': fe58ddc1-1d65-4f87-9456-bc040dc106b3 + Creating host mapping for compute host 'compute': fe58ddc1-1d65-4f87-9456-bc040dc106b3 + + .. note:: + + When you add new compute nodes, you must run ``nova-manage cell_v2 + discover_hosts`` on the controller node to register those new compute + nodes. Alternatively, you can set an appropriate interval in + ``/etc/nova/nova.conf``: + + .. code-block:: ini + + [scheduler] + discover_hosts_in_cells_interval = 300 diff --git a/doc/source/install/compute-install-ubuntu.rst b/doc/source/install/compute-install-ubuntu.rst new file mode 100644 index 00000000000..64392797dd1 --- /dev/null +++ b/doc/source/install/compute-install-ubuntu.rst @@ -0,0 +1,265 @@ +Install and configure a compute node for Ubuntu +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to install and configure the Compute service on a +compute node. The service supports several hypervisors to deploy instances or +virtual machines (VMs). For simplicity, this configuration uses the Quick +EMUlator (QEMU) hypervisor with the kernel-based VM (KVM) extension on compute +nodes that support hardware acceleration for virtual machines. On legacy +hardware, this configuration uses the generic QEMU hypervisor. You can follow +these instructions with minor modifications to horizontally scale your +environment with additional compute nodes. + +.. note:: + + This section assumes that you are following the instructions in this guide + step-by-step to configure the first compute node. If you want to configure + additional compute nodes, prepare them in a similar fashion to the first + compute node in the :ref:`example architectures + ` section. Each additional compute node + requires a unique IP address. + +Install and configure components +-------------------------------- + +.. include:: shared/note_configuration_vary_by_distribution.rst + +#. Install the packages: + + .. code-block:: console + + # apt install nova-compute + +2. Edit the ``/etc/nova/nova.conf`` file and complete the following actions: + + * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + transport_url = rabbit://openstack:RABBIT_PASS@controller + + Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` + account in ``RabbitMQ``. + + * In the ``[api]`` and ``[keystone_authtoken]`` sections, configure Identity + service access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [api] + # ... + auth_strategy = keystone + + [keystone_authtoken] + # ... + auth_uri = http://controller:5000 + auth_url = http://controller:35357 + memcached_servers = controller:11211 + auth_type = password + project_domain_name = default + user_domain_name = default + project_name = service + username = nova + password = NOVA_PASS + + Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in + the Identity service. + + .. note:: + + Comment out or remove any other options in the + ``[keystone_authtoken]`` section. + + * In the ``[DEFAULT]`` section, configure the ``my_ip`` option: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + my_ip = MANAGEMENT_INTERFACE_IP_ADDRESS + + Replace ``MANAGEMENT_INTERFACE_IP_ADDRESS`` with the IP address of the + management network interface on your compute node, typically 10.0.0.31 for + the first node in the :ref:`example architecture + `. + + * In the ``[DEFAULT]`` section, enable support for the Networking service: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + use_neutron = True + firewall_driver = nova.virt.firewall.NoopFirewallDriver + + .. note:: + + By default, Compute uses an internal firewall service. Since Networking + includes a firewall service, you must disable the Compute firewall + service by using the ``nova.virt.firewall.NoopFirewallDriver`` firewall + driver. + + * In the ``[vnc]`` section, enable and configure remote console access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [vnc] + # ... + enabled = True + vncserver_listen = 0.0.0.0 + vncserver_proxyclient_address = $my_ip + novncproxy_base_url = http://controller:6080/vnc_auto.html + + The server component listens on all IP addresses and the proxy component + only listens on the management interface IP address of the compute node. + The base URL indicates the location where you can use a web browser to + access remote consoles of instances on this compute node. + + .. note:: + + If the web browser to access remote consoles resides on a host that + cannot resolve the ``controller`` hostname, you must replace + ``controller`` with the management interface IP address of the + controller node. + + * In the ``[glance]`` section, configure the location of the Image service + API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [glance] + # ... + api_servers = http://controller:9292 + + * In the ``[oslo_concurrency]`` section, configure the lock path: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [oslo_concurrency] + # ... + lock_path = /var/lib/nova/tmp + +.. todo:: + + https://bugs.launchpad.net/ubuntu/+source/nova/+bug/1506667 + + * Due to a packaging bug, remove the ``log_dir`` option from the + ``[DEFAULT]`` section. + + * In the ``[placement]`` section, configure the Placement API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [placement] + # ... + os_region_name = RegionOne + project_domain_name = Default + project_name = service + auth_type = password + user_domain_name = Default + auth_url = http://controller:35357/v3 + username = placement + password = PLACEMENT_PASS + + Replace ``PLACEMENT_PASS`` with the password you choose for the + ``placement`` user in the Identity service. Comment out any other options + in the ``[placement]`` section. + +Finalize installation +--------------------- + +#. Determine whether your compute node supports hardware acceleration for + virtual machines: + + .. code-block:: console + + $ egrep -c '(vmx|svm)' /proc/cpuinfo + + If this command returns a value of ``one or greater``, your compute node + supports hardware acceleration which typically requires no additional + configuration. + + If this command returns a value of ``zero``, your compute node does not + support hardware acceleration and you must configure ``libvirt`` to use QEMU + instead of KVM. + + * Edit the ``[libvirt]`` section in the ``/etc/nova/nova-compute.conf`` file as + follows: + + .. path /etc/nova/nova-compute.conf + .. code-block:: ini + + [libvirt] + # ... + virt_type = qemu + +#. Restart the Compute service: + + .. code-block:: console + + # service nova-compute restart + +.. note:: + + If the ``nova-compute`` service fails to start, check + ``/var/log/nova/nova-compute.log``. The error message ``AMQP server on + controller:5672 is unreachable`` likely indicates that the firewall on the + controller node is preventing access to port 5672. Configure the firewall + to open port 5672 on the controller node and restart ``nova-compute`` + service on the compute node. + +Add the compute node to the cell database +----------------------------------------- + +.. important:: + + Run the following commands on the **controller** node. + +#. Source the admin credentials to enable admin-only CLI commands, then confirm + there are compute hosts in the database: + + .. code-block:: console + + $ . admin-openrc + + $ openstack compute service list --service nova-compute + +----+-------+--------------+------+-------+---------+----------------------------+ + | ID | Host | Binary | Zone | State | Status | Updated At | + +----+-------+--------------+------+-------+---------+----------------------------+ + | 1 | node1 | nova-compute | nova | up | enabled | 2017-04-14T15:30:44.000000 | + +----+-------+--------------+------+-------+---------+----------------------------+ + +#. Discover compute hosts: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage cell_v2 discover_hosts --verbose" nova + + Found 2 cell mappings. + Skipping cell0 since it does not contain hosts. + Getting compute nodes from cell 'cell1': ad5a5985-a719-4567-98d8-8d148aaae4bc + Found 1 computes in cell: ad5a5985-a719-4567-98d8-8d148aaae4bc + Checking host mapping for compute host 'compute': fe58ddc1-1d65-4f87-9456-bc040dc106b3 + Creating host mapping for compute host 'compute': fe58ddc1-1d65-4f87-9456-bc040dc106b3 + + .. note:: + + When you add new compute nodes, you must run ``nova-manage cell_v2 + discover_hosts`` on the controller node to register those new compute + nodes. Alternatively, you can set an appropriate interval in + ``/etc/nova/nova.conf``: + + .. code-block:: ini + + [scheduler] + discover_hosts_in_cells_interval = 300 diff --git a/doc/source/install/compute-install.rst b/doc/source/install/compute-install.rst new file mode 100644 index 00000000000..c65eab74b70 --- /dev/null +++ b/doc/source/install/compute-install.rst @@ -0,0 +1,30 @@ +Install and configure a compute node +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to install and configure the Compute service on a +compute node for Ubuntu, openSUSE and SUSE Linux Enterprise, +and Red Hat Enterprise Linux and CentOS. + +The service supports several hypervisors to deploy instances or +virtual machines (VMs). For simplicity, this configuration uses the Quick +EMUlator (QEMU) hypervisor with the kernel-based VM (KVM) extension on compute +nodes that support hardware acceleration for virtual machines. On legacy +hardware, this configuration uses the generic QEMU hypervisor. You can follow +these instructions with minor modifications to horizontally scale your +environment with additional compute nodes. + +.. note:: + + This section assumes that you are following the instructions in this guide + step-by-step to configure the first compute node. If you want to configure + additional compute nodes, prepare them in a similar fashion to the first + compute node in the :ref:`example architectures + ` section. Each additional compute node + requires a unique IP address. + +.. toctree:: + :glob: + + compute-install-ubuntu.rst + compute-install-rdo.rst + compute-install-obs.rst diff --git a/doc/source/install/controller-install-obs.rst b/doc/source/install/controller-install-obs.rst new file mode 100644 index 00000000000..b1481d54e5a --- /dev/null +++ b/doc/source/install/controller-install-obs.rst @@ -0,0 +1,486 @@ +Install and configure controller node for openSUSE and SUSE Linux Enterprise +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to install and configure the Compute service, +code-named nova, on the controller node. + +Prerequisites +------------- + +Before you install and configure the Compute service, you must create +databases, service credentials, and API endpoints. + +#. To create the databases, complete these steps: + + * Use the database access client to connect to the database server as the + ``root`` user: + + .. code-block:: console + + $ mysql -u root -p + + * Create the ``nova_api``, ``nova``, and ``nova_cell0`` databases: + + .. code-block:: console + + MariaDB [(none)]> CREATE DATABASE nova_api; + MariaDB [(none)]> CREATE DATABASE nova; + MariaDB [(none)]> CREATE DATABASE nova_cell0; + + * Grant proper access to the databases: + + .. code-block:: console + + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'localhost' \ + IDENTIFIED BY 'NOVA_DBPASS'; + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%' \ + IDENTIFIED BY 'NOVA_DBPASS'; + + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'localhost' \ + IDENTIFIED BY 'NOVA_DBPASS'; + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%' \ + IDENTIFIED BY 'NOVA_DBPASS'; + + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'localhost' \ + IDENTIFIED BY 'NOVA_DBPASS'; + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%' \ + IDENTIFIED BY 'NOVA_DBPASS'; + + Replace ``NOVA_DBPASS`` with a suitable password. + + * Exit the database access client. + +#. Source the ``admin`` credentials to gain access to admin-only CLI commands: + + .. code-block:: console + + $ . admin-openrc + +#. Create the Compute service credentials: + + * Create the ``nova`` user: + + .. code-block:: console + + $ openstack user create --domain default --password-prompt nova + + User Password: + Repeat User Password: + +---------------------+----------------------------------+ + | Field | Value | + +---------------------+----------------------------------+ + | domain_id | default | + | enabled | True | + | id | 8a7dbf5279404537b1c7b86c033620fe | + | name | nova | + | options | {} | + | password_expires_at | None | + +---------------------+----------------------------------+ + + * Add the ``admin`` role to the ``nova`` user: + + .. code-block:: console + + $ openstack role add --project service --user nova admin + + .. note:: + + This command provides no output. + + * Create the ``nova`` service entity: + + .. code-block:: console + + $ openstack service create --name nova \ + --description "OpenStack Compute" compute + + +-------------+----------------------------------+ + | Field | Value | + +-------------+----------------------------------+ + | description | OpenStack Compute | + | enabled | True | + | id | 060d59eac51b4594815603d75a00aba2 | + | name | nova | + | type | compute | + +-------------+----------------------------------+ + +#. Create the Compute API service endpoints: + + .. code-block:: console + + $ openstack endpoint create --region RegionOne \ + compute public http://controller:8774/v2.1 + + +--------------+-------------------------------------------+ + | Field | Value | + +--------------+-------------------------------------------+ + | enabled | True | + | id | 3c1caa473bfe4390a11e7177894bcc7b | + | interface | public | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 060d59eac51b4594815603d75a00aba2 | + | service_name | nova | + | service_type | compute | + | url | http://controller:8774/v2.1 | + +--------------+-------------------------------------------+ + + $ openstack endpoint create --region RegionOne \ + compute internal http://controller:8774/v2.1 + + +--------------+-------------------------------------------+ + | Field | Value | + +--------------+-------------------------------------------+ + | enabled | True | + | id | e3c918de680746a586eac1f2d9bc10ab | + | interface | internal | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 060d59eac51b4594815603d75a00aba2 | + | service_name | nova | + | service_type | compute | + | url | http://controller:8774/v2.1 | + +--------------+-------------------------------------------+ + + $ openstack endpoint create --region RegionOne \ + compute admin http://controller:8774/v2.1 + + +--------------+-------------------------------------------+ + | Field | Value | + +--------------+-------------------------------------------+ + | enabled | True | + | id | 38f7af91666a47cfb97b4dc790b94424 | + | interface | admin | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 060d59eac51b4594815603d75a00aba2 | + | service_name | nova | + | service_type | compute | + | url | http://controller:8774/v2.1 | + +--------------+-------------------------------------------+ + +#. Create a Placement service user using your chosen ``PLACEMENT_PASS``: + + .. code-block:: console + + $ openstack user create --domain default --password-prompt placement + + User Password: + Repeat User Password: + +---------------------+----------------------------------+ + | Field | Value | + +---------------------+----------------------------------+ + | domain_id | default | + | enabled | True | + | id | fa742015a6494a949f67629884fc7ec8 | + | name | placement | + | options | {} | + | password_expires_at | None | + +---------------------+----------------------------------+ + +#. Add the Placement user to the service project with the admin role: + + .. code-block:: console + + $ openstack role add --project service --user placement admin + + .. note:: + + This command provides no output. + +#. Create the Placement API entry in the service catalog: + + .. code-block:: console + + $ openstack service create --name placement --description "Placement API" placement + +-------------+----------------------------------+ + | Field | Value | + +-------------+----------------------------------+ + | description | Placement API | + | enabled | True | + | id | 2d1a27022e6e4185b86adac4444c495f | + | name | placement | + | type | placement | + +-------------+----------------------------------+ + +#. Create the Placement API service endpoints: + + .. code-block:: console + + $ openstack endpoint create --region RegionOne placement public http://controller:8780 + +--------------+----------------------------------+ + | Field | Value | + +--------------+----------------------------------+ + | enabled | True | + | id | 2b1b2637908b4137a9c2e0470487cbc0 | + | interface | public | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 2d1a27022e6e4185b86adac4444c495f | + | service_name | placement | + | service_type | placement | + | url | http://controller:8780 | + +--------------+----------------------------------+ + + $ openstack endpoint create --region RegionOne placement internal http://controller:8780 + +--------------+----------------------------------+ + | Field | Value | + +--------------+----------------------------------+ + | enabled | True | + | id | 02bcda9a150a4bd7993ff4879df971ab | + | interface | internal | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 2d1a27022e6e4185b86adac4444c495f | + | service_name | placement | + | service_type | placement | + | url | http://controller:8780 | + +--------------+----------------------------------+ + + $ openstack endpoint create --region RegionOne placement admin http://controller:8780 + +--------------+----------------------------------+ + | Field | Value | + +--------------+----------------------------------+ + | enabled | True | + | id | 3d71177b9e0f406f98cbff198d74b182 | + | interface | admin | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 2d1a27022e6e4185b86adac4444c495f | + | service_name | placement | + | service_type | placement | + | url | http://controller:8780 | + +--------------+----------------------------------+ + +Install and configure components +-------------------------------- + +.. include:: shared/note_configuration_vary_by_distribution.rst + +.. note:: + + As of the Newton release, SUSE OpenStack packages are shipped with the + upstream default configuration files. For example, ``/etc/nova/nova.conf`` + has customizations in ``/etc/nova/nova.conf.d/010-nova.conf``. While the + following instructions modify the default configuration file, adding a new + file in ``/etc/nova/nova.conf.d`` achieves the same result. + +#. Install the packages: + + .. code-block:: console + + # zypper install openstack-nova-api openstack-nova-scheduler \ + openstack-nova-conductor openstack-nova-consoleauth \ + openstack-nova-novncproxy openstack-nova-placement-api \ + iptables + +#. Edit the ``/etc/nova/nova.conf`` file and complete the following actions: + + * In the ``[DEFAULT]`` section, enable only the compute and metadata + APIs: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + enabled_apis = osapi_compute,metadata + + * In the ``[api_database]`` and ``[database]`` sections, configure database + access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [api_database] + # ... + connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova_api + + [database] + # ... + connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova + + Replace ``NOVA_DBPASS`` with the password you chose for the Compute + databases. + + * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + transport_url = rabbit://openstack:RABBIT_PASS@controller + + Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` + account in ``RabbitMQ``. + + * In the ``[api]`` and ``[keystone_authtoken]`` sections, configure Identity + service access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [api] + # ... + auth_strategy = keystone + + [keystone_authtoken] + # ... + auth_uri = http://controller:5000 + auth_url = http://controller:35357 + memcached_servers = controller:11211 + auth_type = password + project_domain_name = default + user_domain_name = default + project_name = service + username = nova + password = NOVA_PASS + + Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in + the Identity service. + + .. note:: + + Comment out or remove any other options in the ``[keystone_authtoken]`` + section. + + * In the ``[DEFAULT]`` section, configure the ``my_ip`` option to use the + management interface IP address of the controller node: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + my_ip = 10.0.0.11 + + * In the ``[DEFAULT]`` section, enable support for the Networking service: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + use_neutron = True + firewall_driver = nova.virt.firewall.NoopFirewallDriver + + .. note:: + + By default, Compute uses an internal firewall driver. Since the + Networking service includes a firewall driver, you must disable the + Compute firewall driver by using the + ``nova.virt.firewall.NoopFirewallDriver`` firewall driver. + + * In the ``[vnc]`` section, configure the VNC proxy to use the management + interface IP address of the controller node: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [vnc] + enabled = true + # ... + vncserver_listen = $my_ip + vncserver_proxyclient_address = $my_ip + + * In the ``[glance]`` section, configure the location of the Image service + API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [glance] + # ... + api_servers = http://controller:9292 + + * In the ``[oslo_concurrency]`` section, configure the lock path: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [oslo_concurrency] + # ... + lock_path = /var/run/nova + + * In the ``[placement]`` section, configure the Placement API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [placement] + # ... + os_region_name = RegionOne + project_domain_name = Default + project_name = service + auth_type = password + user_domain_name = Default + auth_url = http://controller:35357/v3 + username = placement + password = PLACEMENT_PASS + + Replace ``PLACEMENT_PASS`` with the password you choose for the + ``placement`` user in the Identity service. Comment out any other options + in the ``[placement]`` section. + +#. Populate the ``nova-api`` database: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage api_db sync" nova + + .. note:: + + Ignore any deprecation messages in this output. + +#. Register the ``cell0`` database: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage cell_v2 map_cell0" nova + +#. Create the ``cell1`` cell: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage cell_v2 create_cell --name=cell1 --verbose" nova + 109e1d4b-536a-40d0-83c6-5f121b82b650 + +#. Populate the nova database: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage db sync" nova + +#. Verify nova cell0 and cell1 are registered correctly: + + .. code-block:: console + + # nova-manage cell_v2 list_cells + +-------+--------------------------------------+ + | Name | UUID | + +-------+--------------------------------------+ + | cell1 | 109e1d4b-536a-40d0-83c6-5f121b82b650 | + | cell0 | 00000000-0000-0000-0000-000000000000 | + +-------+--------------------------------------+ + +Finalize installation +--------------------- + +* Enable the placement API Apache vhost: + + .. code-block:: console + + # mv /etc/apache2/vhosts.d/nova-placement-api.conf.sample /etc/apache2/vhosts.d/nova-placement-api.conf + # systemctl reload apache2.service + +* Start the Compute services and configure them to start when the system boots: + + .. code-block:: console + + # systemctl enable openstack-nova-api.service \ + openstack-nova-consoleauth.service openstack-nova-scheduler.service \ + openstack-nova-conductor.service openstack-nova-novncproxy.service + # systemctl start openstack-nova-api.service \ + openstack-nova-consoleauth.service openstack-nova-scheduler.service \ + openstack-nova-conductor.service openstack-nova-novncproxy.service diff --git a/doc/source/install/controller-install-rdo.rst b/doc/source/install/controller-install-rdo.rst new file mode 100644 index 00000000000..44c4a297eda --- /dev/null +++ b/doc/source/install/controller-install-rdo.rst @@ -0,0 +1,493 @@ +Install and configure controller node for Red Hat Enterprise Linux and CentOS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to install and configure the Compute service, +code-named nova, on the controller node. + +Prerequisites +------------- + +Before you install and configure the Compute service, you must create +databases, service credentials, and API endpoints. + +#. To create the databases, complete these steps: + + * Use the database access client to connect to the database server as the + ``root`` user: + + .. code-block:: console + + $ mysql -u root -p + + * Create the ``nova_api``, ``nova``, and ``nova_cell0`` databases: + + .. code-block:: console + + MariaDB [(none)]> CREATE DATABASE nova_api; + MariaDB [(none)]> CREATE DATABASE nova; + MariaDB [(none)]> CREATE DATABASE nova_cell0; + + * Grant proper access to the databases: + + .. code-block:: console + + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'localhost' \ + IDENTIFIED BY 'NOVA_DBPASS'; + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%' \ + IDENTIFIED BY 'NOVA_DBPASS'; + + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'localhost' \ + IDENTIFIED BY 'NOVA_DBPASS'; + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%' \ + IDENTIFIED BY 'NOVA_DBPASS'; + + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'localhost' \ + IDENTIFIED BY 'NOVA_DBPASS'; + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%' \ + IDENTIFIED BY 'NOVA_DBPASS'; + + Replace ``NOVA_DBPASS`` with a suitable password. + + * Exit the database access client. + +#. Source the ``admin`` credentials to gain access to admin-only CLI commands: + + .. code-block:: console + + $ . admin-openrc + +#. Create the Compute service credentials: + + * Create the ``nova`` user: + + .. code-block:: console + + $ openstack user create --domain default --password-prompt nova + + User Password: + Repeat User Password: + +---------------------+----------------------------------+ + | Field | Value | + +---------------------+----------------------------------+ + | domain_id | default | + | enabled | True | + | id | 8a7dbf5279404537b1c7b86c033620fe | + | name | nova | + | options | {} | + | password_expires_at | None | + +---------------------+----------------------------------+ + + * Add the ``admin`` role to the ``nova`` user: + + .. code-block:: console + + $ openstack role add --project service --user nova admin + + .. note:: + + This command provides no output. + + * Create the ``nova`` service entity: + + .. code-block:: console + + $ openstack service create --name nova \ + --description "OpenStack Compute" compute + + +-------------+----------------------------------+ + | Field | Value | + +-------------+----------------------------------+ + | description | OpenStack Compute | + | enabled | True | + | id | 060d59eac51b4594815603d75a00aba2 | + | name | nova | + | type | compute | + +-------------+----------------------------------+ + +#. Create the Compute API service endpoints: + + .. code-block:: console + + $ openstack endpoint create --region RegionOne \ + compute public http://controller:8774/v2.1 + + +--------------+-------------------------------------------+ + | Field | Value | + +--------------+-------------------------------------------+ + | enabled | True | + | id | 3c1caa473bfe4390a11e7177894bcc7b | + | interface | public | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 060d59eac51b4594815603d75a00aba2 | + | service_name | nova | + | service_type | compute | + | url | http://controller:8774/v2.1 | + +--------------+-------------------------------------------+ + + $ openstack endpoint create --region RegionOne \ + compute internal http://controller:8774/v2.1 + + +--------------+-------------------------------------------+ + | Field | Value | + +--------------+-------------------------------------------+ + | enabled | True | + | id | e3c918de680746a586eac1f2d9bc10ab | + | interface | internal | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 060d59eac51b4594815603d75a00aba2 | + | service_name | nova | + | service_type | compute | + | url | http://controller:8774/v2.1 | + +--------------+-------------------------------------------+ + + $ openstack endpoint create --region RegionOne \ + compute admin http://controller:8774/v2.1 + + +--------------+-------------------------------------------+ + | Field | Value | + +--------------+-------------------------------------------+ + | enabled | True | + | id | 38f7af91666a47cfb97b4dc790b94424 | + | interface | admin | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 060d59eac51b4594815603d75a00aba2 | + | service_name | nova | + | service_type | compute | + | url | http://controller:8774/v2.1 | + +--------------+-------------------------------------------+ + +#. Create a Placement service user using your chosen ``PLACEMENT_PASS``: + + .. code-block:: console + + $ openstack user create --domain default --password-prompt placement + + User Password: + Repeat User Password: + +---------------------+----------------------------------+ + | Field | Value | + +---------------------+----------------------------------+ + | domain_id | default | + | enabled | True | + | id | fa742015a6494a949f67629884fc7ec8 | + | name | placement | + | options | {} | + | password_expires_at | None | + +---------------------+----------------------------------+ + +#. Add the Placement user to the service project with the admin role: + + .. code-block:: console + + $ openstack role add --project service --user placement admin + + .. note:: + + This command provides no output. + +#. Create the Placement API entry in the service catalog: + + .. code-block:: console + + $ openstack service create --name placement --description "Placement API" placement + +-------------+----------------------------------+ + | Field | Value | + +-------------+----------------------------------+ + | description | Placement API | + | enabled | True | + | id | 2d1a27022e6e4185b86adac4444c495f | + | name | placement | + | type | placement | + +-------------+----------------------------------+ + +#. Create the Placement API service endpoints: + + .. code-block:: console + + $ openstack endpoint create --region RegionOne placement public http://controller:8778 + +--------------+----------------------------------+ + | Field | Value | + +--------------+----------------------------------+ + | enabled | True | + | id | 2b1b2637908b4137a9c2e0470487cbc0 | + | interface | public | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 2d1a27022e6e4185b86adac4444c495f | + | service_name | placement | + | service_type | placement | + | url | http://controller:8778 | + +--------------+----------------------------------+ + + $ openstack endpoint create --region RegionOne placement internal http://controller:8778 + +--------------+----------------------------------+ + | Field | Value | + +--------------+----------------------------------+ + | enabled | True | + | id | 02bcda9a150a4bd7993ff4879df971ab | + | interface | internal | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 2d1a27022e6e4185b86adac4444c495f | + | service_name | placement | + | service_type | placement | + | url | http://controller:8778 | + +--------------+----------------------------------+ + + $ openstack endpoint create --region RegionOne placement admin http://controller:8778 + +--------------+----------------------------------+ + | Field | Value | + +--------------+----------------------------------+ + | enabled | True | + | id | 3d71177b9e0f406f98cbff198d74b182 | + | interface | admin | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 2d1a27022e6e4185b86adac4444c495f | + | service_name | placement | + | service_type | placement | + | url | http://controller:8778 | + +--------------+----------------------------------+ + +Install and configure components +-------------------------------- + +.. include:: shared/note_configuration_vary_by_distribution.rst + +#. Install the packages: + + .. code-block:: console + + # yum install openstack-nova-api openstack-nova-conductor \ + openstack-nova-console openstack-nova-novncproxy \ + openstack-nova-scheduler openstack-nova-placement-api + +#. Edit the ``/etc/nova/nova.conf`` file and complete the following actions: + + * In the ``[DEFAULT]`` section, enable only the compute and metadata APIs: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + enabled_apis = osapi_compute,metadata + + * In the ``[api_database]`` and ``[database]`` sections, configure database + access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [api_database] + # ... + connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova_api + + [database] + # ... + connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova + + Replace ``NOVA_DBPASS`` with the password you chose for the Compute + databases. + + * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + transport_url = rabbit://openstack:RABBIT_PASS@controller + + Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` + account in ``RabbitMQ``. + + * In the ``[api]`` and ``[keystone_authtoken]`` sections, configure Identity + service access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [api] + # ... + auth_strategy = keystone + + [keystone_authtoken] + # ... + auth_uri = http://controller:5000 + auth_url = http://controller:35357 + memcached_servers = controller:11211 + auth_type = password + project_domain_name = default + user_domain_name = default + project_name = service + username = nova + password = NOVA_PASS + + Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in + the Identity service. + + .. note:: + + Comment out or remove any other options in the ``[keystone_authtoken]`` + section. + + * In the ``[DEFAULT]`` section, configure the ``my_ip`` option to use the + management interface IP address of the controller node: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + my_ip = 10.0.0.11 + + * In the ``[DEFAULT]`` section, enable support for the Networking service: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + use_neutron = True + firewall_driver = nova.virt.firewall.NoopFirewallDriver + + .. note:: + + By default, Compute uses an internal firewall driver. Since the + Networking service includes a firewall driver, you must disable the + Compute firewall driver by using the + ``nova.virt.firewall.NoopFirewallDriver`` firewall driver. + + * In the ``[vnc]`` section, configure the VNC proxy to use the management + interface IP address of the controller node: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [vnc] + enabled = true + # ... + vncserver_listen = $my_ip + vncserver_proxyclient_address = $my_ip + + * In the ``[glance]`` section, configure the location of the Image service + API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [glance] + # ... + api_servers = http://controller:9292 + + * In the ``[oslo_concurrency]`` section, configure the lock path: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [oslo_concurrency] + # ... + lock_path = /var/lib/nova/tmp + + * In the ``[placement]`` section, configure the Placement API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [placement] + # ... + os_region_name = RegionOne + project_domain_name = Default + project_name = service + auth_type = password + user_domain_name = Default + auth_url = http://controller:35357/v3 + username = placement + password = PLACEMENT_PASS + + Replace ``PLACEMENT_PASS`` with the password you choose for the + ``placement`` user in the Identity service. Comment out any other options + in the ``[placement]`` section. + + * Due to a `packaging bug + `_, you must enable + access to the Placement API by adding the following configuration to + ``/etc/httpd/conf.d/00-nova-placement-api.conf``: + + .. path /etc/httpd/conf.d/00-nova-placement-api.conf + .. code-block:: ini + + + = 2.4> + Require all granted + + + Order allow,deny + Allow from all + + + + * Restart the httpd service: + + .. code-block:: console + + # systemctl restart httpd + +#. Populate the ``nova-api`` database: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage api_db sync" nova + + .. note:: + + Ignore any deprecation messages in this output. + +4. Register the ``cell0`` database: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage cell_v2 map_cell0" nova + +5. Create the ``cell1`` cell: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage cell_v2 create_cell --name=cell1 --verbose" nova + 109e1d4b-536a-40d0-83c6-5f121b82b650 + +6. Populate the nova database: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage db sync" nova + +7. Verify nova cell0 and cell1 are registered correctly: + + .. code-block:: console + + # nova-manage cell_v2 list_cells + +-------+--------------------------------------+ + | Name | UUID | + +-------+--------------------------------------+ + | cell1 | 109e1d4b-536a-40d0-83c6-5f121b82b650 | + | cell0 | 00000000-0000-0000-0000-000000000000 | + +-------+--------------------------------------+ + +Finalize installation +--------------------- + +* Start the Compute services and configure them to start when the system boots: + + .. code-block:: console + + # systemctl enable openstack-nova-api.service \ + openstack-nova-consoleauth.service openstack-nova-scheduler.service \ + openstack-nova-conductor.service openstack-nova-novncproxy.service + # systemctl start openstack-nova-api.service \ + openstack-nova-consoleauth.service openstack-nova-scheduler.service \ + openstack-nova-conductor.service openstack-nova-novncproxy.service diff --git a/doc/source/install/controller-install-ubuntu.rst b/doc/source/install/controller-install-ubuntu.rst new file mode 100644 index 00000000000..497f5145d55 --- /dev/null +++ b/doc/source/install/controller-install-ubuntu.rst @@ -0,0 +1,465 @@ +Install and configure controller node for Ubuntu +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to install and configure the Compute service, +code-named nova, on the controller node. + +Prerequisites +------------- + +Before you install and configure the Compute service, you must create +databases, service credentials, and API endpoints. + +#. To create the databases, complete these steps: + + * Use the database access client to connect to the database + server as the ``root`` user: + + .. code-block:: console + + # mysql + + * Create the ``nova_api``, ``nova``, and ``nova_cell0`` databases: + + .. code-block:: console + + MariaDB [(none)]> CREATE DATABASE nova_api; + MariaDB [(none)]> CREATE DATABASE nova; + MariaDB [(none)]> CREATE DATABASE nova_cell0; + + * Grant proper access to the databases: + + .. code-block:: console + + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'localhost' \ + IDENTIFIED BY 'NOVA_DBPASS'; + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%' \ + IDENTIFIED BY 'NOVA_DBPASS'; + + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'localhost' \ + IDENTIFIED BY 'NOVA_DBPASS'; + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%' \ + IDENTIFIED BY 'NOVA_DBPASS'; + + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'localhost' \ + IDENTIFIED BY 'NOVA_DBPASS'; + MariaDB [(none)]> GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%' \ + IDENTIFIED BY 'NOVA_DBPASS'; + + Replace ``NOVA_DBPASS`` with a suitable password. + + * Exit the database access client. + +#. Source the ``admin`` credentials to gain access to admin-only CLI commands: + + .. code-block:: console + + $ . admin-openrc + +#. Create the Compute service credentials: + + * Create the ``nova`` user: + + .. code-block:: console + + $ openstack user create --domain default --password-prompt nova + + User Password: + Repeat User Password: + +---------------------+----------------------------------+ + | Field | Value | + +---------------------+----------------------------------+ + | domain_id | default | + | enabled | True | + | id | 8a7dbf5279404537b1c7b86c033620fe | + | name | nova | + | options | {} | + | password_expires_at | None | + +---------------------+----------------------------------+ + + * Add the ``admin`` role to the ``nova`` user: + + .. code-block:: console + + $ openstack role add --project service --user nova admin + + .. note:: + + This command provides no output. + + * Create the ``nova`` service entity: + + .. code-block:: console + + $ openstack service create --name nova \ + --description "OpenStack Compute" compute + + +-------------+----------------------------------+ + | Field | Value | + +-------------+----------------------------------+ + | description | OpenStack Compute | + | enabled | True | + | id | 060d59eac51b4594815603d75a00aba2 | + | name | nova | + | type | compute | + +-------------+----------------------------------+ + +#. Create the Compute API service endpoints: + + .. code-block:: console + + $ openstack endpoint create --region RegionOne \ + compute public http://controller:8774/v2.1 + + +--------------+-------------------------------------------+ + | Field | Value | + +--------------+-------------------------------------------+ + | enabled | True | + | id | 3c1caa473bfe4390a11e7177894bcc7b | + | interface | public | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 060d59eac51b4594815603d75a00aba2 | + | service_name | nova | + | service_type | compute | + | url | http://controller:8774/v2.1 | + +--------------+-------------------------------------------+ + + $ openstack endpoint create --region RegionOne \ + compute internal http://controller:8774/v2.1 + + +--------------+-------------------------------------------+ + | Field | Value | + +--------------+-------------------------------------------+ + | enabled | True | + | id | e3c918de680746a586eac1f2d9bc10ab | + | interface | internal | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 060d59eac51b4594815603d75a00aba2 | + | service_name | nova | + | service_type | compute | + | url | http://controller:8774/v2.1 | + +--------------+-------------------------------------------+ + + $ openstack endpoint create --region RegionOne \ + compute admin http://controller:8774/v2.1 + + +--------------+-------------------------------------------+ + | Field | Value | + +--------------+-------------------------------------------+ + | enabled | True | + | id | 38f7af91666a47cfb97b4dc790b94424 | + | interface | admin | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 060d59eac51b4594815603d75a00aba2 | + | service_name | nova | + | service_type | compute | + | url | http://controller:8774/v2.1 | + +--------------+-------------------------------------------+ + +#. Create a Placement service user using your chosen ``PLACEMENT_PASS``: + + .. code-block:: console + + $ openstack user create --domain default --password-prompt placement + + User Password: + Repeat User Password: + +---------------------+----------------------------------+ + | Field | Value | + +---------------------+----------------------------------+ + | domain_id | default | + | enabled | True | + | id | fa742015a6494a949f67629884fc7ec8 | + | name | placement | + | options | {} | + | password_expires_at | None | + +---------------------+----------------------------------+ + +#. Add the Placement user to the service project with the admin role: + + .. code-block:: console + + $ openstack role add --project service --user placement admin + + .. note:: + + This command provides no output. + +#. Create the Placement API entry in the service catalog: + + .. code-block:: console + + $ openstack service create --name placement --description "Placement API" placement + +-------------+----------------------------------+ + | Field | Value | + +-------------+----------------------------------+ + | description | Placement API | + | enabled | True | + | id | 2d1a27022e6e4185b86adac4444c495f | + | name | placement | + | type | placement | + +-------------+----------------------------------+ + +#. Create the Placement API service endpoints: + + .. code-block:: console + + $ openstack endpoint create --region RegionOne placement public http://controller:8778 + +--------------+----------------------------------+ + | Field | Value | + +--------------+----------------------------------+ + | enabled | True | + | id | 2b1b2637908b4137a9c2e0470487cbc0 | + | interface | public | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 2d1a27022e6e4185b86adac4444c495f | + | service_name | placement | + | service_type | placement | + | url | http://controller:8778 | + +--------------+----------------------------------+ + + $ openstack endpoint create --region RegionOne placement internal http://controller:8778 + +--------------+----------------------------------+ + | Field | Value | + +--------------+----------------------------------+ + | enabled | True | + | id | 02bcda9a150a4bd7993ff4879df971ab | + | interface | internal | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 2d1a27022e6e4185b86adac4444c495f | + | service_name | placement | + | service_type | placement | + | url | http://controller:8778 | + +--------------+----------------------------------+ + + $ openstack endpoint create --region RegionOne placement admin http://controller:8778 + +--------------+----------------------------------+ + | Field | Value | + +--------------+----------------------------------+ + | enabled | True | + | id | 3d71177b9e0f406f98cbff198d74b182 | + | interface | admin | + | region | RegionOne | + | region_id | RegionOne | + | service_id | 2d1a27022e6e4185b86adac4444c495f | + | service_name | placement | + | service_type | placement | + | url | http://controller:8778 | + +--------------+----------------------------------+ + +Install and configure components +-------------------------------- + +.. include:: shared/note_configuration_vary_by_distribution.rst + +#. Install the packages: + + .. code-block:: console + + # apt install nova-api nova-conductor nova-consoleauth \ + nova-novncproxy nova-scheduler nova-placement-api + +#. Edit the ``/etc/nova/nova.conf`` file and complete the following actions: + + * In the ``[api_database]`` and ``[database]`` sections, configure database + access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [api_database] + # ... + connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova_api + + [database] + # ... + connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova + + Replace ``NOVA_DBPASS`` with the password you chose for the Compute + databases. + + * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + transport_url = rabbit://openstack:RABBIT_PASS@controller + + Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` + account in ``RabbitMQ``. + + * In the ``[api]`` and ``[keystone_authtoken]`` sections, configure Identity + service access: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [api] + # ... + auth_strategy = keystone + + [keystone_authtoken] + # ... + auth_uri = http://controller:5000 + auth_url = http://controller:35357 + memcached_servers = controller:11211 + auth_type = password + project_domain_name = default + user_domain_name = default + project_name = service + username = nova + password = NOVA_PASS + + Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in + the Identity service. + + .. note:: + + Comment out or remove any other options in the ``[keystone_authtoken]`` + section. + + * In the ``[DEFAULT]`` section, configure the ``my_ip`` option to use the + management interface IP address of the controller node: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + my_ip = 10.0.0.11 + + * In the ``[DEFAULT]`` section, enable support for the Networking service: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [DEFAULT] + # ... + use_neutron = True + firewall_driver = nova.virt.firewall.NoopFirewallDriver + + .. note:: + + By default, Compute uses an internal firewall driver. Since the + Networking service includes a firewall driver, you must disable the + Compute firewall driver by using the + ``nova.virt.firewall.NoopFirewallDriver`` firewall driver. + + * In the ``[vnc]`` section, configure the VNC proxy to use the management + interface IP address of the controller node: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [vnc] + enabled = true + # ... + vncserver_listen = $my_ip + vncserver_proxyclient_address = $my_ip + + * In the ``[glance]`` section, configure the location of the Image service + API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [glance] + # ... + api_servers = http://controller:9292 + + * In the ``[oslo_concurrency]`` section, configure the lock path: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [oslo_concurrency] + # ... + lock_path = /var/lib/nova/tmp + + .. todo:: + + https://bugs.launchpad.net/ubuntu/+source/nova/+bug/1506667 + + * Due to a packaging bug, remove the ``log_dir`` option from the + ``[DEFAULT]`` section. + + * In the ``[placement]`` section, configure the Placement API: + + .. path /etc/nova/nova.conf + .. code-block:: ini + + [placement] + # ... + os_region_name = RegionOne + project_domain_name = Default + project_name = service + auth_type = password + user_domain_name = Default + auth_url = http://controller:35357/v3 + username = placement + password = PLACEMENT_PASS + + Replace ``PLACEMENT_PASS`` with the password you choose for the + ``placement`` user in the Identity service. Comment out any other options + in the ``[placement]`` section. + +#. Populate the nova-api database: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage api_db sync" nova + + .. note:: + + Ignore any deprecation messages in this output. + +#. Register the ``cell0`` database: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage cell_v2 map_cell0" nova + +#. Create the ``cell1`` cell: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage cell_v2 create_cell --name=cell1 --verbose" nova + 109e1d4b-536a-40d0-83c6-5f121b82b650 + +#. Populate the nova database: + + .. code-block:: console + + # su -s /bin/sh -c "nova-manage db sync" nova + +#. Verify nova cell0 and cell1 are registered correctly: + + .. code-block:: console + + # nova-manage cell_v2 list_cells + +-------+--------------------------------------+ + | Name | UUID | + +-------+--------------------------------------+ + | cell1 | 109e1d4b-536a-40d0-83c6-5f121b82b650 | + | cell0 | 00000000-0000-0000-0000-000000000000 | + +-------+--------------------------------------+ + +Finalize installation +--------------------- + +* Restart the Compute services: + + .. code-block:: console + + # service nova-api restart + # service nova-consoleauth restart + # service nova-scheduler restart + # service nova-conductor restart + # service nova-novncproxy restart diff --git a/doc/source/install/controller-install.rst b/doc/source/install/controller-install.rst new file mode 100644 index 00000000000..4f09ea38427 --- /dev/null +++ b/doc/source/install/controller-install.rst @@ -0,0 +1,12 @@ +Install and configure controller node +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes how to install and configure the Compute service +on the controller node for Ubuntu, openSUSE and SUSE Linux Enterprise, +and Red Hat Enterprise Linux and CentOS. + +.. toctree:: + + controller-install-ubuntu.rst + controller-install-obs.rst + controller-install-rdo.rst diff --git a/doc/source/install/figures/hwreqs.graffle b/doc/source/install/figures/hwreqs.graffle new file mode 100644 index 00000000000..522bb03cba5 Binary files /dev/null and b/doc/source/install/figures/hwreqs.graffle differ diff --git a/doc/source/install/figures/hwreqs.png b/doc/source/install/figures/hwreqs.png new file mode 100644 index 00000000000..5c7e2d0e8bf Binary files /dev/null and b/doc/source/install/figures/hwreqs.png differ diff --git a/doc/source/install/figures/hwreqs.svg b/doc/source/install/figures/hwreqs.svg new file mode 100644 index 00000000000..0b58db752fe --- /dev/null +++ b/doc/source/install/figures/hwreqs.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.5.2 2016-04-26 14:57:28 +0000Canvas 1Layer 1Controller NodeCompute Node 11-2CPUBlock Storage Node 1Object Storage Node 1Object Storage Node 2Hardware RequirementsCore componentOptional component8 GBRAM100 GBStorage2-4+CPU8+ GBRAM100+ GBStorage1-2CPU4 GBRAM2NIC2NIC1NIC1NIC4+ GBRAM1-2CPU1NIC100+ GBStorage100+ GBStorage/dev/sdb/dev/sdb/dev/sdc/dev/sdb/dev/sdc1-2CPU4+ GBRAM100+ GBStorage/dev/sdc diff --git a/doc/source/install/figures/network1-services.graffle b/doc/source/install/figures/network1-services.graffle new file mode 100644 index 00000000000..3e5bea9c616 Binary files /dev/null and b/doc/source/install/figures/network1-services.graffle differ diff --git a/doc/source/install/figures/network1-services.png b/doc/source/install/figures/network1-services.png new file mode 100644 index 00000000000..e83bf5bbf6d Binary files /dev/null and b/doc/source/install/figures/network1-services.png differ diff --git a/doc/source/install/figures/network1-services.svg b/doc/source/install/figures/network1-services.svg new file mode 100644 index 00000000000..153385b16a6 --- /dev/null +++ b/doc/source/install/figures/network1-services.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.5.2 2016-04-26 14:56:09 +0000Canvas 1Layer 1 Controller NodeSQL DatabaseServiceBlock Storage Nodes Object Storage NodesNetworking Option 1: Provider NetworksService LayoutCore componentOptional componentMessage QueueIdentityImage ServiceComputeManagementNetworkingManagementBlock StorageManagementNetwork Time ServiceOrchestrationTelemetryManagementObject StorageProxy ServiceNetworkingDHCP Agent Compute NodesKVM HypervisorComputeNetworkingLinux Bridge AgentTelemetryAgentTelemetryAgent(s)NetworkingML2 Plug-inObject StorageAccount ServiceObject StorageContainer ServiceObject StorageObject ServiceBlock StorageVolume ServiceTelemetryAgentiSCSI TargetServiceNetworkingLinux Bridge AgentLinux NetworkUtilitiesLinux NetworkUtilitiesShared File SystemServiceShared File SystemManagementNoSQL DatabaseServiceNetworkingMetadata AgentDatabaseManagement diff --git a/doc/source/install/figures/network2-services.graffle b/doc/source/install/figures/network2-services.graffle new file mode 100644 index 00000000000..3642050ea6f Binary files /dev/null and b/doc/source/install/figures/network2-services.graffle differ diff --git a/doc/source/install/figures/network2-services.png b/doc/source/install/figures/network2-services.png new file mode 100644 index 00000000000..72b1fc915bf Binary files /dev/null and b/doc/source/install/figures/network2-services.png differ diff --git a/doc/source/install/figures/network2-services.svg b/doc/source/install/figures/network2-services.svg new file mode 100644 index 00000000000..4ff05a0c904 --- /dev/null +++ b/doc/source/install/figures/network2-services.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.5.2 2016-04-26 14:55:33 +0000Canvas 1Layer 1 Controller NodeSQL DatabaseServiceBlock Storage Nodes Object Storage NodesNetworking Option 2: Self-Service NetworksService LayoutCore componentOptional componentMessage QueueIdentityImage ServiceComputeManagementNetworkingManagementBlock StorageManagementNetwork Time ServiceOrchestrationDatabaseManagementObject StorageProxy ServiceNetworkingL3 AgentNetworkingDHCP Agent Compute NodesKVM HypervisorComputeNetworkingLinux Bridge AgentTelemetryAgentTelemetryAgent(s)NetworkingML2 Plug-inObject StorageAccount ServiceObject StorageContainer ServiceObject StorageObject ServiceBlock StorageVolume ServiceShared File SystemServiceiSCSI TargetServiceNetworkingMetadata AgentNetworkingLinux Bridge AgentLinux NetworkUtilitiesLinux NetworkUtilitiesShared File SystemManagementTelemetryAgentNoSQL DatabaseServiceTelemetryManagement diff --git a/doc/source/install/get-started-compute.rst b/doc/source/install/get-started-compute.rst new file mode 100644 index 00000000000..22e18be2b5c --- /dev/null +++ b/doc/source/install/get-started-compute.rst @@ -0,0 +1,100 @@ +======================== +Compute service overview +======================== + +.. todo:: Update a lot of the links in here. + +Use OpenStack Compute to host and manage cloud computing systems. OpenStack +Compute is a major part of an Infrastructure-as-a-Service (IaaS) system. The +main modules are implemented in Python. + +OpenStack Compute interacts with OpenStack Identity for authentication; +OpenStack Image service for disk and server images; and OpenStack Dashboard for +the user and administrative interface. Image access is limited by projects, and +by users; quotas are limited per project (the number of instances, for +example). OpenStack Compute can scale horizontally on standard hardware, and +download images to launch instances. + +OpenStack Compute consists of the following areas and their components: + +``nova-api`` service + Accepts and responds to end user compute API calls. The service supports the + OpenStack Compute API, the Amazon EC2 API, and a special Admin API for + privileged users to perform administrative actions. It enforces some policies + and initiates most orchestration activities, such as running an instance. + +``nova-api-metadata`` service + Accepts metadata requests from instances. The ``nova-api-metadata`` service + is generally used when you run in multi-host mode with ``nova-network`` + installations. For details, see :ref:`metadata-service` + in the Compute Administrator Guide. + +``nova-compute`` service + A worker daemon that creates and terminates virtual machine instances through + hypervisor APIs. For example: + + - XenAPI for XenServer/XCP + + - libvirt for KVM or QEMU + + - VMwareAPI for VMware + + Processing is fairly complex. Basically, the daemon accepts actions from the + queue and performs a series of system commands such as launching a KVM + instance and updating its state in the database. + +``nova-placement-api`` service + Tracks the inventory and usage of each provider. For details, see `Placement + API `__. + +``nova-scheduler`` service + Takes a virtual machine instance request from the queue and determines on + which compute server host it runs. + +``nova-conductor`` module + Mediates interactions between the ``nova-compute`` service and the database. + It eliminates direct accesses to the cloud database made by the + ``nova-compute`` service. The ``nova-conductor`` module scales horizontally. + However, do not deploy it on nodes where the ``nova-compute`` service runs. + For more information, see the ``conductor`` section in the + :doc:`/configuration/config`. + +``nova-consoleauth`` daemon + Authorizes tokens for users that console proxies provide. See + ``nova-novncproxy`` and ``nova-xvpvncproxy``. This service must be running + for console proxies to work. You can run proxies of either type against a + single nova-consoleauth service in a cluster configuration. For information, + see :ref:`about-nova-consoleauth`. + +``nova-novncproxy`` daemon + Provides a proxy for accessing running instances through a VNC connection. + Supports browser-based novnc clients. + +``nova-spicehtml5proxy`` daemon + Provides a proxy for accessing running instances through a SPICE connection. + Supports browser-based HTML5 client. + +``nova-xvpvncproxy`` daemon + Provides a proxy for accessing running instances through a VNC connection. + Supports an OpenStack-specific Java client. + +The queue + A central hub for passing messages between daemons. Usually implemented with + `RabbitMQ `__, also can be implemented with + another AMQP message queue, such as `ZeroMQ `__. + +SQL database + Stores most build-time and run-time states for a cloud infrastructure, + including: + + - Available instance types + + - Instances in use + + - Available networks + + - Projects + + Theoretically, OpenStack Compute can support any database that SQLAlchemy + supports. Common databases are SQLite3 for test and development work, MySQL, + MariaDB, and PostgreSQL. diff --git a/doc/source/install/index.rst b/doc/source/install/index.rst new file mode 100644 index 00000000000..ee6c0a7c42a --- /dev/null +++ b/doc/source/install/index.rst @@ -0,0 +1,11 @@ +=============== +Compute service +=============== + +.. toctree:: + + overview.rst + get-started-compute.rst + controller-install.rst + compute-install.rst + verify.rst diff --git a/doc/source/install/overview.rst b/doc/source/install/overview.rst new file mode 100644 index 00000000000..c8ff1414640 --- /dev/null +++ b/doc/source/install/overview.rst @@ -0,0 +1,174 @@ +======== +Overview +======== + +The OpenStack project is an open source cloud computing platform that supports +all types of cloud environments. The project aims for simple implementation, +massive scalability, and a rich set of features. Cloud computing experts from +around the world contribute to the project. + +OpenStack provides an Infrastructure-as-a-Service (IaaS) solution through a +variety of complementary services. Each service offers an Application +Programming Interface (API) that facilitates this integration. + +This guide covers step-by-step deployment of the major OpenStack services using +a functional example architecture suitable for new users of OpenStack with +sufficient Linux experience. This guide is not intended to be used for +production system installations, but to create a minimum proof-of-concept for +the purpose of learning about OpenStack. + +After becoming familiar with basic installation, configuration, operation, and +troubleshooting of these OpenStack services, you should consider the following +steps toward deployment using a production architecture: + +* Determine and implement the necessary core and optional services to meet + performance and redundancy requirements. + +* Increase security using methods such as firewalls, encryption, and service + policies. + +* Implement a deployment tool such as Ansible, Chef, Puppet, or Salt to + automate deployment and management of the production environment. + +.. _overview-example-architectures: + +Example architecture +~~~~~~~~~~~~~~~~~~~~ + +The example architecture requires at least two nodes (hosts) to launch a basic +virtual machine (VM) or instance. Optional services such as Block Storage and +Object Storage require additional nodes. + +.. important:: + + The example architecture used in this guide is a minimum configuration, and + is not intended for production system installations. It is designed to + provide a minimum proof-of-concept for the purpose of learning about + OpenStack. For information on creating architectures for specific use cases, + or how to determine which architecture is required, see the `Architecture + Design Guide `_. + +This example architecture differs from a minimal production architecture as +follows: + +* Networking agents reside on the controller node instead of one or more + dedicated network nodes. + +* Overlay (tunnel) traffic for self-service networks traverses the management + network instead of a dedicated network. + +For more information on production architectures, see the `Architecture Design +Guide `_, `OpenStack Operations Guide +`_, and `OpenStack Networking Guide +`_. + +.. _figure-hwreqs: + +.. figure:: figures/hwreqs.png + :alt: Hardware requirements + + **Hardware requirements** + +Controller +---------- + +The controller node runs the Identity service, Image service, management +portions of Compute, management portion of Networking, various Networking +agents, and the Dashboard. It also includes supporting services such as an SQL +database, message queue, and Network Time Protocol (NTP). + +Optionally, the controller node runs portions of the Block Storage, Object +Storage, Orchestration, and Telemetry services. + +The controller node requires a minimum of two network interfaces. + +Compute +------- + +The compute node runs the hypervisor portion of Compute that operates +instances. By default, Compute uses the kernel-based VM (KVM) hypervisor. The +compute node also runs a Networking service agent that connects instances to +virtual networks and provides firewalling services to instances via security +groups. + +You can deploy more than one compute node. Each node requires a minimum of two +network interfaces. + +Block Storage +------------- + +The optional Block Storage node contains the disks that the Block Storage and +Shared File System services provision for instances. + +For simplicity, service traffic between compute nodes and this node uses the +management network. Production environments should implement a separate storage +network to increase performance and security. + +You can deploy more than one block storage node. Each node requires a minimum +of one network interface. + +Object Storage +-------------- + +The optional Object Storage node contain the disks that the Object Storage +service uses for storing accounts, containers, and objects. + +For simplicity, service traffic between compute nodes and this node uses the +management network. Production environments should implement a separate storage +network to increase performance and security. + +This service requires two nodes. Each node requires a minimum of one network +interface. You can deploy more than two object storage nodes. + +Networking +~~~~~~~~~~ + +Choose one of the following virtual networking options. + +.. _network1: + +Networking Option 1: Provider networks +-------------------------------------- + +The provider networks option deploys the OpenStack Networking service in the +simplest way possible with primarily layer-2 (bridging/switching) services and +VLAN segmentation of networks. Essentially, it bridges virtual networks to +physical networks and relies on physical network infrastructure for layer-3 +(routing) services. Additionally, a DHCP`__ - -BUGS -==== - -* Nova bugs are managed at Launchpad `Bugs : Nova `__ diff --git a/doc/source/man/nova-api.rst b/doc/source/man/nova-api.rst deleted file mode 100644 index 17a2f33c8a5..00000000000 --- a/doc/source/man/nova-api.rst +++ /dev/null @@ -1,48 +0,0 @@ -======== -nova-api -======== - -------------------------------------------- -Server for the Nova EC2 and OpenStack APIs -------------------------------------------- - -:Author: openstack@lists.openstack.org -:Date: 2012-09-27 -:Copyright: OpenStack Foundation -:Version: 2012.1 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - nova-api [options] - -DESCRIPTION -=========== - -nova-api is a server daemon that serves the nova EC2 and OpenStack APIs in separate greenthreads - -OPTIONS -======= - - **General options** - -FILES -======== - -* /etc/nova/nova.conf -* /etc/nova/api-paste.ini -* /etc/nova/policy.json -* /etc/nova/rootwrap.conf -* /etc/nova/rootwrap.d/ - -SEE ALSO -======== - -* `OpenStack Nova `__ - -BUGS -==== - -* Nova bugs are managed at Launchpad `Bugs : Nova `__ diff --git a/doc/source/man/nova-cells.rst b/doc/source/man/nova-cells.rst deleted file mode 100644 index 7eaaf1d358d..00000000000 --- a/doc/source/man/nova-cells.rst +++ /dev/null @@ -1,49 +0,0 @@ -========== -nova-cells -========== - --------------------------------- -Server for the Nova Cells --------------------------------- - -:Author: openstack@lists.openstack.org -:Date: 2012-09-27 -:Copyright: OpenStack Foundation -:Version: 2012.1 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - nova-cells [options] - -DESCRIPTION -=========== - -Starts the nova-cells service. - -The nova-cells service handles communication between cells and selects cells for new instances. - -OPTIONS -======= - - **General options** - -FILES -======== - -* /etc/nova/nova.conf -* /etc/nova/policy.json -* /etc/nova/rootwrap.conf -* /etc/nova/rootwrap.d/ - -SEE ALSO -======== - -* `OpenStack Nova `__ - -BUGS -==== - -* Nova bugs are managed at Launchpad `Bugs : Nova `__ diff --git a/doc/source/man/nova-cert.rst b/doc/source/man/nova-cert.rst deleted file mode 100644 index ad7a7b355a6..00000000000 --- a/doc/source/man/nova-cert.rst +++ /dev/null @@ -1,47 +0,0 @@ -========== -nova-cert -========== - --------------------------------- -Server for the Nova Cert --------------------------------- - -:Author: openstack@lists.openstack.org -:Date: 2012-09-27 -:Copyright: OpenStack Foundation -:Version: 2012.1 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - nova-cert [options] - -DESCRIPTION -=========== - -nova-cert is a server daemon that serves the Nova Cert service for X509 certificates. Used to generate certificates for euca-bundle-image. Only needed for EC2 API. - -OPTIONS -======= - - **General options** - -FILES -======== - -* /etc/nova/nova.conf -* /etc/nova/policy.json -* /etc/nova/rootwrap.conf -* /etc/nova/rootwrap.d/ - -SEE ALSO -======== - -* `OpenStack Nova `__ - -BUGS -==== - -* Nova bugs are managed at Launchpad `Bugs : Nova `__ diff --git a/doc/source/man/nova-manage.rst b/doc/source/man/nova-manage.rst deleted file mode 100644 index 49fd3abb2f7..00000000000 --- a/doc/source/man/nova-manage.rst +++ /dev/null @@ -1,183 +0,0 @@ -=========== -nova-manage -=========== - ------------------------------------------------------- -control and manage cloud computer instances and images ------------------------------------------------------- - -:Author: openstack@lists.openstack.org -:Date: 2012-04-05 -:Copyright: OpenStack Foundation -:Version: 2012.1 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - nova-manage [] - -DESCRIPTION -=========== - -nova-manage controls cloud computing instances by managing shell selection, vpn connections, and floating IP address configuration. More information about OpenStack Nova is at http://nova.openstack.org. - -OPTIONS -======= - -The standard pattern for executing a nova-manage command is: -``nova-manage []`` - -Run without arguments to see a list of available command categories: -``nova-manage`` - -Categories are project, shell, vpn, and floating. Detailed descriptions are below. - -You can also run with a category argument such as user to see a list of all commands in that category: -``nova-manage floating`` - -These sections describe the available categories and arguments for nova-manage. - -Nova Db -~~~~~~~ - -``nova-manage db version`` - - Print the current main database version. - -``nova-manage db sync`` - - Sync the main database up to the most recent version. This is the standard way to create the db as well. - -``nova-manage db archive_deleted_rows [--max_rows ] [--verbose]`` - - Move deleted rows from production tables to shadow tables. Specifying - --verbose will print the results of the archive operation for any tables - that were changed. - -``nova-manage db null_instance_uuid_scan [--delete]`` - - Lists and optionally deletes database records where instance_uuid is NULL. - -Nova ApiDb -~~~~~~~~~~ - -``nova-manage api_db version`` - - Print the current cells api database version. - -``nova-manage api_db sync`` - - Sync the api cells database up to the most recent version. This is the standard way to create the db as well. - -Nova Logs -~~~~~~~~~ - -``nova-manage logs errors`` - - Displays nova errors from log files. - -``nova-manage logs syslog `` - - Displays nova alerts from syslog. - -Nova Shell -~~~~~~~~~~ - -``nova-manage shell bpython`` - - Starts a new bpython shell. - -``nova-manage shell ipython`` - - Starts a new ipython shell. - -``nova-manage shell python`` - - Starts a new python shell. - -``nova-manage shell run`` - - Starts a new shell using python. - -``nova-manage shell script `` - - Runs the named script from the specified path with flags set. - -Nova VPN -~~~~~~~~ - -``nova-manage vpn list`` - - Displays a list of projects, their IP port numbers, and what state they're in. - -``nova-manage vpn run `` - - Starts the VPN for the named project. - -``nova-manage vpn spawn`` - - Runs all VPNs. - -Nova Floating IPs -~~~~~~~~~~~~~~~~~ - -``nova-manage floating create [--pool ] [--interface ]`` - - Creates floating IP addresses for the given range, optionally specifying - a floating pool and a network interface. - -``nova-manage floating delete `` - - Deletes floating IP addresses in the range given. - -``nova-manage floating list`` - - Displays a list of all floating IP addresses. - -Nova Images -~~~~~~~~~~~ - -``nova-manage image image_register `` - - Registers an image with the image service. - -``nova-manage image kernel_register `` - - Registers a kernel with the image service. - -``nova-manage image ramdisk_register `` - - Registers a ramdisk with the image service. - -``nova-manage image all_register `` - - Registers an image kernel and ramdisk with the image service. - -``nova-manage image convert `` - - Converts all images in directory from the old (Bexar) format to the new format. - -Nova VM -~~~~~~~~~~~ - -``nova-manage vm list [host]`` - Show a list of all instances. Accepts optional hostname (to show only instances on specific host). - -``nova-manage live-migration `` - Live migrate instance from current host to destination host. Requires instance id (which comes from euca-describe-instance) and destination host name (which can be found from nova-manage service list). - - -SEE ALSO -======== - -* `OpenStack Nova `__ - -BUGS -==== - -* Nova bugs are managed at Launchpad `Bugs : Nova `__ - - - diff --git a/doc/source/policies.rst b/doc/source/policies.rst deleted file mode 100644 index 9970588fa91..00000000000 --- a/doc/source/policies.rst +++ /dev/null @@ -1,98 +0,0 @@ -.. - 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. - -Development policies --------------------- - -Out Of Tree Support -=================== - -While nova has many entrypoints and other places in the code that allow for -wiring in out of tree code, upstream doesn't actively make any guarantees -about these extensibility points; we don't support them, make any guarantees -about compatibility, stability, etc. - -Public Contractual APIs -======================== - -Although nova has many internal APIs, they are not all public contractual -APIs. Below is a link of our public contractual APIs: - -* http://developer.openstack.org/api-ref-compute-v2.1.html - -Anything not in this list is considered private, not to be used outside of -nova, and should not be considered stable. - -REST APIs -========== - -Follow the guidelines set in: https://wiki.openstack.org/wiki/APIChangeGuidelines - -The canonical source for REST API behavior is the code *not* documentation. -Documentation is manually generated after the code by folks looking at the -code and writing up what they think it does, and it is very easy to get -this wrong. - -This policy is in place to prevent us from making backwards incompatible -changes to REST APIs. - -Patches and Reviews -=================== - -Merging a patch requires a non-trivial amount of reviewer resources. -As a patch author, you should try to offset the reviewer resources -spent on your patch by reviewing other patches. If no one does this, the review -team (cores and otherwise) become spread too thin. - -For review guidelines see: http://docs.openstack.org/infra/manual/developers.html#peer-review - -Reverts for Retrospective Vetos -=============================== - -Sometimes our simple "2 +2s" approval policy will result in errors. -These errors might be a bug that was missed, or equally importantly, -it might be that other cores feel that there is a need for more -discussion on the implementation of a given piece of code. - -Rather than `an enforced time-based solution`_ - for example, a patch -couldn't be merged until it has been up for review for 3 days - we have -chosen an honor-based system where core reviewers would not approve -potentially contentious patches until the proposal had been -sufficiently socialized and everyone had a chance to raise any -concerns. - -Recognising that mistakes can happen, we also have a policy where -contentious patches which were quickly approved should be reverted so -that the discussion around the proposal can continue as if the patch -had never been merged in the first place. In such a situation, the -procedure is: - -0. The commit to be reverted must not have been released. -1. The core team member who has a -2 worthy objection should propose a - revert, stating the specific concerns that they feel need - addressing. -2. Any subsequent patches depending on the to-be-reverted patch may - need to be reverted also. -3. Other core team members should quickly approve the revert. No detailed - debate should be needed at this point. A -2 vote on a revert is - strongly discouraged, because it effectively blocks the right of - cores approving the revert from -2 voting on the original patch. -4. The original patch submitter should re-submit the change, with a - reference to the original patch and the revert. -5. The original reviewers of the patch should restore their votes and - attempt to summarize their previous reasons for their votes. -6. The patch should not be re-approved until the concerns of the people - proposing the revert are worked through. A mailing list discussion or - design spec might be the best way to achieve this. - -.. _`an enforced time-based solution`: https://lists.launchpad.net/openstack/msg08574.html diff --git a/doc/source/policy_enforcement.rst b/doc/source/policy_enforcement.rst deleted file mode 100644 index 5f9c96f3742..00000000000 --- a/doc/source/policy_enforcement.rst +++ /dev/null @@ -1,178 +0,0 @@ -.. - Copyright 2014 Intel - 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. - - -Rest API Policy Enforcement -=========================== - -Here is a vision of how we want policy to be enforced in nova. - -Problems with current system ----------------------------- - -There are several problems for current API policy. - -* The permission checking is spread through the various levels of the nova - code, also there are some hard-coded permission checks that make some - policies not enforceable. - -* API policy rules need better granularity. Some of extensions just use one - rule for all the APIs. Deployer can't get better granularity control for - the APIs. - -* More easy way to override default policy settings for deployer. And - Currently all the API(EC2, V2, V2.1) rules mix in one policy.conf file. - -These are the kinds of things we need to make easier: - -1. Operator wants to enable a specific role to access the service API which -is not possible because there is currently a hard coded admin check. - -2. One policy rule per API action. Having a check in the REST API and a -redundant check in the compute API can confuse developers and deployers. - -3. Operator can specify different rules for APIs that in same extension. - -4. Operator can override the default policy rule easily without mixing his own -config and default config in one policy.conf file. - -Future of policy enforcement ----------------------------- - -The generic rule for all the improvement is keep V2 API back-compatible. -Because V2 API may be deprecated after V2.1 parity with V2. This can reduce -the risk we take. The improvement just for EC2 and V2.1 API. There isn't -any user for V2.1, as it isn't ready yet. We have to do change for EC2 API. -EC2 API won't be removed like v2 API. If we keep back-compatible for EC2 API -also, the old compute api layer checks won't be removed forever. EC2 API is -really small than Nova API. It's about 29 APIs without volume and image -related(those policy check done by cinder and glance). So it will affect user -less. - -Enforcement policy at REST API layer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The policy should be only enforced at REST API layer. This is clear for user -to know where the policy will be enforced. If the policy spread into multiple -layer of nova code, user won't know when and where the policy will be enforced -if they didn't have knowledge about nova code. - -Remove all the permission checking under REST API layer. Policy will only be -enforced at REST API layer. - -This will affect the EC2 API and V2.1 API, there are some API just have policy -enforcement at Compute/Network API layer, those policy will be move to API -layer and renamed. - -Removes hard-code permission checks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Hard-coded permission checks make it impossible to supply a configurable -policy. They should be removed in order to make nova auth completely -configurable. - -This will affect EC2 API and Nova V2.1 API. User need update their policy -rule to match the old hard-code permission. - -For Nova V2 API, the hard-code permission checks will be moved to REST API -layer to guarantee it won't break the back-compatibility. That may ugly -some hard-code permission check in API layer, but V2 API will be removed -once V2.1 API ready, so our choice will reduce the risk. - -Port policy.d from oslo-incubator into nova -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This feature make deployer can override default policy rule easily. And -When nova default policy config changed, deployer only need replace default -policy config files with new one. It won't affect his own policy config in -other files. - -Use different prefix in policy rule name for EC2/V2/V2.1 API -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Currently all the APIs(Nova v2/v2.1 API, EC2 API) use same set of policy -rules. Especially there isn't obvious mapping between those policy rules -and EC2 API. User can know clearly which policy should be configured for -specific API. - -Nova should provide different prefix for policy rule name that used to -group them, and put them in different policy configure file in policy.d - - * EC2 API: Use prefix "ec2_api". The rule looks like "ec2_api:[action]" - - * Nova V2 API: After we move to V2.1, we needn't spend time to change V2 - api rule, and needn't to bother deployer upgrade their policy config. So - just keep V2 API policy rule named as before. - - * Nova V2.1 API: We name the policy rule as - "os_compute_api:[extension]:[action]". The core API may be changed in - the future, so we needn't name them as "compute" or "compute_extension" - to distinguish the core or extension API. - -This will affect EC2 API and V2.1 API. For EC2 API, it need deployer update -their policy config. For V2.1 API, there isn't any user yet, so there won't -any effect. - - -Group the policy rules into different policy files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -After group the policy rules for different API, we can separate them into -different files. Then deployer will more clear for which rule he can set for -specific API. The rules can be grouped as below: - - * policy.conf: It only contains the generic rule, like: - -:: - "context_is_admin": "role:admin", - "admin_or_owner": "is_admin:True or project_id:%(project_id)s", - "default": "rule:admin_or_owner", - - * policy.d/00-ec2-api.conf: It contains all the policy rules for EC2 API. - - * policy.d/00-v2-api.conf: It contains all the policy rules for nova V2 API. - - * policy.d/00-v2.1-api.conf: It contains all the policy rules for nova v2.1 - API. - -The prefix '00-' is used to order the configure file. All the files in -policy.d will be loaded by alphabetical order. '00-' means those files will -be loaded very early. - -Add separated rule for each API in extension -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This is for provider better granularity for policy rules. Not just provide -policy rule for extension as unit. - -This need user to move the policy rule into separated rule for each API. - -Enable action level rule override extension level rule -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -After separated rule for each API in extension, that will increase the work -for deployer. So enable extension level rule as default for each API in that -extension will ease that a lot. Deployer also can specify one rule for each -API to override the extension level rule. - -Existed Nova API being restricted ---------------------------------- - -Nova provide default policy rules for all the APIs. Operator should only make -the policy rule more permissive. If the Operator make the API to be restricted -that make break the existed API user or application. That's kind of -back-incompatible. SO Operator can free to add additional permission to the -existed API. diff --git a/doc/source/reference/api-microversion-history.rst b/doc/source/reference/api-microversion-history.rst new file mode 100644 index 00000000000..45000e16d25 --- /dev/null +++ b/doc/source/reference/api-microversion-history.rst @@ -0,0 +1 @@ +.. include:: ../../../nova/api/openstack/compute/rest_api_version_history.rst diff --git a/doc/source/gmr.rst b/doc/source/reference/gmr.rst similarity index 96% rename from doc/source/gmr.rst rename to doc/source/reference/gmr.rst index 4fadd56cc9e..93250be4546 100644 --- a/doc/source/gmr.rst +++ b/doc/source/reference/gmr.rst @@ -47,7 +47,7 @@ Adding Support for GMRs to New Executables Adding support for a *GMR* to a given executable is fairly easy. -First import the module (currently residing in oslo-incubator), as well as the Nova version module: +First import the module, as well as the Nova version module: .. code-block:: python diff --git a/doc/source/reference/i18n.rst b/doc/source/reference/i18n.rst new file mode 100644 index 00000000000..d9435816ad5 --- /dev/null +++ b/doc/source/reference/i18n.rst @@ -0,0 +1,39 @@ +Internationalization +==================== + +Nova uses the `oslo.i18n library +`_ to support +internationalization. The oslo.i18n library is built on top of `gettext +`_ and provides functions that are +used to enable user-facing strings such as log messages to appear in the +appropriate language in different locales. + +Nova exposes the oslo.i18n library support via the ``nova/i18n.py`` integration +module. This module provides the functions needed to wrap translatable strings. +It provides the ``_()`` wrapper for general user-facing messages (such +as ones that end up in command line responses, or responses over the +network). + +One upon a time there was an effort to translate log messages in +OpenStack projects. But starting with the Ocata release these are no +longer being supported. Log messages **should not** be translated. Any +use of ``_LI()``, ``_LW()``, ``_LE()``, ``_LC()`` are vestigial and +will be removed over time. No new uses of these should be added. + +You should use the basic wrapper ``_()`` for strings which are not log +messages that are expected to get to an end user:: + + raise nova.SomeException(_('Invalid service catalogue')) + +Do not use ``locals()`` for formatting messages because: +1. It is not as clear as using explicit dicts. +2. It could produce hidden errors during refactoring. +3. Changing the name of a variable causes a change in the message. +4. It creates a lot of otherwise unused variables. + +If you do not follow the project conventions, your code may cause hacking +checks to fail. + +The ``_()`` function can be imported with :: + + from nova.i18n import _ diff --git a/doc/source/reference/index.rst b/doc/source/reference/index.rst new file mode 100644 index 00000000000..cee5c8f2b37 --- /dev/null +++ b/doc/source/reference/index.rst @@ -0,0 +1,46 @@ +================================ + Technical Reference Deep Dives +================================ + +The nova project is large, and there are lots of complicated parts in it where +it helps to have an overview to understand how the internals of a particular +part work. + +Internals +========= + +The following is a dive into some of the internals in nova. + +* :doc:`/reference/rpc`: How nova uses AMQP as an RPC transport +* :doc:`/reference/services`: Module descriptions for some of the key modules + used in starting / running services +* :doc:`/reference/vm-states`: Cheat sheet for understanding the life cycle of + compute instances +* :doc:`/reference/threading`: The concurrency model used in nova, which is + based on eventlet, and may not be familiar to everyone. +* :doc:`/reference/notifications`: How the notifications subsystem works in + nova, and considerations when adding notifications. + +Debugging +========= + +* :doc:`/reference/gmr`: Inspired by Amiga, a way to trigger a very + comprehensive dump of a running service for deep debugging. + +Forward Looking Plans +===================== + +The following section includes documents that describe the overall plan behind +groups of nova-specs. Most of these cover items relating to the evolution of +various parts of nova's architecture. Once the work is complete, +these documents will move into the "Internals" section. + +If you want to get involved in shaping the future of nova's architecture, +these are a great place to start reading up on the current plans. + +* :doc:`/user/cells`: Comparison of Cells v1 and v2, and how v2 is evolving +* :doc:`/reference/policy-enforcement`: How we want policy checks on API actions + to work in the future +* :doc:`/reference/stable-api`: What stable api means to nova +* :doc:`/reference/scheduler-evolution`: Motivation behind the scheduler / + placement evolution diff --git a/doc/source/notifications.rst b/doc/source/reference/notifications.rst similarity index 78% rename from doc/source/notifications.rst rename to doc/source/reference/notifications.rst index 5d362dfb31a..032fd9711fa 100644 --- a/doc/source/notifications.rst +++ b/doc/source/reference/notifications.rst @@ -40,8 +40,8 @@ Notifier object to emit notifications. The configuration of the returned Notifier object depends on the parameters of the get_notifier call and the value of the oslo.messaging configuration options `driver` and `topics`. There are notification configuration options in Nova which are specific for -certain notification types like `notify_on_state_change`, -`notify_api_faults`, `default_notification_level`, etc. +certain notification types like `notifications.notify_on_state_change`, +`notifications.notify_on_api_faults`, `notifications.default_level`, etc. The structure of the payload of the unversioned notifications is defined in the code that emits the notification and no documentation or enforced backward @@ -97,29 +97,47 @@ notification payload: * a major version bump indicates a backward incompatible change of the payload which can mean removed fields, type change, etc in the payload. -There is a Nova configuration parameter `notification_format` that can be used -to specify which notifications are emitted by Nova. The possible values are -`unversioned`, `versioned`, `both` and the default value is `both`. +There is a Nova configuration parameter `notifications.notification_format` +that can be used to specify which notifications are emitted by Nova. The +possible values are `unversioned`, `versioned`, `both` and the default value +is `both`. + +The versioned notifications are emitted to a different topic than the legacy +notifications. By default they are emitted to 'versioned_notifications' but it +is configurable in the nova.conf with the `versioned_notifications_topic` +config option. How to add a new versioned notification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To support the above contract from the Nova code every versioned notification is modeled with oslo versionedobjects. Every versioned notification class -shall inherit from the `nova.objects.notification.NotificationBase` which +shall inherit from the `nova.notifications.objects.base.NotificationBase` which already defines three mandatory fields of the notification `event_type`, `publisher_id` and `priority`. The new notification class shall add a new field `payload` with an appropriate payload type. The payload object of the notifications shall inherit from the -`nova.objects.notification.NotificationPayloadBase` class and shall define the -fields of the payload as versionedobject fields. The base classes are described -in [3]_. +`nova.objects.notifications.base.NotificationPayloadBase` class and shall +define the fields of the payload as versionedobject fields. The base classes +are described in the following section. + +The nova.notifications.objects.base module +.......................................... +.. automodule:: nova.notifications.objects.base + :noindex: + :members: + :show-inheritance: + +Please note that the notification objects shall not be registered to the +NovaObjectRegistry to avoid mixing nova internal objects with the notification +objects. Instead of that use the register_notification decorator on every +concrete notification object. The following code example defines the necessary model classes for a new notification `myobject.update`:: @notification.notification_sample('myobject-update.json') - @base.NovaObjectRegistry.register + @object_base.NovaObjectRegistry.register.register_notification class MyObjectNotification(notification.NotificationBase): # Version 1.0: Initial version VERSION = '1.0' @@ -129,7 +147,7 @@ notification `myobject.update`:: } - @base.NovaObjectRegistry.register + @object_base.NovaObjectRegistry.register.register_notification class MyObjectUpdatePayload(notification.NotificationPayloadBase): # Version 1.0: Initial version VERSION = '1.0' @@ -177,7 +195,7 @@ object. For example the service.status notification reuses the existing `nova.objects.service.Service` object when defines the notification's payload:: @notification.notification_sample('service-update.json') - @base.NovaObjectRegistry.register + @object_base.NovaObjectRegistry.register.register_notification class ServiceStatusNotification(notification.NotificationBase): # Version 1.0: Initial version VERSION = '1.0' @@ -186,7 +204,7 @@ object. For example the service.status notification reuses the existing 'payload': fields.ObjectField('ServiceStatusPayload') } - @base.NovaObjectRegistry.register + @object_base.NovaObjectRegistry.register.register_notification class ServiceStatusPayload(notification.NotificationPayloadBase): SCHEMA = { 'host': ('service', 'host'), @@ -264,6 +282,32 @@ decorated with the `notification_sample` decorator. For example the `doc/sample_notifications/service-update.json` and the ServiceUpdateNotification class is decorated accordingly. +What should be in the notification payload +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This is just a guideline. You should always consider the actual use case that +requires the notification. + +* Always include the identifier (e.g. uuid) of the entity that can be used to + query the whole entity over the REST API so that the consumer can get more + information about the entity. +* You should consider including those fields that are related to the event + you are sending the notification about. For example if a change of a field of + the entity triggers an update notification then you should include the field + to the payload. +* An update notification should contain information about what part of the + entity is changed. Either by filling the nova_object.changes part of the + payload (note that it is not supported by the notification framework + currently) or sending both the old state and the new state of the entity in + the payload. +* You should never include a nova internal object in the payload. Create a new + object and use the SCHEMA field to map the internal object to the + notification payload. This way the evolution of the internal object model + can be decoupled from the evolution of the notification payload. +* The delete notification should contain the same information as the create or + update notifications. This makes it possible for the consumer to listen only to + the delete notifications but still filter on some fields of the entity + (e.g. project_id). + Existing versioned notifications ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -273,4 +317,3 @@ Existing versioned notifications .. [1] http://docs.openstack.org/developer/oslo.messaging/notifier.html .. [2] http://docs.openstack.org/developer/oslo.versionedobjects -.. [3] http://docs.openstack.org/developer/nova/devref/api/nova.objects.notification.html diff --git a/doc/source/reference/policy-enforcement.rst b/doc/source/reference/policy-enforcement.rst new file mode 100644 index 00000000000..5a008822274 --- /dev/null +++ b/doc/source/reference/policy-enforcement.rst @@ -0,0 +1,170 @@ +.. + Copyright 2014 Intel + 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. + + +Rest API Policy Enforcement +=========================== + +Here is a vision of how we want policy to be enforced in nova. + +Problems with current system +---------------------------- + +There are several problems for current API policy. + +* The permission checking is spread through the various levels of the nova + code, also there are some hard-coded permission checks that make some + policies not enforceable. + +* API policy rules need better granularity. Some of extensions just use one + rule for all the APIs. Deployer can't get better granularity control for + the APIs. + +* More easy way to override default policy settings for deployer. And + Currently all the API(EC2, V2, V2.1) rules mix in one policy.json file. + +These are the kinds of things we need to make easier: + +1. Operator wants to enable a specific role to access the service API which +is not possible because there is currently a hard coded admin check. + +2. One policy rule per API action. Having a check in the REST API and a +redundant check in the compute API can confuse developers and deployers. + +3. Operator can specify different rules for APIs that in same extension. + +4. Operator can override the default policy rule easily without mixing his own +config and default config in one policy.json file. + +Future of policy enforcement +---------------------------- + +The generic rule for all the improvement is keep V2 API back-compatible. +Because V2 API may be deprecated after V2.1 parity with V2. This can reduce +the risk we take. The improvement just for EC2 and V2.1 API. There isn't +any user for V2.1, as it isn't ready yet. We have to do change for EC2 API. +EC2 API won't be removed like v2 API. If we keep back-compatible for EC2 API +also, the old compute api layer checks won't be removed forever. EC2 API is +really small than Nova API. It's about 29 APIs without volume and image +related(those policy check done by cinder and glance). So it will affect user +less. + +Enforcement policy at REST API layer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The policy should be only enforced at REST API layer. This is clear for user +to know where the policy will be enforced. If the policy spread into multiple +layer of nova code, user won't know when and where the policy will be enforced +if they didn't have knowledge about nova code. + +Remove all the permission checking under REST API layer. Policy will only be +enforced at REST API layer. + +This will affect the EC2 API and V2.1 API, there are some API just have policy +enforcement at Compute/Network API layer, those policy will be move to API +layer and renamed. + +Removes hard-code permission checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Hard-coded permission checks make it impossible to supply a configurable +policy. They should be removed in order to make nova auth completely +configurable. + +This will affect EC2 API and Nova V2.1 API. User need update their policy +rule to match the old hard-code permission. + +For Nova V2 API, the hard-code permission checks will be moved to REST API +layer to guarantee it won't break the back-compatibility. That may ugly +some hard-code permission check in API layer, but V2 API will be removed +once V2.1 API ready, so our choice will reduce the risk. + +Port policy.d into nova +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This feature make deployer can override default policy rule easily. And +When nova default policy config changed, deployer only need replace default +policy config files with new one. It won't affect his own policy config in +other files. + +Use different prefix in policy rule name for EC2/V2/V2.1 API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently all the APIs(Nova v2/v2.1 API, EC2 API) use same set of policy +rules. Especially there isn't obvious mapping between those policy rules +and EC2 API. User can know clearly which policy should be configured for +specific API. + +Nova should provide different prefix for policy rule name that used to +group them, and put them in different policy configure file in policy.d + +* EC2 API: Use prefix "ec2_api". The rule looks like "ec2_api:[action]" + +* Nova V2 API: After we move to V2.1, we needn't spend time to change V2 + api rule, and needn't to bother deployer upgrade their policy config. So + just keep V2 API policy rule named as before. + +* Nova V2.1 API: We name the policy rule as + "os_compute_api:[extension]:[action]". The core API may be changed in + the future, so we needn't name them as "compute" or "compute_extension" + to distinguish the core or extension API. + +This will affect EC2 API and V2.1 API. For EC2 API, it need deployer update +their policy config. For V2.1 API, there isn't any user yet, so there won't +any effect. + + +Group the policy rules into different policy files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After group the policy rules for different API, we can separate them into +different files. Then deployer will more clear for which rule he can set for +specific API. The rules can be grouped as below: + +* policy.json: It only contains the generic rule, like: :: + + "context_is_admin": "role:admin", + "admin_or_owner": "is_admin:True or project_id:%(project_id)s", + "default": "rule:admin_or_owner", + +* policy.d/00-ec2-api.conf: It contains all the policy rules for EC2 API. + +* policy.d/00-v2-api.conf: It contains all the policy rules for nova V2 API. + +* policy.d/00-v2.1-api.conf: It contains all the policy rules for nova v2.1 + API. + +The prefix '00-' is used to order the configure file. All the files in +policy.d will be loaded by alphabetical order. '00-' means those files will +be loaded very early. + +Existed Nova API being restricted +--------------------------------- + +Nova provide default policy rules for all the APIs. Operator should only make +the policy rule more permissive. If the Operator make the API to be restricted +that make break the existed API user or application. That's kind of +back-incompatible. SO Operator can free to add additional permission to the +existed API. + +Policy Enforcement by user_id +----------------------------- + +In the legacy v2 API, the policy enforces with target object, and some operators +implement user-based authorization based on that. Actually only project-based +authorization is well tested, the user based authorization is untested and +isn't supported by Nova. In the future, the nova will remove all the supports +for user-based authorization. diff --git a/doc/source/reference/rpc.rst b/doc/source/reference/rpc.rst new file mode 100644 index 00000000000..adf51167db4 --- /dev/null +++ b/doc/source/reference/rpc.rst @@ -0,0 +1,328 @@ +.. + Copyright (c) 2010 Citrix Systems, 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. + +AMQP and Nova +============= + +AMQP is the messaging technology chosen by the OpenStack cloud. The AMQP +broker, either RabbitMQ or Qpid, sits between any two Nova components and +allows them to communicate in a loosely coupled fashion. More precisely, Nova +components (the compute fabric of OpenStack) use Remote Procedure Calls (RPC +hereinafter) to communicate to one another; however such a paradigm is built +atop the publish/subscribe paradigm so that the following benefits can be +achieved: + +* Decoupling between client and servant (such as the client does not need to + know where the servant's reference is). + +* Full a-synchronism between client and servant (such as the client does not + need the servant to run at the same time of the remote call). + +* Random balancing of remote calls (such as if more servants are up and + running, one-way calls are transparently dispatched to the first available + servant). + +Nova uses direct, fanout, and topic-based exchanges. The architecture looks +like the one depicted in the figure below: + +.. image:: /_static/images/rpc-arch.png + :width: 60% + +Nova implements RPC (both request+response, and one-way, respectively nicknamed +``rpc.call`` and ``rpc.cast``) over AMQP by providing an adapter class which +take cares of marshaling and unmarshaling of messages into function calls. Each +Nova service (for example Compute, Scheduler, etc.) create two queues at the +initialization time, one which accepts messages with routing keys +``NODE-TYPE.NODE-ID`` (for example ``compute.hostname``) and another, which +accepts messages with routing keys as generic ``NODE-TYPE`` (for example +``compute``). The former is used specifically when Nova-API needs to redirect +commands to a specific node like ``euca-terminate instance``. In this case, +only the compute node whose host's hypervisor is running the virtual machine +can kill the instance. The API acts as a consumer when RPC calls are +request/response, otherwise it acts as a publisher only. + +Nova RPC Mappings +----------------- + +The figure below shows the internals of a message broker node (referred to as a +RabbitMQ node in the diagrams) when a single instance is deployed and shared in +an OpenStack cloud. Every Nova component connects to the message broker and, +depending on its personality (for example a compute node or a network node), +may use the queue either as an Invoker (such as API or Scheduler) or a Worker +(such as Compute or Network). Invokers and Workers do not actually exist in the +Nova object model, but we are going to use them as an abstraction for sake of +clarity. An Invoker is a component that sends messages in the queuing system +via two operations: 1) ``rpc.call`` and ii) ``rpc.cast``; a Worker is a +component that receives messages from the queuing system and reply accordingly +to ``rpc.call`` operations. + +Figure 2 shows the following internal elements: + +Topic Publisher + A Topic Publisher comes to life when an ``rpc.call`` or an ``rpc.cast`` + operation is executed; this object is instantiated and used to push a message + to the queuing system. Every publisher connects always to the same + topic-based exchange; its life-cycle is limited to the message delivery. + +Direct Consumer + A Direct Consumer comes to life if (and only if) an ``rpc.call`` operation is + executed; this object is instantiated and used to receive a response message + from the queuing system. Every consumer connects to a unique direct-based + exchange via a unique exclusive queue; its life-cycle is limited to the + message delivery; the exchange and queue identifiers are determined by a UUID + generator, and are marshaled in the message sent by the Topic Publisher (only + ``rpc.call`` operations). + +Topic Consumer + A Topic Consumer comes to life as soon as a Worker is instantiated and exists + throughout its life-cycle; this object is used to receive messages from the + queue and it invokes the appropriate action as defined by the Worker role. A + Topic Consumer connects to the same topic-based exchange either via a shared + queue or via a unique exclusive queue. Every Worker has two topic consumers, + one that is addressed only during ``rpc.cast`` operations (and it connects to + a shared queue whose exchange key is ``topic``) and the other that is + addressed only during ``rpc.call`` operations (and it connects to a unique + queue whose exchange key is ``topic.host``). + +Direct Publisher + A Direct Publisher comes to life only during ``rpc.call`` operations and it + is instantiated to return the message required by the request/response + operation. The object connects to a direct-based exchange whose identity is + dictated by the incoming message. + +Topic Exchange + The Exchange is a routing table that exists in the context of a virtual host + (the multi-tenancy mechanism provided by Qpid or RabbitMQ); its type (such as + topic vs. direct) determines the routing policy; a message broker node will + have only one topic-based exchange for every topic in Nova. + +Direct Exchange + This is a routing table that is created during ``rpc.call`` operations; there + are many instances of this kind of exchange throughout the life-cycle of a + message broker node, one for each ``rpc.call`` invoked. + +Queue Element + A Queue is a message bucket. Messages are kept in the queue until a Consumer + (either Topic or Direct Consumer) connects to the queue and fetch it. Queues + can be shared or can be exclusive. Queues whose routing key is ``topic`` are + shared amongst Workers of the same personality. + +.. image:: /_static/images/rpc-rabt.png + :width: 60% + +RPC Calls +--------- + +The diagram below shows the message flow during an ``rpc.call`` operation: + +1. A Topic Publisher is instantiated to send the message request to the queuing + system; immediately before the publishing operation, a Direct Consumer is + instantiated to wait for the response message. + +2. Once the message is dispatched by the exchange, it is fetched by the Topic + Consumer dictated by the routing key (such as 'topic.host') and passed to + the Worker in charge of the task. + +3. Once the task is completed, a Direct Publisher is allocated to send the + response message to the queuing system. + +4. Once the message is dispatched by the exchange, it is fetched by the Direct + Consumer dictated by the routing key (such as ``msg_id``) and passed to the + Invoker. + +.. image:: /_static/images/rpc-flow-1.png + :width: 60% + +RPC Casts +--------- + +The diagram below shows the message flow during an ``rpc.cast`` operation: + +1. A Topic Publisher is instantiated to send the message request to the queuing + system. + +2. Once the message is dispatched by the exchange, it is fetched by the Topic + Consumer dictated by the routing key (such as 'topic') and passed to the + Worker in charge of the task. + +.. image:: /_static/images/rpc-flow-2.png + :width: 60% + +AMQP Broker Load +---------------- + +At any given time the load of a message broker node running either Qpid or +RabbitMQ is function of the following parameters: + +Throughput of API calls + The number of API calls (more precisely ``rpc.call`` ops) being served by the + OpenStack cloud dictates the number of direct-based exchanges, related queues + and direct consumers connected to them. + +Number of Workers + There is one queue shared amongst workers with the same personality; however + there are as many exclusive queues as the number of workers; the number of + workers dictates also the number of routing keys within the topic-based + exchange, which is shared amongst all workers. + +The figure below shows the status of a RabbitMQ node after Nova components' +bootstrap in a test environment. Exchanges and queues being created by Nova +components are: + +* Exchanges + + 1. nova (topic exchange) + +* Queues + + 1. ``compute.phantom`` (``phantom`` is hostname) + 2. ``compute`` + 3. ``network.phantom`` (``phantom`` is hostname) + 4. ``network`` + 5. ``scheduler.phantom`` (``phantom`` is hostname) + 6. ``scheduler`` + +.. image:: /_static/images/rpc-state.png + :width: 60% + +RabbitMQ Gotchas +---------------- + +Nova uses Kombu to connect to the RabbitMQ environment. Kombu is a Python +library that in turn uses AMQPLib, a library that implements the standard AMQP +0.8 at the time of writing. When using Kombu, Invokers and Workers need the +following parameters in order to instantiate a Connection object that connects +to the RabbitMQ server (please note that most of the following material can be +also found in the Kombu documentation; it has been summarized and revised here +for sake of clarity): + +``hostname`` + The hostname to the AMQP server. + +``userid`` + A valid username used to authenticate to the server. + +``password`` + The password used to authenticate to the server. + +``virtual_host`` + The name of the virtual host to work with. This virtual host must exist on + the server, and the user must have access to it. Default is "/". + +``port`` + The port of the AMQP server. Default is ``5672`` (amqp). + +The following parameters are default: + +``insist`` + Insist on connecting to a server. In a configuration with multiple + load-sharing servers, the Insist option tells the server that the client is + insisting on a connection to the specified server. Default is False. + +``connect_timeout`` + The timeout in seconds before the client gives up connecting to the server. + The default is no timeout. + +``ssl`` + Use SSL to connect to the server. The default is False. + +More precisely Consumers need the following parameters: + +``connection`` + The above mentioned Connection object. + +``queue`` + Name of the queue. + +``exchange`` + Name of the exchange the queue binds to. + +``routing_key`` + The interpretation of the routing key depends on the value of the + ``exchange_type`` attribute. + + Direct exchange + If the routing key property of the message and the ``routing_key`` attribute of + the queue are identical, then the message is forwarded to the queue. + + Fanout exchange + Messages are forwarded to the queues bound the exchange, even if the + binding does not have a key. + + Topic exchange + If the routing key property of the message matches the routing key of the + key according to a primitive pattern matching scheme, then the message is + forwarded to the queue. The message routing key then consists of words + separated by dots (``.``, like domain names), and two special characters + are available; star (``*``) and hash (``#``). The star matches any word, + and the hash matches zero or more words. For example ``.stock.#`` matches + the routing keys ``usd.stock`` and ``eur.stock.db`` but not + ``stock.nasdaq``. + +``durable`` + This flag determines the durability of both exchanges and queues; durable + exchanges and queues remain active when a RabbitMQ server restarts. + Non-durable exchanges/queues (transient exchanges/queues) are purged when a + server restarts. It is worth noting that AMQP specifies that durable queues + cannot bind to transient exchanges. Default is True. + +``auto_delete`` + If set, the exchange is deleted when all queues have finished using it. + Default is False. + +``exclusive`` + Exclusive queues (such as non-shared) may only be consumed from by the + current connection. When exclusive is on, this also implies ``auto_delete``. + Default is False. + +``exchange_type`` + AMQP defines several default exchange types (routing algorithms) that covers + most of the common messaging use cases. + +``auto_ack`` + Acknowledgment is handled automatically once messages are received. By + default ``auto_ack`` is set to False, and the receiver is required to manually + handle acknowledgment. + +``no_ack`` + It disable acknowledgment on the server-side. This is different from + ``auto_ack`` in that acknowledgment is turned off altogether. This + functionality increases performance but at the cost of reliability. Messages + can get lost if a client dies before it can deliver them to the application. + +``auto_declare`` + If this is True and the exchange name is set, the exchange will be + automatically declared at instantiation. Auto declare is on by default. + +Publishers specify most the parameters of Consumers (such as they do not +specify a queue name), but they can also specify the following: + +``delivery_mode`` + The default delivery mode used for messages. The value is an integer. The + following delivery modes are supported by RabbitMQ: + + ``1`` (transient) + The message is transient. Which means it is stored in memory only, and is + lost if the server dies or restarts. + + ``2`` (persistent) + The message is persistent. Which means the message is stored both + in-memory, and on disk, and therefore preserved if the server dies or + restarts. + +The default value is ``2`` (persistent). During a send operation, Publishers +can override the delivery mode of messages so that, for example, transient +messages can be sent over a durable queue. diff --git a/doc/source/scheduler_evolution.rst b/doc/source/reference/scheduler-evolution.rst similarity index 100% rename from doc/source/scheduler_evolution.rst rename to doc/source/reference/scheduler-evolution.rst diff --git a/doc/source/services.rst b/doc/source/reference/services.rst similarity index 100% rename from doc/source/services.rst rename to doc/source/reference/services.rst diff --git a/doc/source/reference/stable-api.rst b/doc/source/reference/stable-api.rst new file mode 100644 index 00000000000..4afcd319559 --- /dev/null +++ b/doc/source/reference/stable-api.rst @@ -0,0 +1,135 @@ +.. + Copyright 2015 Intel + 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. + + +Nova Stable REST API +==================== + +This document describes both the current state of the Nova REST API -- as +of the Pike release -- and also attempts to describe how the Nova team +evolved the REST API's implementation over time and remove some of the +cruft that has crept in over the years. + +Background +---------- + +Nova used to include two distinct frameworks for exposing REST API +functionality. Older code is called the "v2 API" and existed in the +/nova/api/openstack/compute/legacy_v2/ directory. This code tree was totally +removed during Netwon release time frame (14.0.0 and later). +Newer code is called the "v2.1 API" and exists in the +/nova/api/openstack/compute directory. + +The v2 API is the old Nova REST API. It is mostly replaced by v2.1 API. + +The v2.1 API is the new Nova REST API with a set of improvements which +includes Microversion and standardized validation of inputs using JSON-Schema. +Also the v2.1 API is totally backwards compatible with the v2 API (That is the +reason we call it as v2.1 API). + +Current Stable API +------------------ + +* Nova v2.1 API + Microversion (v2.1 APIs are backward-compatible with + v2 API, but more strict validation) +* /v2 & /v2.1 endpoint supported +* v2 compatible mode for old v2 users + +Evolution of Nova REST API +-------------------------- + +.. image:: /_static/images/evolution-of-api.png + +Nova v2 API + Extensions +************************ + +Nova use to have v2 API. In v2 API, there was a concept called 'extension'. +An operator can use it to enable/disable part of Nova REST API based on requirements. +An end user may query the '/extensions' API to discover what *API functionality* is +supported by the Nova deployment. + +Unfortunately, because v2 API extensions could be enabled or disabled +from one deployment to another -- as well as custom API extensions added +to one deployment and not another -- it was impossible for an end user to +know what the OpenStack Compute API actually included. No two OpenStack +deployments were consistent, which made cloud interoperability impossible. + +In Newton release, extensions magic was deprecated and marked for removal. + +In Newton release, v2 API code base has been removed and /v2 endpoints were +directed to v2.1 code base. + + +v2 API compatibility mode based on v2.1 API +******************************************* +v2.1 API is exactly same as v2 API except strong input validation with no additional +request parameter allowed and Microversion feature. +Since Newton, '/v2' endpoint also started using v2.1 API implementation. But to keep the +backward compatibility of v2 API, '/v2' endpoint should not return error on additional +request parameter or any new headers for Microversion. v2 API must be same as it has +been since starting. + +To achieve that behavior legacy v2 compatibility mode has been introduced. v2 compatibility +mode is based on v2.1 implementation with below difference: + +* Skip additionalProperties checks in request body +* Ignore Microversion headers in request +* No Microversion headers in response + +Nova v2.1 API + Microversion +**************************** + +In Kilo release, nova v2.1 API has been released. v2.1 API +is supposed to be backward compatible with v2 API with strong +input validation using JSON Schema. + +v2.1 API comes up with microversion concept which is a way to version +the API changes. Each new feature or modification in API has to done +via microversion bump. + +API extensions concept was deprecated from the v2.1 API, are no longer +needed to evolve the REST API, and no new API functionality should use +the API extension classes to implement new functionality. Instead, new +API functionality should be added via microversion concept and use the +microversioning decorators to add or change the REST API. + +v2.1 API had plugin framework which was using stevedore to load Nova REST +API extensions instead of old V2 handcrafted extension load mechanism. +There was an argument that the plugin framework supported extensibility in +the Nova API to allow deployers to publish custom API resources. + +In Newton release, config options of blacklist and whitelist extensions and +stevedore things were deprecated and marked for removal. + +In Pike, stevedore based plugin framekwork has been removed and url mapping +is done with plain router list. No more dynamic magic of detecting API +implementation for url. + +The '/extensions' API exposed the list of enabled API functions to users +by GET method. However as the above, new API extensions should not be added +to the list of this API. The '/extensions' API is frozen in Nova V2.1 API and +is deprecated. + +Things which are History now +**************************** + +Till Pike release, many deprecated things have been removed and became +history in Nova API world: + +* v2 legacy framework. +* API extensions concept. +* Stevedore magic to load the extension/plugin dynamically. +* Configurable way to enable/disable APIs extensions. diff --git a/doc/source/threading.rst b/doc/source/reference/threading.rst similarity index 82% rename from doc/source/threading.rst rename to doc/source/reference/threading.rst index 8fbc973172e..3836ff610c9 100644 --- a/doc/source/threading.rst +++ b/doc/source/reference/threading.rst @@ -38,10 +38,11 @@ contributors use ``greenthread.sleep()`` instead of ``time.sleep()``. MySQL access and eventlet ------------------------- -Queries to the MySQL database will block the main thread of a service. This is -because OpenStack services use an external C library for accessing the MySQL -database. Since eventlet cannot use monkey-patching to intercept blocking -calls in a C library, the resulting database query blocks the thread. +There are some MySQL DB API drivers for oslo.db, like `PyMySQL`_, MySQL-python +etc. PyMySQL is the default MySQL DB API driver for oslo.db, and it works well with +eventlet. MySQL-python uses an external C library for accessing the MySQL database. +Since eventlet cannot use monkey-patching to intercept blocking calls in a C library, +so queries to the MySQL database will block the main thread of a service. The Diablo release contained a thread-pooling implementation that did not block, but this implementation resulted in a `bug`_ and was removed. @@ -52,3 +53,4 @@ a discussion of the `impact on performance`_. .. _bug: https://bugs.launchpad.net/nova/+bug/838581 .. _mailing list thread: https://lists.launchpad.net/openstack/msg08118.html .. _impact on performance: https://lists.launchpad.net/openstack/msg08217.html +.. _PyMySQL: https://wiki.openstack.org/wiki/PyMySQL_evaluation diff --git a/doc/source/reference/vm-states.rst b/doc/source/reference/vm-states.rst new file mode 100644 index 00000000000..f9d6ae554ef --- /dev/null +++ b/doc/source/reference/vm-states.rst @@ -0,0 +1,141 @@ +Virtual Machine States and Transitions +======================================= + +The following diagrams and tables show the required virtual machine (VM) +states and task states for various commands issued by the user. + +Allowed State Transitions +-------------------------- + +.. graphviz:: + + digraph states { + graph [pad=".35", ranksep="0.65", nodesep="0.55", concentrate=true]; + node [fontsize=10 fontname="Monospace"]; + edge [arrowhead="normal", arrowsize="0.8"]; + label="All states are allowed to transition to DELETED and ERROR."; + forcelabels=true; + labelloc=bottom; + labeljust=left; + + /* states */ + building [label="BUILDING"] + active [label="ACTIVE"] + paused [label="PAUSED"] + suspended [label="SUSPENDED"] + stopped [label="STOPPED"] + rescued [label="RESCUED"] + resized [label="RESIZED"] + soft_deleted [label="SOFT_DELETED"] + shelved [label="SHELVED"] + shelved_offloaded [label="SHELVED_OFFLOADED"] + deleted [label="DELETED", color="red"] + error [label="ERROR", color="red"] + + /* transitions [action] */ + building -> active + + active -> active [headport=nw, tailport=ne] // manual layout + active -> soft_deleted [tailport=e] // prevent arrowhead overlap + active -> suspended + active -> paused [tailport=w] // prevent arrowhead overlap + active -> stopped + active -> shelved + active -> shelved_offloaded + active -> rescued + active -> resized + + soft_deleted -> active [headport=e] // prevent arrowhead overlap + + suspended -> active + suspended -> shelved + suspended -> shelved_offloaded + + paused -> active + paused -> shelved + paused -> shelved_offloaded + + stopped -> active + stopped -> stopped [headport=nw, tailport=ne] // manual layout + stopped -> resized + stopped -> rescued + stopped -> shelved + stopped -> shelved_offloaded + + resized -> active + + rescued -> active + + shelved -> shelved_offloaded + shelved -> active + + shelved_offloaded -> active + } + + +Requirements for Commands +------------------------- + +================== ================== ==================== ================ +Command Req'd VM States Req'd Task States Target State +================== ================== ==================== ================ +pause Active, Shutoff, Resize Verify, unset Paused + Rescued +unpause Paused N/A Active +suspend Active, Shutoff N/A Suspended +resume Suspended N/A Active +rescue Active, Shutoff Resize Verify, unset Rescued +unrescue Rescued N/A Active +set admin password Active N/A Active +rebuild Active, Shutoff Resize Verify, unset Active +force delete Soft Deleted N/A Deleted +restore Soft Deleted N/A Active +soft delete Active, Shutoff, N/A Soft Deleted + Error +delete Active, Shutoff, N/A Deleted + Building, Rescued, + Error +backup Active, Shutoff N/A Active, Shutoff +snapshot Active, Shutoff N/A Active, Shutoff +start Shutoff, Stopped N/A Active +stop Active, Shutoff, Resize Verify, unset Stopped + Rescued +reboot Active, Shutoff, Resize Verify, unset Active + Rescued +resize Active, Shutoff Resize Verify, unset Resized +revert resize Active, Shutoff Resize Verify, unset Active +confirm resize Active, Shutoff Resize Verify, unset Active +================== ================== ==================== ================ + +VM states and Possible Commands +------------------------------- + +============ ================================================================= +VM State Commands +============ ================================================================= +Paused unpause +Suspended resume +Active set admin password, suspend, pause, rescue, rebuild, soft delete, + delete, backup, snapshot, stop, reboot, resize, revert resize, + confirm resize +Shutoff suspend, pause, rescue, rebuild, soft delete, delete, backup, + start, snapshot, stop, reboot, resize, revert resize, + confirm resize +Rescued unrescue, pause +Stopped rescue, delete, start +Soft Deleted force delete, restore +Error soft delete, delete +Building delete +Rescued delete, stop, reboot +============ ================================================================= + + +Create Instance States +---------------------- + +The following diagram shows the sequence of VM states, task states, and +power states when a new VM instance is created. + +.. image:: /_static/images/create-vm-states.svg + :alt: Sequence of VM states, task states, and power states when a new + VM instance is created. diff --git a/doc/source/rpc.rst b/doc/source/rpc.rst deleted file mode 100644 index 7027f81a10f..00000000000 --- a/doc/source/rpc.rst +++ /dev/null @@ -1,149 +0,0 @@ -. - Copyright (c) 2010 Citrix Systems, 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. - -AMQP and Nova -============= - -AMQP is the messaging technology chosen by the OpenStack cloud. The AMQP broker, either RabbitMQ or Qpid, sits between any two Nova components and allows them to communicate in a loosely coupled fashion. More precisely, Nova components (the compute fabric of OpenStack) use Remote Procedure Calls (RPC hereinafter) to communicate to one another; however such a paradigm is built atop the publish/subscribe paradigm so that the following benefits can be achieved: - - * Decoupling between client and servant (such as the client does not need to know where the servant's reference is). - * Full a-synchronism between client and servant (such as the client does not need the servant to run at the same time of the remote call). - * Random balancing of remote calls (such as if more servants are up and running, one-way calls are transparently dispatched to the first available servant). - -Nova uses direct, fanout, and topic-based exchanges. The architecture looks like the one depicted in the figure below: - -.. image:: ./images/rpc/arch.png - :width: 60% - -.. - -Nova implements RPC (both request+response, and one-way, respectively nicknamed 'rpc.call' and 'rpc.cast') over AMQP by providing an adapter class which take cares of marshaling and unmarshaling of messages into function calls. Each Nova service (for example Compute, Scheduler, etc.) create two queues at the initialization time, one which accepts messages with routing keys 'NODE-TYPE.NODE-ID' (for example compute.hostname) and another, which accepts messages with routing keys as generic 'NODE-TYPE' (for example compute). The former is used specifically when Nova-API needs to redirect commands to a specific node like 'euca-terminate instance'. In this case, only the compute node whose host's hypervisor is running the virtual machine can kill the instance. The API acts as a consumer when RPC calls are request/response, otherwise it acts as a publisher only. - -Nova RPC Mappings ------------------ - -The figure below shows the internals of a message broker node (referred to as a RabbitMQ node in the diagrams) when a single instance is deployed and shared in an OpenStack cloud. Every Nova component connects to the message broker and, depending on its personality (for example a compute node or a network node), may use the queue either as an Invoker (such as API or Scheduler) or a Worker (such as Compute or Network). Invokers and Workers do not actually exist in the Nova object model, but we are going to use them as an abstraction for sake of clarity. An Invoker is a component that sends messages in the queuing system via two operations: 1) rpc.call and ii) rpc.cast; a Worker is a component that receives messages from the queuing system and reply accordingly to rpc.call operations. - -Figure 2 shows the following internal elements: - - * Topic Publisher: a Topic Publisher comes to life when an rpc.call or an rpc.cast operation is executed; this object is instantiated and used to push a message to the queuing system. Every publisher connects always to the same topic-based exchange; its life-cycle is limited to the message delivery. - * Direct Consumer: a Direct Consumer comes to life if (an only if) a rpc.call operation is executed; this object is instantiated and used to receive a response message from the queuing system; Every consumer connects to a unique direct-based exchange via a unique exclusive queue; its life-cycle is limited to the message delivery; the exchange and queue identifiers are determined by a UUID generator, and are marshaled in the message sent by the Topic Publisher (only rpc.call operations). - * Topic Consumer: a Topic Consumer comes to life as soon as a Worker is instantiated and exists throughout its life-cycle; this object is used to receive messages from the queue and it invokes the appropriate action as defined by the Worker role. A Topic Consumer connects to the same topic-based exchange either via a shared queue or via a unique exclusive queue. Every Worker has two topic consumers, one that is addressed only during rpc.cast operations (and it connects to a shared queue whose exchange key is 'topic') and the other that is addressed only during rpc.call operations (and it connects to a unique queue whose exchange key is 'topic.host'). - * Direct Publisher: a Direct Publisher comes to life only during rpc.call operations and it is instantiated to return the message required by the request/response operation. The object connects to a direct-based exchange whose identity is dictated by the incoming message. - * Topic Exchange: The Exchange is a routing table that exists in the context of a virtual host (the multi-tenancy mechanism provided by Qpid or RabbitMQ); its type (such as topic vs. direct) determines the routing policy; a message broker node will have only one topic-based exchange for every topic in Nova. - * Direct Exchange: this is a routing table that is created during rpc.call operations; there are many instances of this kind of exchange throughout the life-cycle of a message broker node, one for each rpc.call invoked. - * Queue Element: A Queue is a message bucket. Messages are kept in the queue until a Consumer (either Topic or Direct Consumer) connects to the queue and fetch it. Queues can be shared or can be exclusive. Queues whose routing key is 'topic' are shared amongst Workers of the same personality. - -.. image:: ./images/rpc/rabt.png - :width: 60% - -.. - -RPC Calls ---------- - -The diagram below shows the message flow during an rpc.call operation: - - 1. a Topic Publisher is instantiated to send the message request to the queuing system; immediately before the publishing operation, a Direct Consumer is instantiated to wait for the response message. - 2. once the message is dispatched by the exchange, it is fetched by the Topic Consumer dictated by the routing key (such as 'topic.host') and passed to the Worker in charge of the task. - 3. once the task is completed, a Direct Publisher is allocated to send the response message to the queuing system. - 4. once the message is dispatched by the exchange, it is fetched by the Direct Consumer dictated by the routing key (such as 'msg_id') and passed to the Invoker. - -.. image:: ./images/rpc/flow1.png - :width: 60% - -.. - -RPC Casts ---------- - -The diagram below shows the message flow during an rpc.cast operation: - - 1. A Topic Publisher is instantiated to send the message request to the queuing system. - 2. Once the message is dispatched by the exchange, it is fetched by the Topic Consumer dictated by the routing key (such as 'topic') and passed to the Worker in charge of the task. - -.. image:: ./images/rpc/flow2.png - :width: 60% - -.. - -AMQP Broker Load ----------------- - -At any given time the load of a message broker node running either Qpid or RabbitMQ is function of the following parameters: - - * Throughput of API calls: the number of API calls (more precisely rpc.call ops) being served by the OpenStack cloud dictates the number of direct-based exchanges, related queues and direct consumers connected to them. - * Number of Workers: there is one queue shared amongst workers with the same personality; however there are as many exclusive queues as the number of workers; the number of workers dictates also the number of routing keys within the topic-based exchange, which is shared amongst all workers. - -The figure below shows the status of a RabbitMQ node after Nova components' bootstrap in a test environment. Exchanges and queues being created by Nova components are: - - * Exchanges - 1. nova (topic exchange) - * Queues - 1. compute.phantom (phantom is hostname) - 2. compute - 3. network.phantom (phantom is hostname) - 4. network - 5. scheduler.phantom (phantom is hostname) - 6. scheduler - -.. image:: ./images/rpc/state.png - :width: 60% - -.. - -RabbitMQ Gotchas ----------------- - -Nova uses Kombu to connect to the RabbitMQ environment. Kombu is a Python library that in turn uses AMQPLib, a library that implements the standard AMQP 0.8 at the time of writing. When using Kombu, Invokers and Workers need the following parameters in order to instantiate a Connection object that connects to the RabbitMQ server (please note that most of the following material can be also found in the Kombu documentation; it has been summarized and revised here for sake of clarity): - - * Hostname: The hostname to the AMQP server. - * Userid: A valid username used to authenticate to the server. - * Password: The password used to authenticate to the server. - * Virtual_host: The name of the virtual host to work with. This virtual host must exist on the server, and the user must have access to it. Default is "/". - * Port: The port of the AMQP server. Default is 5672 (amqp). - -The following parameters are default: - - * Insist: insist on connecting to a server. In a configuration with multiple load-sharing servers, the Insist option tells the server that the client is insisting on a connection to the specified server. Default is False. - * Connect_timeout: the timeout in seconds before the client gives up connecting to the server. The default is no timeout. - * SSL: use SSL to connect to the server. The default is False. - -More precisely Consumers need the following parameters: - - * Connection: the above mentioned Connection object. - * Queue: name of the queue. - * Exchange: name of the exchange the queue binds to. - * Routing_key: the interpretation of the routing key depends on the value of the exchange_type attribute. - - * Direct exchange: if the routing key property of the message and the routing_key attribute of the queue are identical, then the message is forwarded to the queue. - * Fanout exchange: messages are forwarded to the queues bound the exchange, even if the binding does not have a key. - * Topic exchange: if the routing key property of the message matches the routing key of the key according to a primitive pattern matching scheme, then the message is forwarded to the queue. The message routing key then consists of words separated by dots (".", like domain names), and two special characters are available; star ("") and hash ("#"). The star matches any word, and the hash matches zero or more words. For example ".stock.#" matches the routing keys "usd.stock" and "eur.stock.db" but not "stock.nasdaq". - - * Durable: this flag determines the durability of both exchanges and queues; durable exchanges and queues remain active when a RabbitMQ server restarts. Non-durable exchanges/queues (transient exchanges/queues) are purged when a server restarts. It is worth noting that AMQP specifies that durable queues cannot bind to transient exchanges. Default is True. - * Auto_delete: if set, the exchange is deleted when all queues have finished using it. Default is False. - * Exclusive: exclusive queues (such as non-shared) may only be consumed from by the current connection. When exclusive is on, this also implies auto_delete. Default is False. - * Exchange_type: AMQP defines several default exchange types (routing algorithms) that covers most of the common messaging use cases. - * Auto_ack: acknowledgment is handled automatically once messages are received. By default auto_ack is set to False, and the receiver is required to manually handle acknowledgment. - * No_ack: it disable acknowledgment on the server-side. This is different from auto_ack in that acknowledgment is turned off altogether. This functionality increases performance but at the cost of reliability. Messages can get lost if a client dies before it can deliver them to the application. - * Auto_declare: if this is True and the exchange name is set, the exchange will be automatically declared at instantiation. Auto declare is on by default. - Publishers specify most the parameters of Consumers (such as they do not specify a queue name), but they can also specify the following: - * Delivery_mode: the default delivery mode used for messages. The value is an integer. The following delivery modes are supported by RabbitMQ: - - * 1 or "transient": the message is transient. Which means it is stored in memory only, and is lost if the server dies or restarts. - * 2 or "persistent": the message is persistent. Which means the message is stored both in-memory, and on disk, and therefore preserved if the server dies or restarts. - -The default value is 2 (persistent). During a send operation, Publishers can override the delivery mode of messages so that, for example, transient messages can be sent over a durable queue. diff --git a/doc/source/sample_config.rst b/doc/source/sample_config.rst deleted file mode 100644 index ae8e6f17bf2..00000000000 --- a/doc/source/sample_config.rst +++ /dev/null @@ -1,12 +0,0 @@ -========================== -Nova Configuration Options -========================== - -The following is a sample Nova configuration for adaptation and use. It is -auto-generated from Nova when this documentation is built, so -if you are having issues with an option, please compare your version of -Nova with the version of this documentation. - -The sample configuration can also be viewed in `file form <_static/nova.conf.sample>`_. - -.. literalinclude:: _static/nova.conf.sample diff --git a/doc/source/stable_api.rst b/doc/source/stable_api.rst deleted file mode 100644 index 645484ad20e..00000000000 --- a/doc/source/stable_api.rst +++ /dev/null @@ -1,92 +0,0 @@ -.. - Copyright 2015 Intel - 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. - - -Nova Stable REST API -==================== - -This document describes both the current state of the Nova REST API -- as -of the Kilo release -- and also attempts to describe how the Nova team intends -to evolve the REST API's implementation over time and remove some of the -cruft that has crept in over the years. - -Background ----------- - -Nova currently includes two distinct frameworks for exposing REST API -functionality. Older code is called the "V2 API" and exists in the -/nova/api/openstack/compute/contrib/ directory. Newer code is called the -"v2.1 API" and exists in the /nova/api/openstack/compute/plugins directory. - -The V2 API is the old Nova REST API. It will be replaced by V2.1 API totally. -The code tree of V2 API will be removed in the future also. - -The V2.1 API is the new Nova REST API with a set of improvements which -includes Microversion and standardized validation of inputs using JSON-Schema. -Also the V2.1 API is totally backwards compatible with the V2 API (That is the -reason we call it as V2.1 API). - -Stable API ----------- - -In the V2 API, there is a concept called 'extension'. An operator can use it -to enable/disable part of Nova REST API based on requirements. An end user -may query the '/extensions' API to discover what *API functionality* is -supported by the Nova deployment. - -Unfortunately, because V2 API extensions could be enabled or disabled -from one deployment to another -- as well as custom API extensions added -to one deployment and not another -- it was impossible for an end user to -know what the OpenStack Compute API actually included. No two OpenStack -deployments were consistent, which made cloud interoperability impossible. - -API extensions, while not (yet) removed from the V2.1 API, are no longer -needed to evolve the REST API, and no new API functionality should use -the API extension classes to implement new functionality. Instead, new -API functionality should use the microversioning decorators to add or change -the REST API. - -The extension is considered as two things in the Nova V2.1 API: - -* The '/extensions' API - - In the V2 API the user can query it to determine what APIs are supported by - the current Nova deployment. - - In V2.1 API, microversions enable us to add new features in backwards- - compatible ways. And microversions not only enable us to add new futures by - backwards-compatible method, also can be added by appropriate backwards- - incompatible method. - - The '/extensions' API is frozen in Nova V2.1 API and will be deprecated - in the future. - -* The plugin framework - - One of the improvements in the V2.1 API was using stevedore to load - Nova REST API extensions instead of old V2 handcrafted extension load - mechanism. - - There was an argument that the plugin framework supported extensibility in - the Nova API to allow deployers to publish custom API resources. - - We will keep the existing plugin mechanisms in place within Nova but only - to enable modularity in the codebase, not to allow extending of the Nova - REST API. - -As the extension will be removed from Nove V2.1 REST API. So the concept of -core API and extension API is eliminated also. There is no difference between -Nova V2.1 REST API, all of them are part of Nova stable REST API. diff --git a/doc/source/test_strategy.rst b/doc/source/test_strategy.rst deleted file mode 100644 index 0f7acc720dd..00000000000 --- a/doc/source/test_strategy.rst +++ /dev/null @@ -1,110 +0,0 @@ -.. - 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. - -============== -Test Strategy -============== - -A key part of the "four opens" is ensuring the OpenStack delivers well-tested -and usable software. For more details see: -http://docs.openstack.org/project-team-guide/introduction.html#the-four-opens - -Experience has shown that untested features are frequently broken, in part -due to the velocity of upstream changes. As we aim to ensure we keep all -features working across upgrades, we must aim to test all features. - -Reporting Test Coverage -======================= - -For details on plans to report the current test coverage, please see: -:doc:`feature_classification` - -Running tests and reporting results -=================================== - -Voting in Gerrit ----------------- - -On every review in gerrit, check tests are run on very patch set, and are -able to report a +1 or -1 vote. -For more details, please see: -http://docs.openstack.org/infra/manual/developers.html#automated-testing - -Before merging any code, there is an integrate gate test queue, to ensure -master is always passing all tests. -For more details, please see: -http://docs.openstack.org/infra/zuul/gating.html - -Infra vs Third-Party --------------------- - -Tests that use fully open source components are generally run by the -OpenStack Infra teams. Test setups that use non-open technology must -be run outside of that infrastructure, but should still report their -results upstream. - -For more details, please see: -http://docs.openstack.org/infra/system-config/third_party.html - -Ad-hoc testing --------------- - -It is particularly common for people to run ad-hoc tests on each released -milestone, such as RC1, to stop regressions. -While these efforts can help stabilize the release, as a community we have a -much stronger preference for continuous integration testing. Partly this is -because we encourage users to deploy master, and we generally have to assume -that any upstream commit may already been deployed in production. - -Types of tests -============== - -Unit tests ----------- - -Unit tests help document and enforce the contract for each component. -Without good unit test coverage it is hard to continue to quickly evolve the -codebase. -The correct level of unit test coverage is very subjective, and as such we are -not aiming for a particular percentage of coverage, rather we are aiming for -good coverage. -Generally, every code change should have a related unit test: -http://docs.openstack.org/developer/hacking/#creating-unit-tests - -Integration tests ------------------ - -Today, our integration tests involve running the Tempest test suite on a -variety of Nova deployment scenarios. - -In addition, we have third parties running the tests on their preferred Nova -deployment scenario. - -Functional tests ----------------- - -Nova has a set of in-tree functional tests that focus on things that are out -of scope for tempest testing and unit testing. -Tempest tests run against a full live OpenStack deployment, generally deployed -using devstack. At the other extreme, unit tests typically use mock to test a -unit of code in isolation. -Functional tests don't run an entire stack, they are isolated to nova code, -and have no reliance on external services. They do have a WSGI app, nova -services and a database, with minimal stubbing of nova internals. - -Interoperability tests ------------------------ - -The DefCore committee maintains a list that contains a subset of Tempest tests. -These are used to verify if a particular Nova deployment's API responds as -expected. For more details, see: https://github.com/openstack/defcore diff --git a/doc/source/testing/libvirt-numa.rst b/doc/source/testing/libvirt-numa.rst deleted file mode 100644 index 9315823c154..00000000000 --- a/doc/source/testing/libvirt-numa.rst +++ /dev/null @@ -1,676 +0,0 @@ - -================================================ -Testing NUMA related hardware setup with libvirt -================================================ - -This page describes how to test the libvirt driver's handling -of the NUMA placement, large page allocation and CPU pinning -features. It relies on setting up a virtual machine as the -test environment and requires support for nested virtualization -since plain QEMU is not sufficiently functional. The virtual -machine will itself be given NUMA topology, so it can then -act as a virtual "host" for testing purposes. - ------------------------------------------- -Provisioning a virtual machine for testing ------------------------------------------- - -The entire test process will take place inside a large virtual -machine running Fedora 21. The instructions should work for any -other Linux distribution which includes libvirt >= 1.2.9 and -QEMU >= 2.1.2 - -The tests will require support for nested KVM, which is not enabled -by default on hypervisor hosts. It must be explicitly turned on in -the host when loading the kvm-intel/kvm-amd kernel modules. - -On Intel hosts verify it with - -.. code-block:: bash - - # cat /sys/module/kvm_intel/parameters/nested - N - - # rmmod kvm-intel - # echo "options kvm-intel nested=y" > /etc/modprobe.d/dist.conf - # modprobe kvm-intel - - # cat /sys/module/kvm_intel/parameters/nested - Y - -While on AMD hosts verify it with - -.. code-block:: bash - - # cat /sys/module/kvm_amd/parameters/nested - 0 - - # rmmod kvm-amd - # echo "options kvm-amd nested=1" > /etc/modprobe.d/dist.conf - # modprobe kvm-amd - - # cat /sys/module/kvm_amd/parameters/nested - 1 - -The virt-install command below shows how to provision a -basic Fedora 21 x86_64 guest with 8 virtual CPUs, 8 GB -of RAM and 20 GB of disk space: - -.. code-block:: bash - - # cd /var/lib/libvirt/images - # wget http://download.fedoraproject.org/pub/fedora/linux/releases/test/21-Alpha/Server/x86_64/iso/Fedora-Server-netinst-x86_64-21_Alpha.iso - - # virt-install \ - --name f21x86_64 \ - --ram 8000 \ - --vcpus 8 \ - --file /var/lib/libvirt/images/f21x86_64.img \ - --file-size 20 - --cdrom /var/lib/libvirt/images/Fedora-Server-netinst-x86_64-21_Alpha.iso \ - --os-variant fedora20 - -When the virt-viewer application displays the installer, follow -the defaults for the installation with a couple of exceptions - -* The automatic disk partition setup can be optionally tweaked to - reduce the swap space allocated. No more than 500MB is required, - free'ing up an extra 1.5 GB for the root disk. - -* Select "Minimal install" when asked for the installation type - since a desktop environment is not required. - -* When creating a user account be sure to select the option - "Make this user administrator" so it gets 'sudo' rights - -Once the installation process has completed, the virtual machine -will reboot into the final operating system. It is now ready to -deploy an OpenStack development environment. - ---------------------------------- -Setting up a devstack environment ---------------------------------- - -For later ease of use, copy your SSH public key into the virtual -machine - -.. code-block:: bash - - # ssh-copy-id - -Now login to the virtual machine - -.. code-block:: bash - - # ssh - -We'll install devstack under $HOME/src/cloud/. - -.. code-block:: bash - - # mkdir -p $HOME/src/cloud - # cd $HOME/src/cloud - # chmod go+rx $HOME - -The Fedora minimal install does not contain git and only -has the crude & old-fashioned "vi" editor. - -.. code-block:: bash - - # sudo yum -y install git emacs - -At this point a fairly standard devstack setup can be -done. The config below is just an example that is -convenient to use to place everything in $HOME instead -of /opt/stack. Change the IP addresses to something -appropriate for your environment of course - -.. code-block:: bash - - # git clone git://github.com/openstack-dev/devstack.git - # cd devstack - # cat >>local.conf < select numa_topology from compute_nodes; - +----------------------------------------------------------------------------+ - | numa_topology | - +----------------------------------------------------------------------------+ - | { - | "nova_object.name": "NUMATopology", - | "nova_object.data": { - | "cells": [{ - | "nova_object.name": "NUMACell", - | "nova_object.data": { - | "cpu_usage": 0, - | "memory_usage": 0, - | "cpuset": [0, 1, 2, 3, 4, 5, 6, 7], - | "pinned_cpus": [], - | "siblings": [], - | "memory": 7793, - | "mempages": [ - | { - | "nova_object.name": "NUMAPagesTopology", - | "nova_object.data": { - | "total": 987430, - | "used": 0, - | "size_kb": 4 - | }, - | }, - | { - | "nova_object.name": "NUMAPagesTopology", - | "nova_object.data": { - | "total": 0, - | "used": 0, - | "size_kb": 2048 - | }, - | } - | ], - | "id": 0 - | }, - | }, - | ] - | }, - | } - +----------------------------------------------------------------------------+ - - -Meanwhile, the guest instance should not have any NUMA configuration -recorded - -.. code-block:: bash - - MariaDB [nova]> select numa_topology from instance_extra; - +---------------+ - | numa_topology | - +---------------+ - | NULL | - +---------------+ - - - ------------------------------------------------------ -Reconfiguring the test instance to have NUMA topology ------------------------------------------------------ - -Now that devstack is proved operational, it is time to configure some -NUMA topology for the test VM, so that it can be used to verify the -OpenStack NUMA support. To do the changes, the VM instance that is running -devstack must be shut down. - -.. code-block:: bash - - # sudo shutdown -h now - -And now back on the physical host edit the guest config as root - -.. code-block:: bash - - # sudo virsh edit f21x86_64 - -The first thing is to change the block to do passthrough of the -host CPU. In particular this exposes the "SVM" or "VMX" feature bits -to the guest so that "Nested KVM" can work. At the same time we want -to define the NUMA topology of the guest. To make things interesting -we're going to give the guest an asymmetric topology with 4 CPUS and -4 GBs of RAM in the first NUMA node and 2 CPUs and 2 GB of RAM in -the second and third NUMA nodes. So modify the guest XML to include -the following CPU XML - -.. code-block:: bash - - - - - - - - - -The guest can now be started again, and ssh back into it - -.. code-block:: bash - - # virsh start f21x86_64 - - ...wait for it to finish booting - - # ssh - - -Before starting OpenStack services again, it is necessary to -reconfigure Nova to enable the NUMA scheduler filter. The libvirt -virtualization type must also be explicitly set to KVM, so that -guests can take advantage of nested KVM. - -.. code-block:: bash - - # sudo emacs /etc/nova/nova.conf - -Set the following parameters: - -.. code-block:: bash - - [DEFAULT] - scheduler_default_filters=RetryFilter, AvailabilityZoneFilter, RamFilter, ComputeFilter, ComputeCapabilitiesFilter, ImagePropertiesFilter, ServerGroupAntiAffinityFilter, ServerGroupAffinityFilter, NUMATopologyFilter - - [libvirt] - virt_type = kvm - - -With that done, OpenStack can be started again - -.. code-block:: bash - - # cd $HOME/src/cloud/devstack - # ./rejoin-stack.sh - - -The first thing is to check that the compute node picked up the -new NUMA topology setup for the guest - -.. code-block:: bash - - # mysql -u root -p nova - MariaDB [nova]> select numa_topology from compute_nodes; - +----------------------------------------------------------------------------+ - | numa_topology | - +----------------------------------------------------------------------------+ - | { - | "nova_object.name": "NUMATopology", - | "nova_object.data": { - | "cells": [{ - | "nova_object.name": "NUMACell", - | "nova_object.data": { - | "cpu_usage": 0, - | "memory_usage": 0, - | "cpuset": [0, 1, 2, 3], - | "pinned_cpus": [], - | "siblings": [], - | "memory": 3857, - | "mempages": [ - | { - | "nova_object.name": "NUMAPagesTopology", - | "nova_object.data": { - | "total": 987430, - | "used": 0, - | "size_kb": 4 - | }, - | }, - | { - | "nova_object.name": "NUMAPagesTopology", - | "nova_object.data": { - | "total": 0, - | "used": 0, - | "size_kb": 2048 - | }, - | } - | ], - | "id": 0 - | }, - | }, - | { - | "nova_object.name": "NUMACell", - | "nova_object.data": { - | "cpu_usage": 0, - | "memory_usage": 0, - | "cpuset": [4, 5], - | "pinned_cpus": [], - | "siblings": [], - | "memory": 1969, - | "mempages": [ - | { - | "nova_object.name": "NUMAPagesTopology", - | "nova_object.data": { - | "total": 504216, - | "used": 0, - | "size_kb": 4 - | }, - | }, - | { - | "nova_object.name": "NUMAPagesTopology", - | "nova_object.data": { - | "total": 0, - | "used": 0, - | "size_kb": 2048 - | }, - | } - | ], - | "id": 1 - | }, - | }, - | { - | "nova_object.name": "NUMACell", - | "nova_object.data": { - | "cpu_usage": 0, - | "memory_usage": 0, - | "cpuset": [6, 7], - | "pinned_cpus": [], - | "siblings": [], - | "memory": 1967, - | "mempages": [ - | { - | "nova_object.name": "NUMAPagesTopology", - | "nova_object.data": { - | "total": 503575, - | "used": 0, - | "size_kb": 4 - | }, - | }, - | { - | "nova_object.name": "NUMAPagesTopology", - | "nova_object.data": { - | "total": 0, - | "used": 0, - | "size_kb": 2048 - | }, - | } - | ], - | "id": 2 - | }, - | } - | ] - | }, - | } - +----------------------------------------------------------------------------+ - -This indeed shows that there are now 3 NUMA nodes for the "host" -machine, the first with 4 GB of RAM and 4 CPUs, and others with -2 GB of RAM and 2 CPUs each. - - ------------------------------------------------------ -Testing instance boot with no NUMA topology requested ------------------------------------------------------ - -For the sake of backwards compatibility, if the NUMA filter is -enabled, but the flavor/image does not have any NUMA settings -requested, it should be assumed that the guest will have a -single NUMA node. The guest should be locked to a single host -NUMA node too. Boot a guest with the m1.tiny flavor to test -this condition - -.. code-block:: bash - - # . openrc admin admin - # nova boot --image cirros-0.3.2-x86_64-uec --flavor m1.tiny cirros1 - -Now look at the libvirt guest XML. It should show that the vCPUs are -locked to pCPUs within a particular node. - -.. code-block:: bash - - # virsh -c qemu:///system list - .... - # virsh -c qemu:///system dumpxml instanceXXXXXX - ... - 1 - ... - -This example shows that the guest has been locked to the 3rd NUMA -node (which contains pCPUs 6 and 7). Note that there is no explicit -NUMA topology listed in the guest XML. - ------------------------------------------------- -Testing instance boot with 1 NUMA cell requested ------------------------------------------------- - -Moving forward a little, explicitly tell Nova that the NUMA topology -for the guest should have a single NUMA node. This should operate -in an identical manner to the default behavior where no NUMA policy -is set. To define the topology we will create a new flavor - -.. code-block:: bash - - # nova flavor-create m1.numa 999 1024 1 4 - # nova flavor-key m1.numa set hw:numa_nodes=1 - # nova flavor-show m1.numa - -Now boot the guest using this new flavor - -.. code-block:: bash - - # nova boot --image cirros-0.3.2-x86_64-uec --flavor m1.numa cirros2 - -Looking at the resulting guest XML from libvirt - -.. code-block:: bash - - # virsh -c qemu:///system dumpxml instanceXXXXXX - ... - 4 - - - - - - - - ... - - - - - - - ... - - - - - -The XML shows: - -* Each guest CPU has been pinned to the physical CPUs - associated with a particular NUMA node -* The emulator threads have been pinned to the union - of all physical CPUs in the host NUMA node that - the guest is placed on -* The guest has been given a virtual NUMA topology - with a single node holding all RAM and CPUs -* The guest NUMA node has been strictly pinned to - a host NUMA node. - -As a further sanity test, check what Nova recorded for the -instance in the database. This should match the -information - -.. code-block:: bash - - MariaDB [nova]> select numa_topology from instance_extra; - +----------------------------------------------------------------------------+ - | numa_topology | - +----------------------------------------------------------------------------+ - | { - | "nova_object.name": "InstanceNUMATopology", - | "nova_object.data": { - | "instance_uuid": "4c2302fe-3f0f-46f1-9f3e-244011f6e03a", - | "cells": [ - | { - | "nova_object.name": "InstanceNUMACell", - | "nova_object.data": { - | "cpu_topology": null, - | "pagesize": null, - | "cpuset": [ - | 0, - | 1, - | 2, - | 3 - | ], - | "memory": 1024, - | "cpu_pinning_raw": null, - | "id": 0 - | }, - | } - | ] - | }, - | } - +----------------------------------------------------------------------------+ - -------------------------------------------------- -Testing instance boot with 2 NUMA cells requested -------------------------------------------------- - -Now getting more advanced we tell Nova that the guest will have two -NUMA nodes. To define the topology we will change the previously -defined flavor - -.. code-block:: bash - - # nova flavor-key m1.numa set hw:numa_nodes=2 - # nova flavor-show m1.numa - -Now boot the guest using this changed flavor - -.. code-block:: bash - - # nova boot --image cirros-0.3.2-x86_64-uec --flavor m1.numa cirros2 - -Looking at the resulting guest XML from libvirt - -.. code-block:: bash - - # virsh -c qemu:///system dumpxml instanceXXXXXX - ... - 4 - - - - - - - - ... - - - - - - - - ... - - - - - - -The XML shows: - -* Each guest CPU has been pinned to the physical CPUs - associated with particular NUMA nodes -* The emulator threads have been pinned to the union - of all physical CPUs in the host NUMA nodes that - the guest is placed on -* The guest has been given a virtual NUMA topology - with two nodes, each holding half the RAM and CPUs -* The guest NUMA nodes have been strictly pinned to - different host NUMA node. - -As a further sanity test, check what Nova recorded for the -instance in the database. This should match the -information - -.. code-block:: bash - - MariaDB [nova]> select numa_topology from instance_extra; - +----------------------------------------------------------------------------+ - | numa_topology | - +----------------------------------------------------------------------------+ - | { - | "nova_object.name": "InstanceNUMATopology", - | "nova_object.data": { - | "instance_uuid": "a14fcd68-567e-4d71-aaa4-a12f23f16d14", - | "cells": [ - | { - | "nova_object.name": "InstanceNUMACell", - | "nova_object.data": { - | "cpu_topology": null, - | "pagesize": null, - | "cpuset": [ - | 0, - | 1 - | ], - | "memory": 512, - | "cpu_pinning_raw": null, - | "id": 0 - | }, - | }, - | { - | "nova_object.name": "InstanceNUMACell", - | "nova_object.data": { - | "cpu_topology": null, - | "pagesize": null, - | "cpuset": [ - | 2, - | 3 - | ], - | "memory": 512, - | "cpu_pinning_raw": null, - | "id": 1 - | }, - | } - | ] - | }, - | } - | - +----------------------------------------------------------------------------+ diff --git a/doc/source/upgrade.rst b/doc/source/upgrade.rst deleted file mode 100644 index d679aaeb012..00000000000 --- a/doc/source/upgrade.rst +++ /dev/null @@ -1,258 +0,0 @@ -.. - Copyright 2014 Rackspace - 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. - -Upgrades -======== - -Nova aims to provide upgrades with minimal downtime. - -Firstly, the data plane. There should be no VM downtime when you upgrade -Nova. Nova has had this since the early days, with the exception of -some nova-network related services. - -Secondly, we want no downtime during upgrades of the Nova control plane. -This document is trying to describe how we can achieve that. - -Once we have introduced the key concepts relating to upgrade, we will -introduce the process needed for a no downtime upgrade of nova. - - -Current Database Upgrade Types ------------------------------- - -Currently Nova has 2 types of database upgrades that are in use. - -#. Offline Migrations -#. Online Migrations - - -Offline Migrations consist of: -'''''''''''''''''''''''''''''' - - #. Database schema migrations from pre-defined migrations in - nova/db/sqlalchemy/migrate_repo/versions. - - #. *Deprecated* Database data migrations from pre-defined migrations in - nova/db/sqlalchemy/migrate_repo/versions. - - -Online Migrations consist of: -''''''''''''''''''''''''''''' - - #. Online data migrations from inside Nova object source code. - - #. *Future* Online schema migrations using auto-generation from models. - - -An example of online data migrations are the flavor migrations done as part -of Nova object version 1.18. This included a transient migration of flavor -storage from one database location to another. - -:emphasis:`Note: Database downgrades are not supported.` - -Migration policy: -''''''''''''''''' - -The following guidelines for schema and data migrations are followed in order -to ease upgrades: - -* Additive schema migrations - In general, almost all schema migrations should - be additive. Put simply, they should only create elements like columns, - indices, and tables. - -* Subtractive schema migrations - To remove an element like a column or table - during the N release cycle: - - #. The element must be deprecated and retained for backward compatibility. - (This allows for graceful upgrade from N to N+1.) - - #. Data migration, by the objects layer, must completely migrate data from - the old version of the schema to the new version. - - * `Data migration example - `_ - * `Data migration enforcement example - `_ - (for sqlalchemy migrate/deprecated scripts): - - #. The column can then be removed with a migration at the start of N+2. - -* All schema migrations should be idempotent. (For example, a migration - should check if an element exists in the schema before attempting to add - it.) This logic comes for free in the autogenerated workflow of - the online migrations. - -* Constraints - When adding a foreign or unique key constraint, the schema - migration code needs to handle possible problems with data before applying - the constraint. (Example: A unique constraint must clean up duplicate - records before applying said constraint.) - -* Data migrations - As mentioned above, data migrations will be done in an - online fashion by custom code in the object layer that handles moving data - between the old and new portions of the schema. In addition, for each type - of data migration performed, there should exist a nova-manage option for an - operator to manually request that rows be migrated. - - * See `flavor migration spec - `_ - for an example of data migrations in the object layer. - -*Future* work - - #. Adding plumbing to enforce that relevant data migrations are completed - before running `contract` in the expand/migrate/contract schema migration - workflow. A potential solution would be for `contract` to run a gating - test for each specific subtract operation to determine if the operation - can be completed. - -Concepts --------- - -Here are the key concepts you need to know before reading the section on the -upgrade process: - -RPC version pinning - Through careful RPC versioning, newer nodes are able to talk to older - nova-compute nodes. When upgrading control plane nodes, we can pin them - at an older version of the compute RPC API, until all the compute nodes - are able to be upgraded. - https://wiki.openstack.org/wiki/RpcMajorVersionUpdates - - .. note:: - - This does not apply to cells deployments since cells does not currently - support rolling upgrades. It is assumed that cells deployments are - upgraded in lockstep so n-1 cells compatibility does not work. - -Online Configuration Reload - During the upgrade, we pin new serves at the older RPC version. When all - services are updated to use newer code, we need to unpin them so we are - able to use any new functionality. - To avoid having to restart the service, using the current SIGHUP signal - handling, or otherwise, ideally we need a way to update the currently - running process to use the latest configuration. - -Graceful service shutdown - Many nova services are python processes listening for messages on a - AMQP queue, including nova-compute. When sending the process the SIGTERM - the process stops getting new work from its queue, completes any - outstanding work, then terminates. During this process, messages can be - left on the queue for when the python process starts back up. - This gives us a way to shutdown a service using older code, and start - up a service using newer code with minimal impact. If its a service that - can have multiple workers, like nova-conductor, you can usually add the - new workers before the graceful shutdown of the old workers. In the case - of singleton services, like nova-compute, some actions could be delayed - during the restart, but ideally no actions should fail due to the restart. - NOTE: while this is true for the RabbitMQ RPC backend, we need to confirm - what happens for other RPC backends. - -API load balancer draining - When upgrading API nodes, you can make your load balancer only send new - connections to the newer API nodes, allowing for a seamless update of your - API nodes. - -Expand/Contract DB Migrations - Modern databases are able to make many schema changes while you are still - writing to the database. Taking this a step further, we can make all DB - changes by first adding the new structures, expanding. Then you can slowly - move all the data into a new location and format. Once that is complete, - you can drop bits of the scheme that are no long needed, i.e. contract. - We have plans to implement this here: - https://review.openstack.org/#/c/102545/5/specs/juno/online-schema-changes.rst,cm - -Online Data Migrations using objects - In Kilo we are moving all data migration into the DB objects code. - When trying to migrate data in the database from the old format to the - new format, this is done in the object code when reading or saving things - that are in the old format. For records that are not updated, you need to - run a background process to convert those records into the newer format. - This process must be completed before you contract the database schema. - We have the first example of this happening here: - http://specs.openstack.org/openstack/nova-specs/specs/kilo/approved/flavor-from-sysmeta-to-blob.html - -DB prune deleted rows - Currently resources are soft deleted in the database, so users are able - to track instances in the DB that are created and destroyed in production. - However, most people have a data retention policy, of say 30 days or 90 - days after which they will want to delete those entries. Not deleting - those entries affects DB performance as indices grow very large and data - migrations take longer as there is more data to migrate. - -nova-conductor object backports - RPC pinning ensures new services can talk to the older service's method - signatures. But many of the parameters are objects that may well be too - new for the old service to understand, so you are able to send the object - back to the nova-conductor to be downgraded to a version the older service - can understand. - - -Process -------- - -NOTE: - This still requires much work before it can become reality. - This is more an aspirational plan that helps describe how all the - pieces of the jigsaw fit together. - -This is the planned process for a zero downtime upgrade: - -#. Prune deleted DB rows, check previous migrations are complete - -#. Expand DB schema (e.g. add new column) - -#. Pin RPC versions for all services that are upgraded from this point, - using the current version - -#. Upgrade all nova-conductor nodes (to do object backports) - -#. Upgrade all other services, except nova-compute and nova-api, - using graceful shutdown - -#. Upgrade nova-compute nodes (this is the bulk of the work). - -#. Unpin RPC versions - -#. Add new API nodes, and enable new features, while using a load balancer - to "drain" the traffic from old API nodes - -#. Run the new nova-manage command that ensures all DB records are "upgraded" - to new data version - -#. "Contract" DB schema (e.g. drop unused columns) - - -Testing -------- - -Once we have all the pieces in place, we hope to move the Grenade testing -to follow this new pattern. - -The current tests only cover the existing upgrade process where: -* old computes can run with new control plane -* but control plane is turned off for DB migrations - -Unresolved issues ------------------ - -Ideally you could rollback. We would need to add some kind of object data -version pinning, so you can be running all new code to some extent, before -there is no path back. Or have some way of reversing the data migration -before the final contract. - -It is unknown how expensive on demand object backports would be. We could -instead always send older versions of objects until the RPC pin is removed, -but that means we might have new code getting old objects, which is currently -not the case. diff --git a/doc/source/aggregates.rst b/doc/source/user/aggregates.rst similarity index 82% rename from doc/source/aggregates.rst rename to doc/source/user/aggregates.rst index 12aa2a80cb7..3e5eecf0ed5 100644 --- a/doc/source/aggregates.rst +++ b/doc/source/user/aggregates.rst @@ -55,6 +55,9 @@ between aggregates and availability zones: moved to another aggregate or when the user would like to migrate the instance. +.. note:: Availablity zone name must NOT contain ':' since it is used by admin + users to specify hosts where instances are launched in server creation. + See :doc:`Select hosts where instances are launched ` for more detail. Xen Pool Host Aggregates ------------------------ @@ -67,21 +70,21 @@ Design The OSAPI Admin API is extended to support the following operations: - * Aggregates +* Aggregates - * list aggregates: returns a list of all the host-aggregates (optionally filtered by availability zone) - * create aggregate: creates an aggregate, takes a friendly name, etc. returns an id - * show aggregate: shows the details of an aggregate (id, name, availability_zone, hosts and metadata) - * update aggregate: updates the name and availability zone of an aggregate - * set metadata: sets the metadata on an aggregate to the values supplied - * delete aggregate: deletes an aggregate, it fails if the aggregate is not empty - * add host: adds a host to the aggregate - * remove host: removes a host from the aggregate + * list aggregates: returns a list of all the host-aggregates (optionally filtered by availability zone) + * create aggregate: creates an aggregate, takes a friendly name, etc. returns an id + * show aggregate: shows the details of an aggregate (id, name, availability_zone, hosts and metadata) + * update aggregate: updates the name and availability zone of an aggregate + * set metadata: sets the metadata on an aggregate to the values supplied + * delete aggregate: deletes an aggregate, it fails if the aggregate is not empty + * add host: adds a host to the aggregate + * remove host: removes a host from the aggregate - * Hosts +* Hosts - * start host maintenance (or evacuate-host): disallow a host to serve API requests and migrate instances to other hosts of the aggregate - * stop host maintenance: (or rebalance-host): put the host back into operational mode, migrating instances back onto that host + * start host maintenance (or evacuate-host): disallow a host to serve API requests and migrate instances to other hosts of the aggregate + * stop host maintenance: (or rebalance-host): put the host back into operational mode, migrating instances back onto that host Using the Nova CLI ------------------ diff --git a/doc/source/architecture.rst b/doc/source/user/architecture.rst similarity index 96% rename from doc/source/architecture.rst rename to doc/source/user/architecture.rst index 5aacc92dce9..6e1e962c44b 100644 --- a/doc/source/architecture.rst +++ b/doc/source/user/architecture.rst @@ -32,7 +32,7 @@ a `manager` that is listening for `RPC` messages. The one major exception is nova-compute, where a single process runs on the hypervisor it is managing (except when using the VMware or Ironic drivers). The manager also, optionally, has periodic tasks. -For more details on our `RPC` system, please see: :doc:`rpc` +For more details on our `RPC` system, please see: :doc:`/reference/rpc` Nova also uses a central database that is (logically) shared between all components. However, to aid upgrade, the DB is accessed through an object @@ -50,7 +50,7 @@ Components Below you will find a helpful explanation of the key components of a typical (non-cells v1) Nova deployment. -.. image:: ./images/architecture.svg +.. image:: /_static/images/architecture.svg :width: 100% * DB: sql database for data storage. diff --git a/doc/source/user/block-device-mapping.rst b/doc/source/user/block-device-mapping.rst new file mode 100644 index 00000000000..4517a5bf9a1 --- /dev/null +++ b/doc/source/user/block-device-mapping.rst @@ -0,0 +1,208 @@ +.. + 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. + +Block Device Mapping in Nova +============================ + +Nova has a concept of block devices that can be exposed to cloud instances. +There are several types of block devices an instance can have (we will go into +more details about this later in this document), and which ones are available +depends on a particular deployment and the usage limitations set for tenants +and users. Block device mapping is a way to organize and keep data about all of +the block devices an instance has. + +When we talk about block device mapping, we usually refer to one of two things + +1. API/CLI structure and syntax for specifying block devices for an instance + boot request + +2. The data structure internal to Nova that is used for recording and keeping, + which is ultimately persisted in the block_device_mapping table. However, + Nova internally has several "slightly" different formats for representing + the same data. All of them are documented in the code and or presented by + a distinct set of classes, but not knowing that they exist might trip up + people reading the code. So in addition to BlockDeviceMapping [1]_ objects + that mirror the database schema, we have: + + 2.1 The API format - this is the set of raw key-value pairs received from + the API client, and is almost immediately transformed into the object; + however, some validations are done using this format. We will refer to this + format as the 'API BDMs' from now on. + + 2.2 The virt driver format - this is the format defined by the classes in + :mod: `nova.virt.block_device`. This format is used and expected by the code + in the various virt drivers. These classes, in addition to exposing a + different format (mimicking the Python dict interface), also provide a place + to bundle some functionality common to certain types of block devices (for + example attaching volumes which has to interact with both Cinder and the + virt driver code). We will refer to this format as 'Driver BDMs' from now + on. + + +Data format and its history +---------------------------- + +In the early days of Nova, block device mapping general structure closely +mirrored that of the EC2 API. During the Havana release of Nova, block device +handling code, and in turn the block device mapping structure, had work done on +improving the generality and usefulness. These improvements included exposing +additional details and features in the API. In order to facilitate this, a new +extension was added to the v2 API called `BlockDeviceMappingV2Boot` [2]_, that +added an additional `block_device_mapping_v2` field to the instance boot API +request. + +Block device mapping v1 (aka legacy) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This was the original format that supported only cinder volumes (similar to how +EC2 block devices support only EBS volumes). Every entry was keyed by device +name (we will discuss why this was problematic in its own section later on +this page), and would accept only: + +* UUID of the Cinder volume or snapshot +* Type field - used only to distinguish between volumes and Cinder volume + snapshots +* Optional size field +* Optional `delete_on_termination` flag + +While all of Nova internal code only uses and stores the new data structure, we +still need to handle API requests that use the legacy format. This is handled +by the Nova API service on every request. As we will see later, since block +device mapping information can also be stored in the image metadata in Glance, +this is another place where we need to handle the v1 format. The code to handle +legacy conversions is part of the :mod: `nova.block_device` module. + +Intermezzo - problem with device names +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Using device names as the primary per-instance identifier, and exposing them in +the API, is problematic for Nova mostly because several hypervisors Nova +supports with its drivers can't guarantee that the device names the guest OS +assigns are the ones the user requested from Nova. Exposing such a detail +in the public API of Nova is obviously not ideal, but it needed to stay for +backwards compatibility. It is also required for some (slightly obscure) +features around overloading a block device in a Glance image when booting an +instance [3]_. + +The plan for fixing this was to allow users to not specify the device name of a +block device, and Nova will determine it (with the help of the virt driver), so +that it can still be discovered through the API and used when necessary, like +for the features mentioned above (and preferably only then). + +Another use for specifying the device name was to allow the "boot from volume" +functionality, by specifying a device name that matches the root device name +for the instance (usually `/dev/vda`). + +Currently (mid Liberty) users are discouraged from specifying device names +for all calls requiring or allowing block device mapping, except when trying to +override the image block device mapping on instance boot, and it will likely +remain like that in the future. Libvirt device driver will outright override +any device names passed with it's own values. + +Block device mapping v2 +^^^^^^^^^^^^^^^^^^^^^^^ + +New format was introduced in an attempt to solve issues with the original +block device mapping format discussed above, and also to allow for more +flexibility and addition of features that were not possible with the simple +format we had. + +New block device mapping is a list of dictionaries containing the following +fields (in addition to the ones that were already there): + +* source_type - this can have one of the following values: + + * `image` + * `volume` + * `snapshot` + * `blank` + +* dest_type - this can have one of the following values: + + * `local` + * `volume` + +* guest_format - Tells Nova how/if to format the device prior to attaching, + should be only used with blank local images. Denotes a swap disk if the value + is `swap`. + +* device_name - See the previous section for a more in depth explanation of + this - currently best left empty (not specified that is), unless the user + wants to override the existing device specified in the image metadata. + In case of Libvirt, even when passed in with the purpose of overriding the + existing image metadata, final set of device names for the instance may still + get changed by the driver. + +* disk_bus and device_type - low level details that some hypervisors (currently + only libvirt) may support. Some example disk_bus values can be: `ide`, `usb`, + `virtio`, `scsi`, while device_type may be `disk`, `cdrom`, `floppy`, `lun`. + This is not an exhaustive list as it depends on the virtualization driver, + and may change as more support is added. Leaving these empty is the most + common thing to do. + +* boot_index - Defines the order in which a hypervisor will try devices when + attempting to boot the guest from storage. Each device which is capable of + being used as boot device should be given a unique boot index, starting from + 0 in ascending order. Some hypervisors may not support booting from multiple + devices, so will only consider the device with boot index of 0. Some + hypervisors will support booting from multiple devices, but only if they are + of different types - eg a disk and CD-ROM. Setting a negative value or None + indicates that the device should not be used for booting. The simplest + usage is to set it to 0 for the boot device and leave it as None for any + other devices. + +Valid source / dest combinations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Combination of the ``source_type`` and ``dest_type`` will define the +kind of block device the entry is referring to. The following +combinations are supported: + +* `image` -> `local` - this is only currently reserved for the entry + referring to the Glance image that the instance is being booted with + (it should also be marked as a boot device). It is also worth noting + that an API request that specifies this, also has to provide the + same Glance uuid as the `image_ref` parameter to the boot request + (this is done for backwards compatibility and may be changed in the + future). This functionality might be extended to specify additional + Glance images to be attached to an instance after boot (similar to + kernel/ramdisk images) but this functionality is not supported by + any of the current drivers. +* `volume` -> `volume` - this is just a Cinder volume to be attached to the + instance. It can be marked as a boot device. +* `snapshot` -> `volume` - this works exactly as passing `type=snap` does. + It would create a volume from a Cinder volume snapshot and attach that + volume to the instance. Can be marked bootable. +* `image` -> `volume` - As one would imagine, this would download a Glance + image to a cinder volume and attach it to an instance. Can also be marked + as bootable. This is really only a shortcut for creating a volume out of + an image before booting an instance with the newly created volume. +* `blank` -> `volume` - Creates a blank Cinder volume and attaches it. This + will also require the volume size to be set. +* `blank` -> `local` - Depending on the guest_format field (see below), + this will either mean an ephemeral blank disk on hypervisor local + storage, or a swap disk (instances can have only one of those). + + +Nova will not allow mixing of BDMv1 and BDMv2 in a single request, and +will do basic validation to make sure that the requested block device +mapping is valid before accepting a boot request. + +.. [1] In addition to the BlockDeviceMapping Nova object, we also have the + BlockDeviceDict class in :mod: `nova.block_device` module. This class + handles transforming and validating the API BDM format. +.. [2] This work predates API microversions and thus the only way to add it was + by means of an API extension. +.. [3] This is a feature that the EC2 API offers as well and has been in Nova + for a long time, although it has been broken in several releases. More info + can be found on `this bug ` diff --git a/doc/source/user/cells.rst b/doc/source/user/cells.rst new file mode 100644 index 00000000000..334bf0338c3 --- /dev/null +++ b/doc/source/user/cells.rst @@ -0,0 +1,613 @@ +.. + 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. + +.. _cells: + +======= + Cells +======= + +Before reading further, there is a nice overview presentation_ that +Andrew Laski gave at the Austin (Newton) summit which is worth watching. + +.. _presentation: https://www.openstack.org/videos/video/nova-cells-v2-whats-going-on + +Cells V1 +======== + +Historically, Nova has depended on a single logical database and message queue +that all nodes depend on for communication and data persistence. This becomes +an issue for deployers as scaling and providing fault tolerance for these +systems is difficult. + +We have an experimental feature in Nova called "cells", hereafter referred to +as "cells v1", which is used by some large deployments to partition compute +nodes into smaller groups, coupled with a database and queue. This seems to be +a well-liked and easy-to-understand arrangement of resources, but the +implementation of it has issues for maintenance and correctness. +See `Comparison with Cells V1`_ for more detail. + +Status +~~~~~~ + +.. warning:: Cells v1 is deprecated in favor of Cells v2 as of the + 16.0.0 Pike release. + +Cells v1 is considered experimental and receives much less testing than the +rest of Nova. For example, there is no job for testing cells v1 with Neutron. + +The priority for the core team is implementation of and migration to cells v2. +Because of this, there are a few restrictions placed on cells v1: + +#. Cells v1 is in feature freeze. This means no new feature proposals for cells + v1 will be accepted by the core team, which includes but is not limited to + API parity, e.g. supporting virtual interface attach/detach with Neutron. +#. Latent bugs caused by the cells v1 design will not be fixed, e.g. + `bug 1489581 `_. So if new + tests are added to Tempest which trigger a latent bug in cells v1 it may not + be fixed. However, regressions in working function should be tracked with + bugs and fixed. + +**Suffice it to say, new deployments of cells v1 are not encouraged.** + +The restrictions above are basically meant to prioritize effort and focus on +getting cells v2 completed, and feature requests and hard to fix latent bugs +detract from that effort. Further discussion on this can be found in the +`2015/11/12 Nova meeting minutes +`_. + +There are no plans to remove Cells V1 until V2 is usable by existing +deployments and there is a migration path. + + +Cells V2 +======== + +* `Newton Summit Video - Nova Cells V2: What's Going On? `_ +* `Pike Summit Video - Scaling Nova: How CellsV2 Affects Your Deployment `_ + +Manifesto +~~~~~~~~~ + +Proposal +-------- + +Right now, when a request hits the Nova API for a particular instance, the +instance information is fetched from the database, which contains the hostname +of the compute node on which the instance currently lives. If the request needs +to take action on the instance (which is most of them), the hostname is used to +calculate the name of a queue, and a message is written there which finds its +way to the proper compute node. + +The meat of this proposal is changing the above hostname lookup into two parts +that yield three pieces of information instead of one. Basically, instead of +merely looking up the *name* of the compute node on which an instance lives, we +will also obtain database and queue connection information. Thus, when asked to +take action on instance $foo, we will: + +1. Lookup the three-tuple of (database, queue, hostname) for that instance +2. Connect to that database and fetch the instance record +3. Connect to the queue and send the message to the proper hostname queue + +The above differs from the current organization in two ways. First, we need to +do two database lookups before we know where the instance lives. Second, we +need to demand-connect to the appropriate database and queue. Both of these +have performance implications, but we believe we can mitigate the impacts +through the use of things like a memcache of instance mapping information and +pooling of connections to database and queue systems. The number of cells will +always be much smaller than the number of instances. + +There are availability implications with this change since something like a +'nova list' which might query multiple cells could end up with a partial result +if there is a database failure in a cell. A database failure within a cell +would cause larger issues than a partial list result so the expectation is that +it would be addressed quickly and cellsv2 will handle it by indicating in the +response that the data may not be complete. + +Since this is very similar to what we have with current cells, in terms of +organization of resources, we have decided to call this "cellsv2" for +disambiguation. + +After this work is complete there will no longer be a "no cells" deployment. +The default installation of Nova will be a single cell setup. + +Benefits +-------- + +The benefits of this new organization are: + +* Native sharding of the database and queue as a first-class-feature in nova. + All of the code paths will go through the lookup procedure and thus we won't + have the same feature parity issues as we do with current cells. + +* No high-level replication of all the cell databases at the top. The API will + need a database of its own for things like the instance index, but it will + not need to replicate all the data at the top level. + +* It draws a clear line between global and local data elements. Things like + flavors and keypairs are clearly global concepts that need only live at the + top level. Providing this separation allows compute nodes to become even more + stateless and insulated from things like deleted/changed global data. + +* Existing non-cells users will suddenly gain the ability to spawn a new "cell" + from their existing deployment without changing their architecture. Simply + adding information about the new database and queue systems to the new index + will allow them to consume those resources. + +* Existing cells users will need to fill out the cells mapping index, shutdown + their existing cells synchronization service, and ultimately clean up their + top level database. However, since the high-level organization is not + substantially different, they will not have to re-architect their systems to + move to cellsv2. + +* Adding new sets of hosts as a new "cell" allows them to be plugged into a + deployment and tested before allowing builds to be scheduled to them. + +Comparison with Cells V1 +------------------------ + +In reality, the proposed organization is nearly the same as what we currently +have in cells today. A cell mostly consists of a database, queue, and set of +compute nodes. The primary difference is that current cells require a +nova-cells service that synchronizes information up and down from the top level +to the child cell. Additionally, there are alternate code paths in +compute/api.py which handle routing messages to cells instead of directly down +to a compute host. Both of these differences are relevant to why we have a hard +time achieving feature and test parity with regular nova (because many things +take an alternate path with cells) and why it's hard to understand what is +going on (all the extra synchronization of data). The new proposed cellsv2 +organization avoids both of these problems by letting things live where they +should, teaching nova to natively find the right db, queue, and compute node to +handle a given request. + + +Database split +~~~~~~~~~~~~~~ + +As mentioned above there is a split between global data and data that is local +to a cell. + +The following is a breakdown of what data can uncontroversially considered +global versus local to a cell. Missing data will be filled in as consensus is +reached on the data that is more difficult to cleanly place. The missing data +is mostly concerned with scheduling and networking. + +Global (API-level) Tables +------------------------- + +instance_types +instance_type_projects +instance_type_extra_specs +quotas +project_user_quotas +quota_classes +quota_usages +security_groups +security_group_rules +security_group_default_rules +provider_fw_rules +key_pairs +migrations +networks +tags + +Cell-level Tables +----------------- + +instances +instance_info_caches +instance_extra +instance_metadata +instance_system_metadata +instance_faults +instance_actions +instance_actions_events +instance_id_mappings +pci_devices +block_device_mapping +virtual_interfaces + +Setup of Cells V2 +================= + +Overview +~~~~~~~~ + +As more of the CellsV2 implementation is finished, all operators are +required to make changes to their deployment. For all deployments +(even those that only intend to have one cell), these changes are +configuration-related, both in the main nova configuration file as +well as some extra records in the databases. + +All nova deployments must now have the following databases available +and configured: + +1. The "API" database +2. One special "cell" database called "cell0" +3. One (or eventually more) "cell" databases + +Thus, a small nova deployment will have an API database, a cell0, and +what we will call here a "cell1" database. High-level tracking +information is kept in the API database. Instances that are never +scheduled are relegated to the cell0 database, which is effectively a +graveyard of instances that failed to start. All successful/running +instances are stored in "cell1". + +First Time Setup +~~~~~~~~~~~~~~~~ + +Since there is only one API database, the connection information for +it is stored in the nova.conf file. +:: + + [api_database] + connection = mysql+pymysql://root:secretmysql@dbserver/nova_api?charset=utf8 + +Since there may be multiple "cell" databases (and in fact everyone +will have cell0 and cell1 at a minimum), connection info for these is +stored in the API database. Thus, you must have connection information +in your config file for the API database before continuing to the +steps below, so that `nova-manage` can find your other databases. + +The following examples show the full expanded command line usage of +the setup commands. This is to make it easier to visualize which of +the various URLs are used by each of the commands. However, you should +be able to put all of that in the config file and `nova-manage` will +use those values. If need be, you can create separate config files and +pass them as `nova-manage --config-file foo.conf` to control the +behavior without specifying things on the command lines. + +The commands below use the API database so remember to run +`nova-manage api_db sync` first. + +First we will create the necessary records for the cell0 database. To +do that we use `nova-manage` like this:: + + nova-manage cell_v2 map_cell0 --database_connection \ + mysql+pymysql://root:secretmysql@dbserver/nova_cell0?charset=utf8 + +.. note:: If you don't specify `--database_connection` then + `nova-manage` will use the `[database]/connection` value + from your config file, and mangle the database name to have + a `_cell0` suffix. +.. warning:: If your databases are on separate hosts then you should specify + `--database_connection` or make certain that the nova.conf + being used has the `[database]/connection` value pointing to the + same user/password/host that will work for the cell0 database. + If the cell0 mapping was created incorrectly, it can be deleted + using the `nova-manage cell_v2 delete_cell` command and then run + `map_cell0` again with the proper database connection value. + +Since no hosts are ever in cell0, nothing further is required for its +setup. Note that all deployments only ever have one cell0, as it is +special, so once you have done this step you never need to do it +again, even if you add more regular cells. + +Now, we must create another cell which will be our first "regular" +cell, which has actual compute hosts in it, and to which instances can +actually be scheduled. First, we create the cell record like this:: + + nova-manage cell_v2 create_cell --verbose --name cell1 \ + --database_connection mysql+pymysql://root:secretmysql@127.0.0.1/nova?charset=utf8 + --transport-url rabbit://stackrabbit:secretrabbit@mqserver:5672/ + +.. note:: If you don't specify the database and transport urls then + `nova-manage` will use the + `[database]/connection` and `[DEFAULT]/transport_url` values + from the config file. + +.. note:: At this point, the API database can now find the cell + database, and further commands will attempt to look + inside. If this is a completely fresh database (such as if + you're adding a cell, or if this is a new deployment), then + you will need to run `nova-manage db sync` on it to + initialize the schema. + +The `nova-manage cell_v2 create_cell` command will print the UUID of the +newly-created cell if `--verbose` is passed, which is useful if you +need to run commands like `discover_hosts` targeted at a specific +cell. + +Now we have a cell, but no hosts are in it which means the scheduler +will never actually place instances there. The next step is to scan +the database for compute node records and add them into the cell we +just created. For this step, you must have had a compute node started +such that it registers itself as a running service. Once that has +happened, you can scan and add it to the cell:: + + nova-manage cell_v2 discover_hosts + +This command will connect to any databases for which you have created +cells (as above), look for hosts that have registered themselves +there, and map those hosts in the API database so that +they are visible to the scheduler as available targets for +instances. Any time you add more compute hosts to a cell, you need to +re-run this command to map them from the top-level so they can be +utilized. + +References +~~~~~~~~~~ + +* :doc:`nova-manage man page ` + +Step-By-Step for Common Use Cases +================================= + +The following are step-by-step examples for common use cases setting +up Cells V2. This is intended as a quick reference that puts together +everything explained in `Setup of Cells V2`_. It is assumed that you have +followed all other install steps for Nova and are setting up Cells V2 +specifically at this point. + +Fresh Install +~~~~~~~~~~~~~ + +You are installing Nova for the first time and have no compute hosts in the +database yet. This will set up a single cell Nova deployment. + +1. Reminder: You should have already created and synced the Nova API database + by creating a database, configuring its connection in the + ``[api_database]/connection`` setting in the Nova configuration file, and + running ``nova-manage api_db sync``. + +2. Create a database for cell0. If you are going to pass the database + connection url on the command line in step 3, you can name the cell0 + database whatever you want. If you are not going to pass the database url on + the command line in step 3, you need to name the cell0 database based on the + name of your existing Nova database: _cell0. For + example, if your Nova database is named ``nova``, then your cell0 database + should be named ``nova_cell0``. + +3. Run the ``map_cell0`` command to create and map cell0:: + + nova-manage cell_v2 map_cell0 \ + --database_connection + + The database connection url is generated based on the + ``[database]/connection`` setting in the Nova configuration file if not + specified on the command line. + +4. Run ``nova-manage db sync`` to populate the cell0 database with a schema. + The ``db sync`` command reads the database connection for cell0 that was + created in step 3. + +5. Run the ``create_cell`` command to create the single cell which will contain + your compute hosts:: + + nova-manage cell_v2 create_cell --name \ + --transport-url \ + --database_connection + + The transport url is taken from the ``[DEFAULT]/transport_url`` setting in + the Nova configuration file if not specified on the command line. The + database url is taken from the ``[database]/connection`` setting in the Nova + configuration file if not specified on the command line. + +6. Configure and start your compute hosts. Before step 7, make sure you have + compute hosts in the database by running + ``nova service-list --binary nova-compute``. + +7. Run the ``discover_hosts`` command to map compute hosts to the single cell:: + + nova-manage cell_v2 discover_hosts + + The command will search for compute hosts in the database of the cell you + created in step 5 and map them to the cell. You can also configure a + periodic task to have Nova discover new hosts automatically by setting + the ``[scheduler]/discover_hosts_in_cells_interval`` to a time interval in + seconds. The periodic task is run by the nova-scheduler service, so you must + be sure to configure it on all of your nova-scheduler hosts. + +.. note:: Remember: In the future, whenever you add new compute hosts, you + will need to run the ``discover_hosts`` command after starting them + to map them to the cell if you did not configure the automatic host + discovery in step 7. + +Upgrade (minimal) +~~~~~~~~~~~~~~~~~ + +You are upgrading an existing Nova install and have compute hosts in the +database. This will set up a single cell Nova deployment. + +1. If you haven't already created a cell0 database in a prior release, + create a database for cell0 with a name based on the name of your Nova + database: _cell0. If your Nova database is named + ``nova``, then your cell0 database should be named ``nova_cell0``. + +.. warning:: In Newton, the ``simple_cell_setup`` command expects the name of + the cell0 database to be based on the name of the Nova API + database: _cell0 and the database + connection url is taken from the ``[api_database]/connection`` + setting in the Nova configuration file. + +2. Run the ``simple_cell_setup`` command to create and map cell0, create and + map the single cell, and map existing compute hosts and instances to the + single cell:: + + nova-manage cell_v2 simple_cell_setup \ + --transport-url + + The transport url is taken from the ``[DEFAULT]/transport_url`` setting in + the Nova configuration file if not specified on the command line. The + database connection url will be generated based on the + ``[database]/connection`` setting in the Nova configuration file. + +.. note:: Remember: In the future, whenever you add new compute hosts, you + will need to run the ``discover_hosts`` command after starting them + to map them to the cell. You can also configure a periodic task to + have Nova discover new hosts automatically by setting the + ``[scheduler]/discover_hosts_in_cells_interval`` to a time interval + in seconds. The periodic task is run by the nova-scheduler service, + so you must be sure to configure it on all of your nova-scheduler + hosts. + +Upgrade with Cells V1 +~~~~~~~~~~~~~~~~~~~~~ + +You are upgrading an existing Nova install that has Cells V1 enabled and have +compute hosts in your databases. This will set up a multiple cell Nova +deployment. At this time, it is recommended to keep Cells V1 enabled during and +after the upgrade as multiple Cells V2 cell support is not fully finished and +may not work properly in all scenarios. These upgrade steps will help ensure a +simple cutover from Cells V1 to Cells V2 in the future. + +1. If you haven't already created a cell0 database in a prior release, + create a database for cell0. If you are going to pass the database + connection url on the command line in step 2, you can name the cell0 + database whatever you want. If you are not going to pass the database url on + the command line in step 2, you need to name the cell0 database based on the + name of your existing Nova database: _cell0. For + example, if your Nova database is named ``nova``, then your cell0 database + should be named ``nova_cell0``. + +2. Run the ``map_cell0`` command to create and map cell0:: + + nova-manage cell_v2 map_cell0 \ + --database_connection + + The database connection url is generated based on the + ``[database]/connection`` setting in the Nova configuration file if not + specified on the command line. + +3. Run ``nova-manage db sync`` to populate the cell0 database with a schema. + The ``db sync`` command reads the database connection for cell0 that was + created in step 2. + +4. Run the ``create_cell`` command to create cells which will contain your + compute hosts:: + + nova-manage cell_v2 create_cell --name \ + --transport-url \ + --database_connection + + You will need to repeat this step for each cell in your deployment. Your + existing cell database will be re-used -- this simply informs the top-level + API database about your existing cell databases. + + It is a good idea to specify a name for the new cell you create so you can + easily look up cell uuids with the ``list_cells`` command later if needed. + + The transport url is taken from the ``[DEFAULT]/transport_url`` setting in + the Nova configuration file if not specified on the command line. The + database url is taken from the ``[database]/connection`` setting in the Nova + configuration file if not specified on the command line. If you are not + going to specify ``--database_connection`` and ``--transport-url`` on the + command line, be sure to specify your existing cell Nova configuration + file:: + + nova-manage --config-file cell_v2 create_cell \ + --name + +5. Run the ``discover_hosts`` command to map compute hosts to cells:: + + nova-manage cell_v2 discover_hosts --cell_uuid + + You will need to repeat this step for each cell in your deployment unless + you omit the ``--cell_uuid`` option. If the cell uuid is not specified on + the command line, ``discover_hosts`` will search for compute hosts in each + cell database and map them to the corresponding cell. You can use the + ``list_cells`` command to look up cell uuids if you are going to specify + ``--cell_uuid``. + + You can also configure a periodic task to have Nova discover new hosts + automatically by setting the + ``[scheduler]/discover_hosts_in_cells_interval`` to a time interval in + seconds. The periodic task is run by the nova-scheduler service, so you must + be sure to configure it on all of your nova-scheduler hosts. + +6. Run the ``map_instances`` command to map instances to cells:: + + nova-manage cell_v2 map_instances --cell_uuid \ + --max-count + + You will need to repeat this step for each cell in your deployment. You can + use the ``list_cells`` command to look up cell uuids. + + The ``--max-count`` option can be specified if you would like to limit the + number of instances to map in a single run. If ``--max-count`` is not + specified, all instances will be mapped. Repeated runs of the command will + start from where the last run finished so it is not necessary to increase + ``--max-count`` to finish. An exit code of 0 indicates that all instances + have been mapped. An exit code of 1 indicates that there are remaining + instances that need to be mapped. + +.. note:: Remember: In the future, whenever you add new compute hosts, you + will need to run the ``discover_hosts`` command after starting them + to map them to a cell if you did not configure the automatic host + discovery in step 5. + +Adding a new cell to an existing deployment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To expand your deployment with a new cell, first follow the usual steps for +standing up a new Cells V1 cell. After that is finished, follow step 4 in +`Upgrade with Cells V1`_ to create a new Cells V2 cell for it. If you have +added new compute hosts for the new cell, you will also need to run the +``discover_hosts`` command after starting them to map them to the new cell if +you did not configure the automatic host discovery as described in step 5 in +`Upgrade with Cells V1`_. + +References +~~~~~~~~~~ + +* :doc:`nova-manage man page ` + +FAQs +==== + +#. How do I find out which hosts are bound to which cell? + + There are a couple of ways to do this. + + 1. Run ``nova-manage --config-file host list``. This will + only lists hosts in the provided cell nova.conf. Note, however, that + this command is deprecated as of the 16.0.0 Pike release. + + 2. Run ``nova-manage cell_v2 discover_hosts --verbose``. This does not + produce a report but if you are trying to determine if a host is in a + cell you can run this and it will report any hosts that are not yet + mapped to a cell and map them. This command is idempotent. + + In the future, we may add a flag to the ``nova-manage cell_v2 list_cells`` + command or add another command to list hosts in a specific cell (or all + cells). + +#. I updated the database_connection and/or transport_url in a cell using the + ``nova-manage cell_v2 update_cell`` command but the API is still trying to + use the old settings. + + The cell mappings are cached in the nova-api service worker so you will need + to restart the worker process to rebuild the cache. Note that there is + another global cache tied to request contexts, which is used in the + nova-conductor and nova-scheduler services, so you might need to do the same + if you are having the same issue in those services. As of the 16.0.0 Pike + release there is no timer on the cache or hook to refresh the cache using a + SIGHUP to the service. + +#. I have upgraded from Newton to Ocata and I can list instances but I get a + 404 NotFound error when I try to get details on a specific instance. + + Instances need to be mapped to cells so the API knows which cell an instance + lives in. When upgrading, the ``nova-manage cell_v2 simple_cell_setup`` + command will automatically map the instances to the single cell which is + backed by the existing nova database. If you have already upgraded + and did not use the ``simple_cell_setup`` command, you can run the + ``nova-manage cell_v2 map_instances --cell_uuid `` command to + map all instances in the given cell. See the :ref:`man-page-cells-v2` man + page for details on command usage. + +#. Should I change any of the ``[cells]`` configuration options for Cells v2? + + **NO**. Those options are for Cells v1 usage only and are not used at all + for Cells v2. That includes the ``nova-cells`` service - it has nothing + to do with Cells v2. diff --git a/doc/source/user/cellsv2_layout.rst b/doc/source/user/cellsv2_layout.rst new file mode 100644 index 00000000000..da8d287f0a3 --- /dev/null +++ b/doc/source/user/cellsv2_layout.rst @@ -0,0 +1,338 @@ +.. + 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. + +=================== + Cells Layout (v2) +=================== + +This document describes the layout of a deployment with Cells +version 2, including deployment considerations for security and +scale. It is focused on code present in Pike and later, and while it +is geared towards people who want to have multiple cells for whatever +reason, the nature of the cellsv2 support in Nova means that it +applies in some way to all deployments. + +.. note:: The concepts laid out in this document do not in any way + relate to CellsV1, which includes the ``nova-cells`` + service, and the ``[cells]`` section of the configuration + file. For more information on the differences, see the main + :ref:`cells` page. + +Concepts +======== + +A basic Nova system consists of the following components: + +* The nova-api service which provides the external REST API to users. +* The nova-scheduler and placement services which are responsible + for tracking resources and deciding which compute node instances + should be on. +* An "API database" that is used primarily by nova-api and + nova-scheduler (called *API-level services* below) to track location + information about instances, as well as a temporary location for + instances being built but not yet scheduled. +* The nova-conductor service which offloads long-running tasks for the + API-level service, as well as insulates compute nodes from direct + database access +* The nova-compute service which manages the virt driver and + hypervisor host. +* A "cell database" which is used by API, conductor and compute + services, and which houses the majority of the information about + instances. +* A "cell0 database" which is just like the cell database, but + contains only instances that failed to be scheduled. +* A message queue which allows the services to communicate with each + other via RPC. + +All deployments have at least the above components. Small deployments +likely have a single message queue that all services share, and a +single database server which hosts the API database, a single cell +database, as well as the required cell0 database. This is considered a +"single-cell deployment" because it only has one "real" cell. The +cell0 database mimics a regular cell, but has no compute nodes and is +used only as a place to put instances that fail to land on a real +compute node (and thus a real cell). + +The purpose of the cells functionality in nova is specifically to +allow larger deployments to shard their many compute nodes into cells, +each of which has a database and message queue. The API database is +always and only global, but there can be many cell databases (where +the bulk of the instance information lives), each with a portion of +the instances for the entire deployment within. + +All of the nova services use a configuration file, all of which will +at a minimum specify a message queue endpoint +(i.e. ``[DEFAULT]/transport_url``). Most of the services also require +configuration of database connection information +(i.e. ``[database]/connection``). API-level services that need access +to the global routing and placement information will also be +configured to reach the API database +(i.e. ``[api_database]/connection``). + +.. note:: The pair of ``transport_url`` and ``[database]/connection`` + configured for a service defines what cell a service lives + in. + +API-level services need to be able to contact other services in all of +the cells. Since they only have one configured ``transport_url`` and +``[database]/connection`` they look up the information for the other +cells in the API database, with records called *cell mappings*. + +.. note:: The API database must have cell mapping records that match + the ``transport_url`` and ``[database]/connection`` + configuration elements of the lower-level services. See the + ``nova-manage`` :ref:`man-page-cells-v2` commands for more + information about how to create and examine these records. + +Service Layout +============== + +The services generally have a well-defined communication pattern that +dictates their layout in a deployment. In a small/simple scenario, the +rules do not have much of an impact as all the services can +communicate with each other on a single message bus and in a single +cell database. However, as the deployment grows, scaling and security +concerns may drive separation and isolation of the services. + +Simple +------ + +This is a diagram of the basic services that a simple (single-cell) +deployment would have, as well as the relationships +(i.e. communication paths) between them: + +.. graphviz:: + + digraph services { + graph [pad="0.35", ranksep="0.65", nodesep="0.55", concentrate=true]; + node [fontsize=10 fontname="Monospace"]; + edge [arrowhead="normal", arrowsize="0.8"]; + labelloc=bottom; + labeljust=left; + + { rank=same + api [label="nova-api"] + apidb [label="API Database" shape="box"] + scheduler [label="nova-scheduler"] + } + { rank=same + mq [label="MQ" shape="diamond"] + conductor [label="nova-conductor"] + } + { rank=same + cell0db [label="Cell0 Database" shape="box"] + celldb [label="Cell Database" shape="box"] + compute [label="nova-compute"] + } + + api -> mq -> compute + conductor -> mq -> scheduler + + api -> apidb + api -> cell0db + api -> celldb + + conductor -> apidb + conductor -> cell0db + conductor -> celldb + } + +All of the services are configured to talk to each other over the same +message bus, and there is only one cell database where live instance +data resides. The cell0 database is present (and required) but as no +compute nodes are connected to it, this is still a "single cell" +deployment. + +Multiple Cells +-------------- + +In order to shard the services into multiple cells, a number of things +must happen. First, the message bus must be split into pieces along +the same lines as the cell database. Second, a dedicated conductor +must be run for the API-level services, with access to the API +database and a dedicated message queue. We call this *super conductor* +to distinguish its place and purpose from the per-cell conductor nodes. + +.. graphviz:: + + digraph services2 { + graph [pad="0.35", ranksep="0.65", nodesep="0.55", concentrate=true]; + node [fontsize=10 fontname="Monospace"]; + edge [arrowhead="normal", arrowsize="0.8"]; + labelloc=bottom; + labeljust=left; + + subgraph api { + api [label="nova-api"] + scheduler [label="nova-scheduler"] + conductor [label="super conductor"] + { rank=same + apimq [label="API MQ" shape="diamond"] + apidb [label="API Database" shape="box"] + } + + api -> apimq -> conductor + api -> apidb + conductor -> apimq -> scheduler + conductor -> apidb + } + + subgraph clustercell0 { + label="Cell 0" + color=green + cell0db [label="Cell Database" shape="box"] + } + + subgraph clustercell1 { + label="Cell 1" + color=blue + mq1 [label="Cell MQ" shape="diamond"] + cell1db [label="Cell Database" shape="box"] + conductor1 [label="nova-conductor"] + compute1 [label="nova-compute"] + + conductor1 -> mq1 -> compute1 + conductor1 -> cell1db + + } + + subgraph clustercell2 { + label="Cell 2" + color=red + mq2 [label="Cell MQ" shape="diamond"] + cell2db [label="Cell Database" shape="box"] + conductor2 [label="nova-conductor"] + compute2 [label="nova-compute"] + + conductor2 -> mq2 -> compute2 + conductor2 -> cell2db + } + + api -> mq1 -> conductor1 + api -> mq2 -> conductor2 + api -> cell0db + api -> cell1db + api -> cell2db + + conductor -> cell0db + conductor -> cell1db + conductor -> mq1 + conductor -> cell2db + conductor -> mq2 + } + +It is important to note that services in the lower cell boxes do not +have the ability to call back to the API-layer services via RPC, nor +do they have access to the API database for global visibility of +resources across the cloud. This is intentional and provides security +and failure domain isolation benefits, but also has impacts on some +things that would otherwise require this any-to-any communication +style. Check the release notes for the version of Nova you are using +for the most up-to-date information about any caveats that may be +present due to this limitation. + +Caveats of a Multi-Cell deployment +---------------------------------- + +.. note: This information is correct as of the Pike release. + +Cross-cell instance migrations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently it is not possible to migrate an instance from a host in one +cell to a host in another cell. This may be possible in the future, +but it is currently unsupported. This impacts cold migration, +resizes, live migrations, evacuate, and unshelve operations. + +Quota-related quirks +~~~~~~~~~~~~~~~~~~~~ + +Quotas are now calculated live at the point at which an operation +would consume more resource, instead of being kept statically in the +database. This means that a multi-cell environment may incorrectly +calculate the usage of a tenant if one of the cells is unreachable, as +those resources cannot be counted. In this case, the tenant may be +able to consume more resource from one of the available cells, putting +them far over quota when the unreachable cell returns. In the future, +placement will provide us with a consistent way to calculate usage +independent of the actual cell being reachable. + +Performance of listing instances +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +With multiple cells, the instance list operation may not sort and +paginate results properly when crossing multiple cell +boundaries. Further, the performance of a sorted list operation will +be considerably slower than with a single cell. + +Notifications +~~~~~~~~~~~~~ + +With a multi-cell environment with multiple message queues, it is +likely that operators will want to configure a separate connection to +a unified queue for notifications. This can be done in the +configuration file of all nodes. See the `oslo.messaging configuration +`_ +documentation for more details. + +Neutron Metadata API proxy +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Neutron metadata API proxy should be global across all cells, and +thus be configured as an API-level service with access to the +``[api_database]/connection`` information. + +Consoleauth service and console proxies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The consoleauth service should be global across all cells and thus be +configured as an API-level service with access to the +``[api_database]/connection`` information. The various console proxies +should also be global across all cells but they don't need access to the +API database. + +Future work will deprecate the consoleauth service, store token +authorizations in the cell databases, and require console proxies running +per cell instead of globally. + +Operations Requiring upcalls +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you deploy multiple cells with a superconductor as described above, +computes and cell-based conductors will not have the ability to speak +to the scheduler as they are not connected to the same MQ. This is by +design for isolation, but currently the processes are not in place to +implement some features without such connectivity. Thus, anything that +requires a so-called "upcall" will not function. This impacts the +following: + +- Instance reschedules during boot +- Instance affinity reporting from the compute nodes to scheduler +- The late anti-affinity check + +The first is simple: if you boot an instance, it gets scheduled to a +compute node, fails, it would normally be re-scheduled to another +node. That requires scheduler intervention and thus it will not work +in Pike with a multi-cell layout. If you do not rely on reschedules +for covering up transient compute-node failures, then this will not +affect you. To ensure you do not make futile attempts at rescheduling, +you should set ``[scheduler]/max_attempts=1`` in ``nova.conf``. + +The second two are related. The summary is that some of the facilities +that Nova has for ensuring that affinty/anti-affinity is preserved +between instances does not function in Pike with a multi-cell +layout. If you don't use affinity operations, then this will not +affect you. To make sure you don't make futile attempts at the +affinity check, you should set +``[workarounds]/disable_group_policy_check_upcall=True`` and +``[filter_scheduler]/track_instance_changes=False`` in ``nova.conf``. diff --git a/doc/source/conductor.rst b/doc/source/user/conductor.rst similarity index 100% rename from doc/source/conductor.rst rename to doc/source/user/conductor.rst diff --git a/doc/source/user/config-drive.rst b/doc/source/user/config-drive.rst new file mode 100644 index 00000000000..c43b771e8ed --- /dev/null +++ b/doc/source/user/config-drive.rst @@ -0,0 +1,296 @@ +======================================= +Store metadata on a configuration drive +======================================= +You can configure OpenStack to write metadata to a special configuration drive +that attaches to the instance when it boots. The instance can mount this drive +and read files from it to get information that is normally available through +the :ref:`metadata service `. +This metadata is different from the user data. + +One use case for using the configuration drive is to pass a networking +configuration when you do not use DHCP to assign IP addresses to +instances. For example, you might pass the IP address configuration for +the instance through the configuration drive, which the instance can +mount and access before you configure the network settings for the +instance. + +Any modern guest operating system that is capable of mounting an ISO +9660 or VFAT file system can use the configuration drive. + +Requirements and guidelines +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the configuration drive, you must follow the following +requirements for the compute host and image. + +**Compute host requirements** + +- The following hypervisors support the configuration drive: libvirt, + XenServer, Hyper-V, and VMware. + + Also, the Bare Metal service supports the configuration drive. + +- To use configuration drive with libvirt, XenServer, or VMware, you + must first install the genisoimage package on each compute host. + Otherwise, instances do not boot properly. + + Use the ``mkisofs_cmd`` flag to set the path where you install the + genisoimage program. If genisoimage is in same path as the + ``nova-compute`` service, you do not need to set this flag. + +- To use configuration drive with Hyper-V, you must set the + ``mkisofs_cmd`` value to the full path to an ``mkisofs.exe`` + installation. Additionally, you must set the ``qemu_img_cmd`` value + in the ``hyperv`` configuration section to the full path to an + :command:`qemu-img` command installation. + +- To use configuration drive with the Bare Metal service, + you do not need to prepare anything because the Bare Metal + service treats the configuration drive properly. + +**Image requirements** + +- An image built with a recent version of the cloud-init package can + automatically access metadata passed through the configuration drive. + The cloud-init package version 0.7.1 works with Ubuntu, Fedora + based images (such as Red Hat Enterprise Linux) and openSUSE based + images (such as SUSE Linux Enterprise Server). + +- If an image does not have the cloud-init package installed, you must + customize the image to run a script that mounts the configuration + drive on boot, reads the data from the drive, and takes appropriate + action such as adding the public key to an account. You can read more + details about how data is organized on the configuration drive. + +- If you use Xen with a configuration drive, use the + ``xenapi_disable_agent`` configuration parameter to disable the + agent. + +**Guidelines** + +- Do not rely on the presence of the EC2 metadata in the configuration + drive, because this content might be removed in a future release. For + example, do not rely on files in the ``ec2`` directory. + +- When you create images that access configuration drive data and + multiple directories are under the ``openstack`` directory, always + select the highest API version by date that your consumer supports. + For example, if your guest image supports the 2012-03-05, 2012-08-05, + and 2013-04-13 versions, try 2013-04-13 first and fall back to a + previous version if 2013-04-13 is not present. + +Enable and access the configuration drive +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. To enable the configuration drive, pass the ``--config-drive true`` + parameter to the :command:`openstack server create` command. + + The following example enables the configuration drive and passes user + data, two files, and two key/value metadata pairs, all of which are + accessible from the configuration drive: + + .. code-block:: console + + $ openstack server create --config-drive true --image my-image-name \ + --flavor 1 --key-name mykey --user-data ./my-user-data.txt \ + --file /etc/network/interfaces=/home/myuser/instance-interfaces \ + --file known_hosts=/home/myuser/.ssh/known_hosts \ + --property role=webservers --property essential=false MYINSTANCE + + You can also configure the Compute service to always create a + configuration drive by setting the following option in the + ``/etc/nova/nova.conf`` file: + + .. code-block:: console + + force_config_drive = true + + .. note:: + + If a user passes the ``--config-drive true`` flag to the :command:`nova + boot` command, an administrator cannot disable the configuration + drive. + +#. If your guest operating system supports accessing disk by label, you + can mount the configuration drive as the + ``/dev/disk/by-label/configurationDriveVolumeLabel`` device. In the + following example, the configuration drive has the ``config-2`` + volume label: + + .. code-block:: console + + # mkdir -p /mnt/config + # mount /dev/disk/by-label/config-2 /mnt/config + +.. note:: + + Ensure that you use at least version 0.3.1 of CirrOS for + configuration drive support. + + If your guest operating system does not use ``udev``, the + ``/dev/disk/by-label`` directory is not present. + + You can use the :command:`blkid` command to identify the block device that + corresponds to the configuration drive. For example, when you boot + the CirrOS image with the ``m1.tiny`` flavor, the device is + ``/dev/vdb``: + + .. code-block:: console + + # blkid -t LABEL="config-2" -odevice + + .. code-block:: console + + /dev/vdb + + Once identified, you can mount the device: + + .. code-block:: console + + # mkdir -p /mnt/config + # mount /dev/vdb /mnt/config + +Configuration drive contents +---------------------------- + +In this example, the contents of the configuration drive are as follows:: + + ec2/2009-04-04/meta-data.json + ec2/2009-04-04/user-data + ec2/latest/meta-data.json + ec2/latest/user-data + openstack/2012-08-10/meta_data.json + openstack/2012-08-10/user_data + openstack/content + openstack/content/0000 + openstack/content/0001 + openstack/latest/meta_data.json + openstack/latest/user_data + +The files that appear on the configuration drive depend on the arguments +that you pass to the :command:`openstack server create` command. + +OpenStack metadata format +------------------------- + +The following example shows the contents of the +``openstack/2012-08-10/meta_data.json`` and +``openstack/latest/meta_data.json`` files. These files are identical. +The file contents are formatted for readability. + +.. code-block:: json + + { + "availability_zone": "nova", + "files": [ + { + "content_path": "/content/0000", + "path": "/etc/network/interfaces" + }, + { + "content_path": "/content/0001", + "path": "known_hosts" + } + ], + "hostname": "test.novalocal", + "launch_index": 0, + "name": "test", + "meta": { + "role": "webservers", + "essential": "false" + }, + "public_keys": { + "mykey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDBqUfVvCSez0/Wfpd8dLLgZXV9GtXQ7hnMN+Z0OWQUyebVEHey1CXuin0uY1cAJMhUq8j98SiW+cU0sU4J3x5l2+xi1bodDm1BtFWVeLIOQINpfV1n8fKjHB+ynPpe1F6tMDvrFGUlJs44t30BrujMXBe8Rq44cCk6wqyjATA3rQ== Generated by Nova\n" + }, + "uuid": "83679162-1378-4288-a2d4-70e13ec132aa" + } + +Note the effect of the +``--file /etc/network/interfaces=/home/myuser/instance-interfaces`` +argument that was passed to the :command:`openstack server create` command. +The contents of this file are contained in the ``openstack/content/0000`` +file on the configuration drive, and the path is specified as +``/etc/network/interfaces`` in the ``meta_data.json`` file. + +EC2 metadata format +------------------- + +The following example shows the contents of the +``ec2/2009-04-04/meta-data.json`` and the ``ec2/latest/meta-data.json`` +files. These files are identical. The file contents are formatted to +improve readability. + +.. code-block:: json + + { + "ami-id": "ami-00000001", + "ami-launch-index": 0, + "ami-manifest-path": "FIXME", + "block-device-mapping": { + "ami": "sda1", + "ephemeral0": "sda2", + "root": "/dev/sda1", + "swap": "sda3" + }, + "hostname": "test.novalocal", + "instance-action": "none", + "instance-id": "i-00000001", + "instance-type": "m1.tiny", + "kernel-id": "aki-00000002", + "local-hostname": "test.novalocal", + "local-ipv4": null, + "placement": { + "availability-zone": "nova" + }, + "public-hostname": "test.novalocal", + "public-ipv4": "", + "public-keys": { + "0": { + "openssh-key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDBqUfVvCSez0/Wfpd8dLLgZXV9GtXQ7hnMN+Z0OWQUyebVEHey1CXuin0uY1cAJMhUq8j98SiW+cU0sU4J3x5l2+xi1bodDm1BtFWVeLIOQINpfV1n8fKjHB+ynPpe1F6tMDvrFGUlJs44t30BrujMXBe8Rq44cCk6wqyjATA3rQ== Generated by Nova\n" + } + }, + "ramdisk-id": "ari-00000003", + "reservation-id": "r-7lfps8wj", + "security-groups": [ + "default" + ] + } + +User data +--------- + +The ``openstack/2012-08-10/user_data``, ``openstack/latest/user_data``, +``ec2/2009-04-04/user-data``, and ``ec2/latest/user-data`` file are +present only if the ``--user-data`` flag and the contents of the user +data file are passed to the :command:`openstack server create` command. + +Configuration drive format +-------------------------- + +The default format of the configuration drive as an ISO 9660 file +system. To explicitly specify the ISO 9660 format, add the following +line to the ``/etc/nova/nova.conf`` file: + +.. code-block:: console + + config_drive_format=iso9660 + +By default, you cannot attach the configuration drive image as a CD +drive instead of as a disk drive. To attach a CD drive, add the +following line to the ``/etc/nova/nova.conf`` file: + +.. code-block:: console + + config_drive_cdrom=true + +For legacy reasons, you can configure the configuration drive to use +VFAT format instead of ISO 9660. It is unlikely that you would require +VFAT format because ISO 9660 is widely supported across operating +systems. However, to use the VFAT format, add the following line to the +``/etc/nova/nova.conf`` file: + +.. code-block:: console + + config_drive_format=vfat + +If you choose VFAT, the configuration drive is 64 MB. diff --git a/doc/source/user/feature-classification.rst b/doc/source/user/feature-classification.rst new file mode 100644 index 00000000000..ceeafe572f5 --- /dev/null +++ b/doc/source/user/feature-classification.rst @@ -0,0 +1,206 @@ +.. + 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. + +====================== +Feature Classification +====================== + +This document presents a matrix that describes which features are ready to be +used and which features are works in progress. It includes links to relevant +documentation and functional tests. + +.. warning:: Please note: this is a work in progress! + +Aims +==== + +Users want reliable, long-term solutions for their use cases. +The feature classification matrix identifies which features are +complete and ready to use, and which should be used with caution. + +The matrix also benefits developers by providing a list of features that +require further work to be considered complete. + +Below is a matrix for a selection of important verticals: + +* :ref:`matrix-gp` +* :ref:`matrix-nfv` +* :ref:`matrix-hpc` + +For more details on the concepts in each matrix, +please see :ref:`notes-on-concepts`. + +.. _matrix-gp: + +General Purpose Cloud Features +=============================== + +This is a summary of the key features dev/test clouds, and other similar +general purpose clouds need, and it describes their current state. + +Below there are sections on NFV and HPC specific features. These look at +specific features and scenarios that are important to those more specific +sets of use cases. + +.. feature_matrix:: feature-matrix-gp.ini + +.. _matrix-nfv: + +NFV Cloud Features +================== + +Network Function Virtualization (NFV) is about virtualizing network node +functions into building blocks that may connect, or chain together to +create a particular service. It is common for this workloads needing +bare metal like performance, i.e. low latency and close to line speed +performance. + +.. feature_matrix:: feature-matrix-nfv.ini + +.. _matrix-hpc: + +HPC Cloud Features +================== + +High Performance Compute (HPC) cloud have some specific needs that are covered +in this set of features. + +.. feature_matrix:: feature-matrix-hpc.ini + +.. _notes-on-concepts: + +Notes on Concepts +================= + +This document uses the following terminology. + +Users +----- + +These are the users we talk about in this document: + +application deployer + creates and deletes servers, directly or indirectly using an API + +application developer + creates images and apps that run on the cloud + +cloud operator + administers the cloud + +self service administrator + runs and uses the cloud + +.. note:: + + This is not an exhaustive list of personas, but rather an indicative set of + users. + +Feature Group +------------- + +To reduce the size of the matrix, we organize the features into groups. +Each group maps to a set of user stories that can be validated by a set +of scenarios and tests. Typically, this means a set of tempest tests. + +This list focuses on API concepts like attach and detach volumes, rather than +deployment specific concepts like attach an iSCSI volume to a KVM based VM. + +Deployment +---------- + +A deployment maps to a specific test environment. We provide a full description +of the environment, so it is possible to reproduce the reported test results +for each of the Feature Groups. + +This description includes all aspects of the deployment, for example +the hypervisor, number of nova-compute services, storage, network driver, +and types of images being tested. + +Feature Group Maturity +----------------------- + +The Feature Group Maturity rating is specific to the API concepts, rather than +specific to a particular deployment. That detail is covered in the deployment +rating for each feature group. + +.. note:: + + Although having some similarities, this list is not directly related + to the DefCore effort. + +**Feature Group ratings:** + +Incomplete + Incomplete features are those that do not have enough functionality to + satisfy real world use cases. + +Experimental + Experimental features should be used with extreme caution. They are likely + to have little or no upstream testing, and are therefore likely to + contain bugs. + +Complete + For a feature to be considered complete, it must have: + + * complete API docs (concept and REST call definition) + * complete Administrator docs + * tempest tests that define if the feature works correctly + * sufficient functionality and reliability to be useful in real world + scenarios + * a reasonable expectation that the feature will be supported long-term + +Complete and Required + There are various reasons why a complete feature may be required, but + generally it is when all drivers support that feature. New + drivers need to prove they support all required features before they are + allowed in upstream Nova. + + Required features are those that any new technology must support before + being allowed into tree. The larger the list, the more features are + available on all Nova based clouds. + +Deprecated + Deprecated features are those that are scheduled to be removed in a future + major release of Nova. If a feature is marked as complete, it should + never be deprecated. + + If a feature is incomplete or experimental for several releases, + it runs the risk of being deprecated and later removed from the code base. + +Deployment Rating for a Feature Group +-------------------------------------- + +The deployment rating refers to the state of the tests for each +Feature Group on a particular deployment. + +**Deployment ratings:** + +Unknown + No data is available. + +Not Implemented + No tests exist. + +Implemented + Self declared that the tempest tests pass. + +Regularly Tested + Tested by third party CI. + +Checked + Tested as part of the check or gate queue. + +The eventual goal is to automate this list from a third party CI reporting +system, but currently we document manual inspections in an ini file. +Ideally, we will review the list at every milestone. diff --git a/doc/source/user/feature-matrix-gp.ini b/doc/source/user/feature-matrix-gp.ini new file mode 100644 index 00000000000..6d70f748947 --- /dev/null +++ b/doc/source/user/feature-matrix-gp.ini @@ -0,0 +1,359 @@ +# +# Lists all the CI jobs as targets +# + +[target.libvirt-kvm] +title=libvirt+kvm (x86 & ppc64) +link=http://docs.openstack.org/infra/manual/developers.html#project-gating + +[target.libvirt-kvm-s390] +title=libvirt+kvm (s390x) +link=http://docs.openstack.org/infra/manual/developers.html#project-gating + +[target.libvirt-virtuozzo-ct] +title=libvirt+virtuozzo CT +link=https://wiki.openstack.org/wiki/ThirdPartySystems/Virtuozzo_CI + +[target.libvirt-virtuozzo-vm] +title=libvirt+virtuozzo VM +link=https://wiki.openstack.org/wiki/ThirdPartySystems/Virtuozzo_Storage_CI + +[target.libvirt-xen] +title=libvirt+xen +link=https://wiki.openstack.org/wiki/ThirdPartySystems/XenProject_CI + +[target.xenserver] +title=XenServer CI +link=https://wiki.openstack.org/wiki/XenServer/XenServer_CI + +[target.vmware] +title=VMware CI +link=https://wiki.openstack.org/wiki/NovaVMware/Minesweeper + +[target.hyperv] +title=Hyper-V CI +link=https://wiki.openstack.org/wiki/ThirdPartySystems/Hyper-V_CI + +[target.ironic] +title=Ironic CI +link= + +# +# Lists all features +# +# Includes information on the feature, its maturity, status, +# links to admin docs, api docs and tempest test uuids. +# +# It then lists the current state for each of the about CI jobs. +# It is hoped this mapping will eventually be automated. +# +# This doesn't include things like Server metadata, Server tagging, +# or Lock Server, or Keypair CRUD as they can all be tested independently +# of the nova virt driver used. +# + +[operation.create-delete-server] +title=Create Server and Delete Server +notes=This includes creating a server, and deleting a server. + Specifically this is about booting a server from a glance image + using the default disk and network configuration. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/#servers-servers +admin_doc_link=http://docs.openstack.org/admin-guide/compute-images-instances.html +tempest_test_uuids=9a438d88-10c6-4bcd-8b5b-5b6e25e1346f;585e934c-448e-43c4-acbf-d06a9b899997 +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=partial +driver-notes-libvirt-virtuozzo-ct=This is not tested in a CI system, but it is implemented. +libvirt-virtuozzo-vm=partial +driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is implemented. +libvirt-xen=complete +xenserver=complete +vmware=complete +hyperv=complete +ironic=unknown + +[operation.snapshot-server] +title=Snapshot Server +notes=This is creating a glance image from the currently running server. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/?expanded=#servers-run-an-action-servers-action +admin_doc_link=http://docs.openstack.org/admin-guide/compute-images-instances.html +tempest_test_uuids=aaacd1d0-55a2-4ce8-818a-b5439df8adc9 +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=partial +driver-notes-libvirt-virtuozzo-ct=This is not tested in a CI system, but it is implemented. +libvirt-virtuozzo-vm=partial +driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is implemented. +libvirt-xen=complete +xenserver=complete +vmware=unknown +hyperv=unknown +ironic=unknown + +[operation.power-ops] +title=Server power ops +notes=This includes reboot, shutdown and start. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/?expanded=#servers-run-an-action-servers-action +tempest_test_uuids=2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32;af8eafd4-38a7-4a4b-bdbc-75145a580560 +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=partial +driver-notes-libvirt-virtuozzo-ct=This is not tested in a CI system, but it is implemented. +libvirt-virtuozzo-vm=partial +driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is implemented. +libvirt-xen=complete +xenserver=complete +vmware=complete +hyperv=complete +ironic=unknown + +[operation.rebuild-server] +title=Rebuild Server +notes=You can rebuild a server, optionally specifying the glance image to use. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/?expanded=#servers-run-an-action-servers-action +tempest_test_uuids=aaa6cdf3-55a7-461a-add9-1c8596b9a07c +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=partial +driver-notes-libvirt-virtuozzo-ct=This is not tested in a CI system, but it is implemented. +libvirt-virtuozzo-vm=partial +driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is implemented. +libvirt-xen=complete +xenserver=complete +vmware=complete +hyperv=complete +ironic=unknown + +[operation.resize-server] +title=Resize Server +notes=You resize a server to a new flavor, then confirm or revert that + operation. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/?expanded=#servers-run-an-action-servers-action +tempest_test_uuids=1499262a-9328-4eda-9068-db1ac57498d2 +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=complete +libvirt-virtuozzo-vm=partial +driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is implemented. +libvirt-xen=complete +xenserver=complete +vmware=complete +hyperv=complete +ironic=unknown + +[operation.server-volume-ops] +title=Volume Operations +notes=This is about attaching volumes, detaching volumes. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/#servers-with-volume-attachments-servers-os-volume-attachments +admin_doc_link=http://docs.openstack.org/admin-guide/blockstorage-manage-volumes.html +tempest_test_uuids=fff42874-7db5-4487-a8e1-ddda5fb5288d +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=complete +libvirt-virtuozzo-vm=complete +libvirt-xen=complete +xenserver=complete +vmware=complete +hyperv=complete +ironic=missing + +[operation.server-bdm] +title=Custom disk configurations on boot +notes=This is about supporting all the features of BDMv2. + This includes booting from a volume, in various ways, and + specifying a custom set of ephemeral disks. Note some drivers + only supports part of what the API allows. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/?expanded=create-image-createimage-action-detail#create-server +admin_doc_link=http://docs.openstack.org/admin-guide/compute-manage-volumes.html +tempest_test_uuids=557cd2c2-4eb8-4dce-98be-f86765ff311b, 36c34c67-7b54-4b59-b188-02a2f458a63b +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=missing +libvirt-virtuozzo-vm=complete +libvirt-xen=complete +xenserver=partial +driver-notes-xenserver=This is not tested in a CI system, and only partially implemented. +vmware=partial +driver-notes-vmware=This is not tested in a CI system, but it is implemented. +hyperv=complete:n +ironic=missing + +[operation.server-neutron] +title=Custom neutron configurations on boot +notes=This is about supporting booting from one or more neutron ports, + or all the related short cuts such as booting a specified network. + This does not include SR-IOV or similar, just simple neutron ports. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/?&expanded=create-server-detail +admin_doc_link=http://docs.openstack.org/admin-guide/compute-manage-volumes.html +tempest_test_uuids=2f3a0127-95c7-4977-92d2-bc5aec602fb4 +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=unknown +libvirt-virtuozzo-vm=unknown +libvirt-xen=partial +driver-notes-libvirt-xen=This is not tested in a CI system, but it is implemented. +xenserver=partial +driver-notes-xenserver=This is not tested in a CI system, but it is implemented. +vmware=partial +driver-notes-vmware=This is not tested in a CI system, but it is implemented. +hyperv=partial +driver-notes-hyperv=This is not tested in a CI system, but it is implemented. +ironic=missing + +[operation.server-pause] +title=Pause a Server +notes=This is pause and unpause a server, where the state is held in memory. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/?#pause-server-pause-action +admin_doc_link=http://docs.openstack.org/admin-guide/compute-images-instances.html +tempest_test_uuids=bd61a9fd-062f-4670-972b-2d6c3e3b9e73 +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=missing +libvirt-virtuozzo-vm=partial +driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is implemented. +libvirt-xen=complete +xenserver=complete +vmware=partial +driver-notes-vmware=This is not tested in a CI system, but it is implemented. +hyperv=complete +ironic=missing + +[operation.server-suspend] +title=Suspend a Server +notes=This suspend and resume a server, where the state is held on disk. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/?expanded=suspend-server-suspend-action-detail +admin_doc_link=http://docs.openstack.org/admin-guide/compute-images-instances.html +tempest_test_uuids=0d8ee21e-b749-462d-83da-b85b41c86c7f +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=partial +driver-notes-libvirt-virtuozzo-ct=This is not tested in a CI system, but it is implemented. +libvirt-virtuozzo-vm=partial +driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is implemented. +libvirt-xen=complete +xenserver=complete +vmware=complete +hyperv=complete +ironic=missing + +[operation.server-consoleoutput] +title=Server console output +notes=This gets the current server console output. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/#show-console-output-os-getconsoleoutput-action +admin_doc_link=http://docs.openstack.org/user-guide/cli_access_instance_through_a_console.html +tempest_test_uuids=4b8867e6-fffa-4d54-b1d1-6fdda57be2f3 +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=unknown +libvirt-virtuozzo-vm=unknown +libvirt-xen=complete +xenserver=complete +vmware=partial +driver-notes-vmware=This is not tested in a CI system, but it is implemented. +hyperv=partial +driver-notes-hyperv=This is not tested in a CI system, but it is implemented. +ironic=missing + +[operation.server-rescue] +title=Server Rescue +notes=This boots a server with a new root disk from the specified glance image + to allow a user to fix a boot partition configuration, or similar. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/#rescue-server-rescue-action +admin_doc_link=http://docs.openstack.org/user-guide/cli_reboot_an_instance.html +tempest_test_uuids=fd032140-714c-42e4-a8fd-adcd8df06be6;70cdb8a1-89f8-437d-9448-8844fd82bf46 +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=partial +driver-notes-libvirt-virtuozzo-ct=This is not tested in a CI system, but it is implemented. +libvirt-virtuozzo-vm=complete +libvirt-xen=complete +xenserver=complete +vmware=complete +hyperv=partial +driver-notes-hyperv=This is not tested in a CI system, but it is implemented. +ironic=missing + +[operation.server-configdrive] +title=Server Config Drive +notes=This ensures the user data provided by the user when booting + a server is available in one of the expected config drive locations. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/#create-server +admin_doc_link=http://docs.openstack.org/user-guide/cli_config_drive.html +tempest_test_uuids=7fff3fb3-91d8-4fd0-bd7d-0204f1f180ba +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=missing +libvirt-virtuozzo-vm=partial +driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is implemented. +libvirt-xen=complete +xenserver=complete +vmware=complete +hyperv=complete +ironic=partial +driver-notes-ironic=This is not tested in a CI system, but it is implemented. + +[operation.server-changepassword] +title=Server Change Password +notes=The ability to reset the password of a user within the server. +maturity=experimental +api_doc_link=http://developer.openstack.org/api-ref/compute/#change-administrative-password-changepassword-action +admin_doc_link=http://docs.openstack.org/cli-reference/nova.html#nova-set-password +tempest_test_uuids=6158df09-4b82-4ab3-af6d-29cf36af858d +cli= +libvirt-kvm=partial +driver-notes-libvirt-kvm=This is not tested in a CI system, but it is implemented. +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=missing +libvirt-virtuozzo-vm=missing +libvirt-xen=missing +xenserver=partial +driver-notes-xenserver=This is not tested in a CI system, but it is implemented. +vmware=missing +hyperv=partial +driver-notes-hyperv=This is not tested in a CI system, but it is implemented. +ironic=missing + +[operation.server-shelve] +title=Server Shelve and Unshelve +notes=The ability to keep a server logically alive, but not using any + cloud resources. For local disk based instances, this involves taking + a snapshot, called offloading. +maturity=complete +api_doc_link=http://developer.openstack.org/api-ref/compute/#shelve-server-shelve-action +admin_doc_link=http://docs.openstack.org/user-guide/cli_stop_and_start_an_instance.html#shelve-and-unshelve-an-instance +tempest_test_uuids=1164e700-0af0-4a4c-8792-35909a88743c,c1b6318c-b9da-490b-9c67-9339b627271f +cli= +libvirt-kvm=complete +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=missing +libvirt-virtuozzo-vm=complete +libvirt-xen=complete +xenserver=complete +vmware=missing +hyperv=complete +ironic=missing diff --git a/doc/source/user/feature-matrix-hpc.ini b/doc/source/user/feature-matrix-hpc.ini new file mode 100644 index 00000000000..a37c561e54b --- /dev/null +++ b/doc/source/user/feature-matrix-hpc.ini @@ -0,0 +1,51 @@ +[target.libvirt-kvm] +title=libvirt+kvm (x86 & ppc64) +link=http://docs.openstack.org/infra/manual/developers.html#project-gating + +[target.libvirt-kvm-s390] +title=libvirt+kvm (s390x) +link=http://docs.openstack.org/infra/manual/developers.html#project-gating + +[target.libvirt-virtuozzo-ct] +title=libvirt+virtuozzo CT +link=https://wiki.openstack.org/wiki/ThirdPartySystems/Virtuozzo_CI + +[target.libvirt-virtuozzo-vm] +title=libvirt+virtuozzo VM +link=https://wiki.openstack.org/wiki/ThirdPartySystems/Virtuozzo_Storage_CI + +[target.libvirt-xen] +title=libvirt+xen +link=https://wiki.openstack.org/wiki/ThirdPartySystems/XenProject_CI + +[target.xenserver] +title=XenServer CI +link=https://wiki.openstack.org/wiki/XenServer/XenServer_CI + +[target.vmware] +title=VMware CI +link=https://wiki.openstack.org/wiki/NovaVMware/Minesweeper + +[target.hyperv] +title=Hyper-V CI +link=https://wiki.openstack.org/wiki/ThirdPartySystems/Hyper-V_CI + + +[operation.gpu-passthrough] +title=GPU Passthrough +notes=TODO add notes for feature +maturity=experimental +api_doc_link=http://developer.openstack.org/api-ref-compute-v2.1.html#createServer +admin_doc_link=http://docs.openstack.org/admin-guide-cloud/compute-images-instances.html#instance-building-blocks +tempest_test_uuids=9a438d88-10c6-4bcd-8b5b-5b6e25e1346f;585e934c-448e-43c4-acbf-d06a9b899997 +libvirt-kvm=complete:l +libvirt-kvm-s390=unknown +libvirt-virtuozzo-ct=partial +driver-notes-libvirt-virtuozzo-ct=This is not tested in a CI system, but it is implemented. +libvirt-virtuozzo-vm=partial +driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is implemented. +libvirt-xen=missing +xenserver=partial:k +vmware=missing +hyperv=missing +ironic=unknown diff --git a/doc/source/user/feature-matrix-nfv.ini b/doc/source/user/feature-matrix-nfv.ini new file mode 100644 index 00000000000..1d6c3d95556 --- /dev/null +++ b/doc/source/user/feature-matrix-nfv.ini @@ -0,0 +1,23 @@ +[target.libvirt-kvm] +title=libvirt+kvm (x86 & ppc64) +link=http://docs.openstack.org/infra/manual/developers.html#project-gating + +[target.libvirt-kvm-s390] +title=libvirt+kvm (s390x) +link=http://docs.openstack.org/infra/manual/developers.html#project-gating + +[target.libvirt-xen] +title=libvirt+xen +link=https://wiki.openstack.org/wiki/ThirdPartySystems/XenProject_CI + + +[operation.numa-placement] +title=NUMA Placement +notes=TODO add notes for feature +maturity=experimental +api_doc_link=http://developer.openstack.org/api-ref-compute-v2.1.html#createServer +admin_doc_link=http://docs.openstack.org/admin-guide-cloud/compute-images-instances.html#instance-building-blocks +tempest_test_uuids=9a438d88-10c6-4bcd-8b5b-5b6e25e1346f;585e934c-448e-43c4-acbf-d06a9b899997 +libvirt-kvm=partial:m +libvirt-kvm-s390=unknown +libvirt-xen=missing diff --git a/doc/source/user/filter-scheduler.rst b/doc/source/user/filter-scheduler.rst new file mode 100644 index 00000000000..bc5d7ac6954 --- /dev/null +++ b/doc/source/user/filter-scheduler.rst @@ -0,0 +1,520 @@ +Filter Scheduler +================ + +The **Filter Scheduler** supports `filtering` and `weighting` to make informed +decisions on where a new instance should be created. This Scheduler supports +working with Compute Nodes only. + +Filtering +--------- + +.. image:: /_static/images/filtering-workflow-1.png + +During its work Filter Scheduler iterates over all found compute nodes, +evaluating each against a set of filters. The list of resulting hosts is +ordered by weighers. The Scheduler then chooses hosts for the requested +number of instances, choosing the most weighted hosts. For a specific +filter to succeed for a specific host, the filter matches the user +request against the state of the host plus some extra magic as defined +by each filter (described in more detail below). + +If the Scheduler cannot find candidates for the next instance, it means that +there are no appropriate hosts where that instance can be scheduled. + +The Filter Scheduler has to be quite flexible to support the required variety +of `filtering` and `weighting` strategies. If this flexibility is insufficient +you can implement `your own filtering algorithm`. + +There are many standard filter classes which may be used +(:mod:`nova.scheduler.filters`): + +* |AllHostsFilter| - does no filtering. It passes all the available hosts. +* |ImagePropertiesFilter| - filters hosts based on properties defined + on the instance's image. It passes hosts that can support the properties + specified on the image used by the instance. +* |AvailabilityZoneFilter| - filters hosts by availability zone. It passes + hosts matching the availability zone specified in the instance properties. + Use a comma to specify multiple zones. The filter will then ensure it matches + any zone specified. +* |ComputeCapabilitiesFilter| - checks that the capabilities provided by the + host compute service satisfy any extra specifications associated with the + instance type. It passes hosts that can create the specified instance type. + + If an extra specs key contains a colon (:), anything before the colon is + treated as a namespace and anything after the colon is treated as the key to + be matched. If a namespace is present and is not ``capabilities``, the filter + ignores the namespace. For example ``capabilities:cpu_info:features`` is + a valid scope format. For backward compatibility, when a key doesn't contain + a colon (:), the key's contents are important. If this key is an attribute of + HostState object, like ``free_disk_mb``, the filter also treats the extra + specs key as the key to be matched. If not, the filter will ignore the key. + + The extra specifications can have an operator at the beginning of the value + string of a key/value pair. If there is no operator specified, then a + default operator of ``s==`` is used. Valid operators are: + + :: + + * = (equal to or greater than as a number; same as vcpus case) + * == (equal to as a number) + * != (not equal to as a number) + * >= (greater than or equal to as a number) + * <= (less than or equal to as a number) + * s== (equal to as a string) + * s!= (not equal to as a string) + * s>= (greater than or equal to as a string) + * s> (greater than as a string) + * s<= (less than or equal to as a string) + * s< (less than as a string) + * (substring) + * (all elements contained in collection) + * (find one of these) + + Examples are: ">= 5", "s== 2.1.0", " gcc", " aes mmx", and " fpu gpu" + + some of attributes that can be used as useful key and their values contains: + + :: + + * free_ram_mb (compared with a number, values like ">= 4096") + * free_disk_mb (compared with a number, values like ">= 10240") + * host (compared with a string, values like: " compute","s== compute_01") + * hypervisor_type (compared with a string, values like: "s== QEMU", "s== powervm") + * hypervisor_version (compared with a number, values like : ">= 1005003", "== 2000000") + * num_instances (compared with a number, values like: "<= 10") + * num_io_ops (compared with a number, values like: "<= 5") + * vcpus_total (compared with a number, values like: "= 48", ">=24") + * vcpus_used (compared with a number, values like: "= 0", "<= 10") + +* |AggregateInstanceExtraSpecsFilter| - checks that the aggregate metadata + satisfies any extra specifications associated with the instance type (that + have no scope or are scoped with ``aggregate_instance_extra_specs``). + It passes hosts that can create the specified instance type. + The extra specifications can have the same operators as + |ComputeCapabilitiesFilter|. To specify multiple values for the same key + use a comma. E.g., "value1,value2". All hosts are passed if no extra_specs + are specified. +* |ComputeFilter| - passes all hosts that are operational and enabled. +* |CoreFilter| - filters based on CPU core utilization. It passes hosts with + sufficient number of CPU cores. +* |AggregateCoreFilter| - filters hosts by CPU core number with per-aggregate + ``cpu_allocation_ratio`` setting. If no per-aggregate value is found, it will + fall back to the global default ``cpu_allocation_ratio``. If more than one value + is found for a host (meaning the host is in two different aggregates with + different ratio settings), the minimum value will be used. +* |IsolatedHostsFilter| - filter based on ``isolated_images``, ``isolated_hosts`` + and ``restrict_isolated_hosts_to_isolated_images`` flags. +* |JsonFilter| - allows simple JSON-based grammar for selecting hosts. +* |RamFilter| - filters hosts by their RAM. Only hosts with sufficient RAM + to host the instance are passed. +* |AggregateRamFilter| - filters hosts by RAM with per-aggregate + ``ram_allocation_ratio`` setting. If no per-aggregate value is found, it will + fall back to the global default ``ram_allocation_ratio``. If more than one value + is found for a host (meaning the host is in two different aggregates with + different ratio settings), the minimum value will be used. +* |DiskFilter| - filters hosts by their disk allocation. Only hosts with sufficient + disk space to host the instance are passed. + ``disk_allocation_ratio`` setting. The virtual disk to physical disk + allocation ratio, 1.0 by default. The total allowed allocated disk size will + be physical disk multiplied this ratio. +* |AggregateDiskFilter| - filters hosts by disk allocation with per-aggregate + ``disk_allocation_ratio`` setting. If no per-aggregate value is found, it will + fall back to the global default ``disk_allocation_ratio``. If more than one value + is found for a host (meaning the host is in two or more different aggregates with + different ratio settings), the minimum value will be used. +* |NumInstancesFilter| - filters compute nodes by number of running instances. Nodes + with too many instances will be filtered. + ``max_instances_per_host`` setting. Maximum number of instances allowed to run on + this host. The host will be ignored by the scheduler if more than ``max_instances_per_host`` + already exist on the host. +* |AggregateNumInstancesFilter| - filters hosts by number of instances with + per-aggregate ``max_instances_per_host`` setting. If no per-aggregate value + is found, it will fall back to the global default ``max_instances_per_host``. + If more than one value is found for a host (meaning the host is in two or more + different aggregates with different max instances per host settings), + the minimum value will be used. +* |IoOpsFilter| - filters hosts by concurrent I/O operations on it. + hosts with too many concurrent I/O operations will be filtered. + ``max_io_ops_per_host`` setting. Maximum number of I/O intensive instances allowed to + run on this host, the host will be ignored by scheduler if more than ``max_io_ops_per_host`` + instances such as build/resize/snapshot etc are running on it. +* |AggregateIoOpsFilter| - filters hosts by I/O operations with per-aggregate + ``max_io_ops_per_host`` setting. If no per-aggregate value is found, it will + fall back to the global default ``max_io_ops_per_host``. If more than + one value is found for a host (meaning the host is in two or more different + aggregates with different max io operations settings), the minimum value + will be used. +* |PciPassthroughFilter| - Filter that schedules instances on a host if the host + has devices to meet the device requests in the 'extra_specs' for the flavor. +* |SimpleCIDRAffinityFilter| - allows a new instance on a host within + the same IP block. +* |DifferentHostFilter| - allows the instance on a different host from a + set of instances. +* |SameHostFilter| - puts the instance on the same host as another instance in + a set of instances. +* |RetryFilter| - filters hosts that have been attempted for scheduling. + Only passes hosts that have not been previously attempted. +* |TrustedFilter| (EXPERIMENTAL) - filters hosts based on their trust. Only + passes hosts that meet the trust requirements specified in the instance + properties. + + .. warning:: TrustedFilter is deprecated for removal in the 17.0.0 Queens + release. There is no replacement planned for this filter. It has been + marked experimental since its inception. It is incomplete and not tested. + +* |TypeAffinityFilter| - Only passes hosts that are not already running an + instance of the requested type. + + .. warning:: TypeAffinityFilter is deprecated for removal in the + 17.0.0 Queens release. There is no replacement planned for this + filter. It is fundamentally flawed in that it relies on the + ``flavors.id`` primary key and if a flavor "changed", i.e. deleted + and re-created with new values, it will result in this filter + thinking it is a different flavor, thus breaking the usefulness of + this filter. + +* |AggregateTypeAffinityFilter| - limits instance_type by aggregate. + This filter passes hosts if no instance_type key is set or + the instance_type aggregate metadata value contains the name of the + instance_type requested. The value of the instance_type metadata entry is + a string that may contain either a single instance_type name or a comma + separated list of instance_type names. e.g. 'm1.nano' or "m1.nano,m1.small" +* |ServerGroupAntiAffinityFilter| - This filter implements anti-affinity for a + server group. First you must create a server group with a policy of + 'anti-affinity' via the server groups API. Then, when you boot a new server, + provide a scheduler hint of 'group=' where is the UUID of the + server group you created. This will result in the server getting added to the + group. When the server gets scheduled, anti-affinity will be enforced among + all servers in that group. +* |ServerGroupAffinityFilter| - This filter works the same way as + ServerGroupAntiAffinityFilter. The difference is that when you create the server + group, you should specify a policy of 'affinity'. +* |AggregateMultiTenancyIsolation| - isolate tenants in specific aggregates. + To specify multiple tenants use a comma. Eg. "tenant1,tenant2" +* |AggregateImagePropertiesIsolation| - isolates hosts based on image + properties and aggregate metadata. Use a comma to specify multiple values for the + same property. The filter will then ensure at least one value matches. +* |MetricsFilter| - filters hosts based on metrics weight_setting. Only hosts with + the available metrics are passed. +* |NUMATopologyFilter| - filters hosts based on the NUMA topology requested by the + instance, if any. + +Now we can focus on these standard filter classes in some detail. We'll skip the +simplest ones, such as |AllHostsFilter|, |CoreFilter| and |RamFilter|, +because their functionality is relatively simple and can be understood from the +code. For example class |RamFilter| has the next realization: + +:: + + class RamFilter(filters.BaseHostFilter): + """Ram Filter with over subscription flag""" + + def host_passes(self, host_state, filter_properties): + """Only return hosts with sufficient available RAM.""" + instance_type = filter_properties.get('instance_type') + requested_ram = instance_type['memory_mb'] + free_ram_mb = host_state.free_ram_mb + total_usable_ram_mb = host_state.total_usable_ram_mb + used_ram_mb = total_usable_ram_mb - free_ram_mb + return total_usable_ram_mb * FLAGS.ram_allocation_ratio - used_ram_mb >= requested_ram + +Here ``ram_allocation_ratio`` means the virtual RAM to physical RAM allocation +ratio (it is ``1.5`` by default). + +The |AvailabilityZoneFilter| looks at the availability zone of compute node +and availability zone from the properties of the request. Each compute service +has its own availability zone. So deployment engineers have an option to run +scheduler with availability zones support and can configure availability zones +on each compute host. This class's method ``host_passes`` returns ``True`` if +availability zone mentioned in request is the same on the current compute host. + +The |ImagePropertiesFilter| filters hosts based on the architecture, +hypervisor type and virtual machine mode specified in the +instance. For example, an instance might require a host that supports the ARM +architecture on a qemu compute host. The |ImagePropertiesFilter| will only +pass hosts that can satisfy this request. These instance +properties are populated from properties defined on the instance's image. +E.g. an image can be decorated with these properties using +``glance image-update img-uuid --property architecture=arm --property +hypervisor_type=qemu`` +Only hosts that satisfy these requirements will pass the +|ImagePropertiesFilter|. + +|ComputeCapabilitiesFilter| checks if the host satisfies any ``extra_specs`` +specified on the instance type. The ``extra_specs`` can contain key/value pairs. +The key for the filter is either non-scope format (i.e. no ``:`` contained), or +scope format in capabilities scope (i.e. ``capabilities:xxx:yyy``). One example +of capabilities scope is ``capabilities:cpu_info:features``, which will match +host's cpu features capabilities. The |ComputeCapabilitiesFilter| will only +pass hosts whose capabilities satisfy the requested specifications. All hosts +are passed if no ``extra_specs`` are specified. + +|ComputeFilter| is quite simple and passes any host whose compute service is +enabled and operational. + +Now we are going to |IsolatedHostsFilter|. There can be some special hosts +reserved for specific images. These hosts are called **isolated**. So the +images to run on the isolated hosts are also called isolated. The filter +checks if ``isolated_images`` flag named in instance specifications is the same +as the host specified in ``isolated_hosts``. Isolated hosts can run non-isolated +images if the flag ``restrict_isolated_hosts_to_isolated_images`` is set to false. + +|DifferentHostFilter| - method ``host_passes`` returns ``True`` if the host to +place an instance on is different from all the hosts used by a set of instances. + +|SameHostFilter| does the opposite to what |DifferentHostFilter| does. +``host_passes`` returns ``True`` if the host we want to place an instance on is +one of the hosts used by a set of instances. + +|SimpleCIDRAffinityFilter| looks at the subnet mask and investigates if +the network address of the current host is in the same sub network as it was +defined in the request. + +|JsonFilter| - this filter provides the opportunity to write complicated +queries for the hosts capabilities filtering, based on simple JSON-like syntax. +There can be used the following operations for the host states properties: +``=``, ``<``, ``>``, ``in``, ``<=``, ``>=``, that can be combined with the following +logical operations: ``not``, ``or``, ``and``. For example, the following query can be +found in tests: + +:: + + ['and', + ['>=', '$free_ram_mb', 1024], + ['>=', '$free_disk_mb', 200 * 1024] + ] + +This query will filter all hosts with free RAM greater or equal than 1024 MB +and at the same time with free disk space greater or equal than 200 GB. + +Many filters use data from ``scheduler_hints``, that is defined in the moment of +creation of the new server for the user. The only exception for this rule is +|JsonFilter|, that takes data from the schedulers ``HostState`` data structure +directly. Variable naming, such as the ``$free_ram_mb`` example above, should +be based on those attributes. + +The |RetryFilter| filters hosts that have already been attempted for +scheduling. It only passes hosts that have not been previously attempted. If a +compute node is raising an exception when spawning an instance, then the +compute manager will reschedule it by adding the failing host to a retry +dictionary so that the RetryFilter will not accept it as a possible +destination. That means that if all of your compute nodes are failing, then the +RetryFilter will return 0 hosts and the scheduler will raise a NoValidHost +exception even if the problem is related to 1:N compute nodes. If you see that +case in the scheduler logs, then your problem is most likely related to a +compute problem and you should check the compute logs. + +The |TrustedFilter| filters hosts based on their trust. Only passes hosts +that match the trust requested in the ``extra_specs`` for the flavor. The key +for this filter must be scope format as ``trust:trusted_host``, where ``trust`` +is the scope of the key and ``trusted_host`` is the actual key value. +The value of this pair (``trusted``/``untrusted``) must match the +integrity of a host (obtained from the Attestation service) before it is +passed by the |TrustedFilter|. + +The |NUMATopologyFilter| considers the NUMA topology that was specified for the instance +through the use of flavor extra_specs in combination with the image properties, as +described in detail in the related nova-spec document: + +* http://git.openstack.org/cgit/openstack/nova-specs/tree/specs/juno/virt-driver-numa-placement.rst + +and try to match it with the topology exposed by the host, accounting for the +``ram_allocation_ratio`` and ``cpu_allocation_ratio`` for over-subscription. The +filtering is done in the following manner: + +* Filter will attempt to pack instance cells onto host cells. +* It will consider the standard over-subscription limits for each host NUMA cell, + and provide limits to the compute host accordingly (as mentioned above). +* If instance has no topology defined, it will be considered for any host. +* If instance has a topology defined, it will be considered only for NUMA + capable hosts. + +Configuring Filters +------------------- + +To use filters you specify two settings: + +* ``filter_scheduler.available_filters`` - Defines filter classes made + available to the scheduler. This setting can be used multiple times. +* ``filter_scheduler.enabled_filters`` - Of the available filters, defines + those that the scheduler uses by default. + +The default values for these settings in nova.conf are: + +:: + + --filter_scheduler.available_filters=nova.scheduler.filters.all_filters + --filter_scheduler.enabled_filters=RamFilter,ComputeFilter,AvailabilityZoneFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,ServerGroupAntiAffinityFilter,ServerGroupAffinityFilter + +With this configuration, all filters in ``nova.scheduler.filters`` +would be available, and by default the |RamFilter|, |ComputeFilter|, +|AvailabilityZoneFilter|, |ComputeCapabilitiesFilter|, +|ImagePropertiesFilter|, |ServerGroupAntiAffinityFilter|, +and |ServerGroupAffinityFilter| would be used. + +Writing Your Own Filter +----------------------- + +To create **your own filter** you must inherit from +|BaseHostFilter| and implement one method: +``host_passes``. This method should return ``True`` if a host passes the filter. It +takes ``host_state`` (describing the host) and ``filter_properties`` dictionary as the +parameters. + +As an example, nova.conf could contain the following scheduler-related +settings: + +:: + + --scheduler.driver=nova.scheduler.FilterScheduler + --filter_scheduler.available_filters=nova.scheduler.filters.all_filters + --filter_scheduler.available_filters=myfilter.MyFilter + --filter_scheduler.enabled_filters=RamFilter,ComputeFilter,MyFilter + +.. note:: When writing your own filter, be sure to add it to the list of available filters + and enable it in the default filters. The "all_filters" setting only includes the + filters shipped with nova. + +With these settings, nova will use the ``FilterScheduler`` for the scheduler +driver. All of the standard nova filters and MyFilter are available to the +FilterScheduler, but just the RamFilter, ComputeFilter, and MyFilter will be +used on each request. + +Each filter selects hosts in a different way and has different costs. The order +of ``filter_scheduler.enabled_filters`` affects scheduling performance. The +general suggestion is to filter out invalid hosts as soon as possible to avoid +unnecessary costs. We can sort ``filter_scheduler.enabled_filters`` items by +their costs in reverse order. For example, ComputeFilter is better before any +resource calculating filters like RamFilter, CoreFilter. + +In medium/large environments having AvailabilityZoneFilter before any +capability or resource calculating filters can be useful. + +Weights +------- + +Filter Scheduler uses the so-called **weights** during its work. A weigher is a +way to select the best suitable host from a group of valid hosts by giving +weights to all the hosts in the list. + +In order to prioritize one weigher against another, all the weighers have to +define a multiplier that will be applied before computing the weight for a node. +All the weights are normalized beforehand so that the multiplier can be applied +easily. Therefore the final weight for the object will be:: + + weight = w1_multiplier * norm(w1) + w2_multiplier * norm(w2) + ... + +A weigher should be a subclass of ``weights.BaseHostWeigher`` and they can implement +both the ``weight_multiplier`` and ``_weight_object`` methods or just implement the +``weight_objects`` method. ``weight_objects`` method is overridden only if you need +access to all objects in order to calculate weights, and it just return a list of weights, +and not modify the weight of the object directly, since final weights are normalized +and computed by ``weight.BaseWeightHandler``. + +The Filter Scheduler weighs hosts based on the config option +`filter_scheduler.weight_classes`, this defaults to +`nova.scheduler.weights.all_weighers`, which selects the following weighers: + +* |RAMWeigher| Compute weight based on available RAM on the compute node. + Sort with the largest weight winning. If the multiplier is negative, the + host with least RAM available will win (useful for stacking hosts, instead + of spreading). +* |DiskWeigher| Hosts are weighted and sorted by free disk space with the largest + weight winning. If the multiplier is negative, the host with less disk space available + will win (useful for stacking hosts, instead of spreading). +* |MetricsWeigher| This weigher can compute the weight based on the compute node + host's various metrics. The to-be weighed metrics and their weighing ratio + are specified in the configuration file as the followings:: + + metrics_weight_setting = name1=1.0, name2=-1.0 + +* |IoOpsWeigher| The weigher can compute the weight based on the compute node + host's workload. The default is to preferably choose light workload compute + hosts. If the multiplier is positive, the weigher prefer choosing heavy + workload compute hosts, the weighing has the opposite effect of the default. + +* |PCIWeigher| Compute a weighting based on the number of PCI devices on the + host and the number of PCI devices requested by the instance. For example, + given three hosts - one with a single PCI device, one with many PCI devices, + and one with no PCI devices - nova should prioritise these differently based + on the demands of the instance. If the instance requests a single PCI device, + then the first of the hosts should be preferred. Similarly, if the instance + requests multiple PCI devices, then the second of these hosts would be + preferred. Finally, if the instance does not request a PCI device, then the + last of these hosts should be preferred. + + For this to be of any value, at least one of the |PciPassthroughFilter| or + |NUMATopologyFilter| filters must be enabled. + + :Configuration Option: ``[filter_scheduler] pci_weight_multiplier``. Only + positive values are allowed for the multiplier as a negative value would + force non-PCI instances away from non-PCI hosts, thus, causing future + scheduling issues. + +* |ServerGroupSoftAffinityWeigher| The weigher can compute the weight based + on the number of instances that run on the same server group. The largest + weight defines the preferred host for the new instance. For the multiplier + only a positive value is meaningful for the calculation as a negative value + would mean that the affinity weigher would prefer non collocating placement. + +* |ServerGroupSoftAntiAffinityWeigher| The weigher can compute the weight based + on the number of instances that run on the same server group as a negative + value. The largest weight defines the preferred host for the new instance. + For the multiplier only a positive value is meaningful for the calculation as + a negative value would mean that the anti-affinity weigher would prefer + collocating placement. + +Filter Scheduler makes a local list of acceptable hosts by repeated filtering and +weighing. Each time it chooses a host, it virtually consumes resources on it, +so subsequent selections can adjust accordingly. It is useful if the customer +asks for a large block of instances, because weight is computed for +each instance requested. + +.. image:: /_static/images/filtering-workflow-2.png + +At the end Filter Scheduler sorts selected hosts by their weight and attempts +to provision instances on the chosen hosts. + +P.S.: you can find more examples of using Filter Scheduler and standard filters +in :mod:`nova.tests.scheduler`. + +.. |AllHostsFilter| replace:: :class:`AllHostsFilter ` +.. |ImagePropertiesFilter| replace:: :class:`ImagePropertiesFilter ` +.. |AvailabilityZoneFilter| replace:: :class:`AvailabilityZoneFilter ` +.. |BaseHostFilter| replace:: :class:`BaseHostFilter ` +.. |ComputeCapabilitiesFilter| replace:: :class:`ComputeCapabilitiesFilter ` +.. |ComputeFilter| replace:: :class:`ComputeFilter ` +.. |CoreFilter| replace:: :class:`CoreFilter ` +.. |AggregateCoreFilter| replace:: :class:`AggregateCoreFilter ` +.. |IsolatedHostsFilter| replace:: :class:`IsolatedHostsFilter ` +.. |JsonFilter| replace:: :class:`JsonFilter ` +.. |RamFilter| replace:: :class:`RamFilter ` +.. |AggregateRamFilter| replace:: :class:`AggregateRamFilter ` +.. |DiskFilter| replace:: :class:`DiskFilter ` +.. |AggregateDiskFilter| replace:: :class:`AggregateDiskFilter ` +.. |NumInstancesFilter| replace:: :class:`NumInstancesFilter ` +.. |AggregateNumInstancesFilter| replace:: :class:`AggregateNumInstancesFilter ` +.. |IoOpsFilter| replace:: :class:`IoOpsFilter ` +.. |AggregateIoOpsFilter| replace:: :class:`AggregateIoOpsFilter ` +.. |PciPassthroughFilter| replace:: :class:`PciPassthroughFilter ` +.. |SimpleCIDRAffinityFilter| replace:: :class:`SimpleCIDRAffinityFilter ` +.. |DifferentHostFilter| replace:: :class:`DifferentHostFilter ` +.. |SameHostFilter| replace:: :class:`SameHostFilter ` +.. |RetryFilter| replace:: :class:`RetryFilter ` +.. |TrustedFilter| replace:: :class:`TrustedFilter ` +.. |TypeAffinityFilter| replace:: :class:`TypeAffinityFilter ` +.. |AggregateTypeAffinityFilter| replace:: :class:`AggregateTypeAffinityFilter ` +.. |ServerGroupAntiAffinityFilter| replace:: :class:`ServerGroupAntiAffinityFilter ` +.. |ServerGroupAffinityFilter| replace:: :class:`ServerGroupAffinityFilter ` +.. |AggregateInstanceExtraSpecsFilter| replace:: :class:`AggregateInstanceExtraSpecsFilter ` +.. |AggregateMultiTenancyIsolation| replace:: :class:`AggregateMultiTenancyIsolation ` +.. |NUMATopologyFilter| replace:: :class:`NUMATopologyFilter ` +.. |RAMWeigher| replace:: :class:`RAMWeigher ` +.. |AggregateImagePropertiesIsolation| replace:: :class:`AggregateImagePropertiesIsolation ` +.. |MetricsFilter| replace:: :class:`MetricsFilter ` +.. |MetricsWeigher| replace:: :class:`MetricsWeigher ` +.. |IoOpsWeigher| replace:: :class:`IoOpsWeigher ` +.. |PCIWeigher| replace:: :class:`PCIWeigher ` +.. |ServerGroupSoftAffinityWeigher| replace:: :class:`ServerGroupSoftAffinityWeigher ` +.. |ServerGroupSoftAntiAffinityWeigher| replace:: :class:`ServerGroupSoftAntiAffinityWeigher ` +.. |DiskWeigher| replace:: :class:`DiskWeigher ` diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst new file mode 100644 index 00000000000..aa4aaeeddda --- /dev/null +++ b/doc/source/user/index.rst @@ -0,0 +1,85 @@ +================== +User Documentation +================== + +End user guide +-------------- + +.. toctree:: + :maxdepth: 1 + + launch-instances + +.. todo:: The rest of this document should probably move to the admin guide. + +Architecture Overview +--------------------- + +* :doc:`Nova architecture `: An overview of how all the parts in + nova fit together. + +* :doc:`Block Device Mapping `: One of the more + complicated parts to understand is the Block Device Mapping parameters used + to connect specific block devices to computes. This deserves its own deep + dive. + +* :doc:`Conductor `: TODO + +Deployment Considerations +------------------------- + +There is information you might want to consider before doing your deployment, +especially if it is going to be a larger deployment. For smaller deployments +the defaults from the :doc:`install guide ` will be sufficient. + +* **Compute Driver Features Supported**: While the majority of nova deployments use + libvirt/kvm, you can use nova with other compute drivers. Nova attempts to + provide a unified feature set across these, however, not all features are + implemented on all backends, and not all features are equally well tested. + + * :doc:`Feature Support by Use Case `: A view of + what features each driver supports based on what's important to some large + use cases (General Purpose Cloud, NFV Cloud, HPC Cloud). + + * :doc:`Feature Support full list `: A detailed dive through + features in each compute driver backend. + +* :doc:`Cells v2 Planning `: For large deployments, Cells v2 + allows sharding of your compute environment. Upfront planning is key to a + successful Cells v2 layout. + +* :doc:`Placement service `: Overview of the placement + service, including how it fits in with the rest of nova. + +* :doc:`Running nova-api on wsgi `: Considerations for using a real + WSGI container instead of the baked-in eventlet web server. + +Maintenance +----------- + +Once you are running nova, the following information is extremely useful. + +* :doc:`Admin Guide `: A collection of guides for administrating + nova. + + .. warning:: + + This guide was imported during the Pike cycle and is a bit out of + date. It will be updated during Queens to be more accurate. + +* :doc:`Upgrades `: How nova is designed to be upgraded for minimal + service impact, and the order you should do them in. + +* :doc:`Quotas `: Managing project quotas in nova. + +* :doc:`Aggregates `: Aggregates are a useful way of grouping + hosts together for scheduling purposes. + +* :doc:`Filter Scheduler `: How the filter scheduler is + configured, and how that will impact where compute instances land in your + environment. If you are seeing unexpected distribution of compute instances + in your hosts, you'll want to dive into this configuration. + +* :doc:`Exposing custom metadata to compute instances `: How and + when you might want to extend the basic metadata exposed to compute instances + (either via metadata server or config drive) for your specific purposes. diff --git a/doc/source/user/launch-instance-from-image.rst b/doc/source/user/launch-instance-from-image.rst new file mode 100644 index 00000000000..700fab67975 --- /dev/null +++ b/doc/source/user/launch-instance-from-image.rst @@ -0,0 +1,143 @@ +================================ +Launch an instance from an image +================================ + +Follow the steps below to launch an instance from an image. + +#. After you gather required parameters, run the following command to + launch an instance. Specify the server name, flavor ID, and image ID. + + .. code-block:: console + + $ openstack server create --flavor FLAVOR_ID --image IMAGE_ID --key-name KEY_NAME \ + --user-data USER_DATA_FILE --security-group SEC_GROUP_NAME --property KEY=VALUE \ + INSTANCE_NAME + + Optionally, you can provide a key name for access control and a security + group for security. You can also include metadata key and value pairs. + For example, you can add a description for your server by providing the + ``--property description="My Server"`` parameter. + + You can pass user data in a local file at instance launch by using the + ``--user-data USER-DATA-FILE`` parameter. + + .. important:: + + If you boot an instance with an INSTANCE_NAME greater than 63 characters, + Compute truncates it automatically when turning it into a host name to + ensure the correct work of dnsmasq. The corresponding warning is written + into the ``neutron-dnsmasq.log`` file. + + The following command launches the ``MyCirrosServer`` instance with the + ``m1.small`` flavor (ID of ``1``), ``cirros-0.3.2-x86_64-uec`` image (ID + of ``397e713c-b95b-4186-ad46-6126863ea0a9``), ``default`` security + group, ``KeyPair01`` key, and a user data file called + ``cloudinit.file``: + + .. code-block:: console + + $ openstack server create --flavor 1 --image 397e713c-b95b-4186-ad46-6126863ea0a9 \ + --security-group default --key-name KeyPair01 --user-data cloudinit.file \ + myCirrosServer + + Depending on the parameters that you provide, the command returns a list + of server properties. + + .. code-block:: console + + +--------------------------------------+-----------------------------------------------+ + | Field | Value | + +--------------------------------------+-----------------------------------------------+ + | OS-DCF:diskConfig | MANUAL | + | OS-EXT-AZ:availability_zone | | + | OS-EXT-SRV-ATTR:host | None | + | OS-EXT-SRV-ATTR:hypervisor_hostname | None | + | OS-EXT-SRV-ATTR:instance_name | | + | OS-EXT-STS:power_state | NOSTATE | + | OS-EXT-STS:task_state | scheduling | + | OS-EXT-STS:vm_state | building | + | OS-SRV-USG:launched_at | None | + | OS-SRV-USG:terminated_at | None | + | accessIPv4 | | + | accessIPv6 | | + | addresses | | + | adminPass | E4Ksozt4Efi8 | + | config_drive | | + | created | 2016-11-30T14:48:05Z | + | flavor | m1.tiny | + | hostId | | + | id | 89015cc9-bdf1-458a-8518-fdca2b4a5785 | + | image | cirros (397e713c-b95b-4186-ad46-6126863ea0a9) | + | key_name | KeyPair01 | + | name | myCirrosServer | + | os-extended-volumes:volumes_attached | [] | + | progress | 0 | + | project_id | 5669caad86a04256994cdf755df4d3c1 | + | properties | | + | security_groups | [{u'name': u'default'}] | + | status | BUILD | + | updated | 2016-11-30T14:48:05Z | + | user_id | c36cec73b0e44876a4478b1e6cd749bb | + | metadata | {u'KEY': u'VALUE'} | + +--------------------------------------+-----------------------------------------------+ + + A status of ``BUILD`` indicates that the instance has started, but is + not yet online. + + A status of ``ACTIVE`` indicates that the instance is active. + +#. Copy the server ID value from the ``id`` field in the output. Use the + ID to get server details or to delete your server. + +#. Copy the administrative password value from the ``adminPass`` field. Use the + password to log in to your server. + + .. note:: + + You can also place arbitrary local files into the instance file + system at creation time by using the ``--file `` + option. You can store up to five files. For example, if you have a + special authorized keys file named ``special_authorized_keysfile`` that + you want to put on the instance rather than using the regular SSH key + injection, you can use the ``--file`` option as shown in the following + example. + + .. code-block:: console + + $ openstack server create --image ubuntu-cloudimage --flavor 1 vm-name \ + --file /root/.ssh/authorized_keys=special_authorized_keysfile + +#. Check if the instance is online. + + .. code-block:: console + + $ openstack server list + + The list shows the ID, name, status, and private (and if assigned, + public) IP addresses for all instances in the project to which you + belong: + + .. code-block:: console + + +-------------+----------------------+--------+------------+-------------+------------------+------------+ + | ID | Name | Status | Task State | Power State | Networks | Image Name | + +-------------+----------------------+--------+------------+-------------+------------------+------------+ + | 84c6e57d... | myCirrosServer | ACTIVE | None | Running | private=10.0.0.3 | cirros | + | 8a99547e... | myInstanceFromVolume | ACTIVE | None | Running | private=10.0.0.4 | centos | + +-------------+----------------------+--------+------------+-------------+------------------+------------+ + + If the status for the instance is ACTIVE, the instance is online. + +#. To view the available options for the :command:`openstack server list` + command, run the following command: + + .. code-block:: console + + $ openstack help server list + + .. note:: + + If you did not provide a key pair, security groups, or rules, you + can access the instance only from inside the cloud through VNC. Even + pinging the instance is not possible. + diff --git a/doc/source/user/launch-instance-from-volume.rst b/doc/source/user/launch-instance-from-volume.rst new file mode 100644 index 00000000000..eacd7661dfa --- /dev/null +++ b/doc/source/user/launch-instance-from-volume.rst @@ -0,0 +1,335 @@ +================================ +Launch an instance from a volume +================================ + +You can boot instances from a volume instead of an image. + +To complete these tasks, use these parameters on the +:command:`openstack server create` command: + +.. tabularcolumns:: |p{0.3\textwidth}|p{0.25\textwidth}|p{0.4\textwidth}| +.. list-table:: + :header-rows: 1 + :widths: 30 15 30 + + * - Task + - openstack server create parameter + - Information + * - Boot an instance from an image and attach a non-bootable + volume. + - ``--block-device`` + - :ref:`Boot_instance_from_image_and_attach_non-bootable_volume` + * - Create a volume from an image and boot an instance from that + volume. + - ``--block-device`` + - :ref:`Create_volume_from_image_and_boot_instance` + * - Boot from an existing source image, volume, or snapshot. + - ``--block-device`` + - :ref:`Create_volume_from_image_and_boot_instance` + * - Attach a swap disk to an instance. + - ``--swap`` + - :ref:`Attach_swap_or_ephemeral_disk_to_an_instance` + * - Attach an ephemeral disk to an instance. + - ``--ephemeral`` + - :ref:`Attach_swap_or_ephemeral_disk_to_an_instance` + +.. note:: + + To attach a volume to a running instance, see + `Attach a volume to an instance`_. + +.. _Attach a volume to an instance: https://docs.openstack.org/cinder/latest/cli/cli-manage-volumes.html#attach-a-volume-to-an-instance +.. _Boot_instance_from_image_and_attach_non-bootable_volume: + +Boot instance from image and attach non-bootable volume +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Create a non-bootable volume and attach that volume to an instance that +you boot from an image. + +To create a non-bootable volume, do not create it from an image. The +volume must be entirely empty with no partition table and no file +system. + +#. Create a non-bootable volume. + + .. code-block:: console + + $ openstack volume create --size 8 my-volume + +---------------------+--------------------------------------+ + | Field | Value | + +---------------------+--------------------------------------+ + | attachments | [] | + | availability_zone | nova | + | bootable | false | + | consistencygroup_id | None | + | created_at | 2016-11-25T10:37:08.850997 | + | description | None | + | encrypted | False | + | id | b8f7bbec-6274-4cd7-90e7-60916a5e75d4 | + | migration_status | None | + | multiattach | False | + | name | my-volume | + | properties | | + | replication_status | disabled | + | size | 8 | + | snapshot_id | None | + | source_volid | None | + | status | creating | + | type | None | + | updated_at | None | + | user_id | 0678735e449149b0a42076e12dd54e28 | + +---------------------+--------------------------------------+ + +#. List volumes. + + .. code-block:: console + + $ openstack volume list + +--------------------------------------+--------------+-----------+------+-------------+ + | ID | Display Name | Status | Size | Attached to | + +--------------------------------------+--------------+-----------+------+-------------+ + | b8f7bbec-6274-4cd7-90e7-60916a5e75d4 | my-volume | available | 8 | | + +--------------------------------------+--------------+-----------+------+-------------+ + +#. Boot an instance from an image and attach the empty volume to the + instance. + + .. code-block:: console + + $ openstack server create --flavor 2 --image 98901246-af91-43d8-b5e6-a4506aa8f369 \ + --block-device source=volume,id=d620d971-b160-4c4e-8652-2513d74e2080,dest=volume,shutdown=preserve \ + myInstanceWithVolume + +--------------------------------------+--------------------------------------------+ + | Field | Value | + +--------------------------------------+--------------------------------------------+ + | OS-DCF:diskConfig | MANUAL | + | OS-EXT-AZ:availability_zone | nova | + | OS-EXT-SRV-ATTR:host | - | + | OS-EXT-SRV-ATTR:hypervisor_hostname | - | + | OS-EXT-SRV-ATTR:instance_name | instance-00000004 | + | OS-EXT-STS:power_state | 0 | + | OS-EXT-STS:task_state | scheduling | + | OS-EXT-STS:vm_state | building | + | OS-SRV-USG:launched_at | - | + | OS-SRV-USG:terminated_at | - | + | accessIPv4 | | + | accessIPv6 | | + | adminPass | ZaiYeC8iucgU | + | config_drive | | + | created | 2014-05-09T16:34:50Z | + | flavor | m1.small (2) | + | hostId | | + | id | 1e1797f3-1662-49ff-ae8c-a77e82ee1571 | + | image | cirros-0.3.5-x86_64-uec (98901246-af91-... | + | key_name | - | + | metadata | {} | + | name | myInstanceWithVolume | + | os-extended-volumes:volumes_attached | [{"id": "d620d971-b160-4c4e-8652-2513d7... | + | progress | 0 | + | security_groups | default | + | status | BUILD | + | tenant_id | ccef9e62b1e645df98728fb2b3076f27 | + | updated | 2014-05-09T16:34:51Z | + | user_id | fef060ae7bfd4024b3edb97dff59017a | + +--------------------------------------+--------------------------------------------+ + +.. _Create_volume_from_image_and_boot_instance: + +Create volume from image and boot instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can create a volume from an existing image, volume, or snapshot. +This procedure shows you how to create a volume from an image, and use +the volume to boot an instance. + +#. List the available images. + + .. code-block:: console + + $ openstack image list + +-----------------+---------------------------------+--------+ + | ID | Name | Status | + +-----------------+---------------------------------+--------+ + | 484e05af-a14... | Fedora-x86_64-20-20131211.1-sda | active | + | 98901246-af9... | cirros-0.3.5-x86_64-uec | active | + | b6e95589-7eb... | cirros-0.3.5-x86_64-uec-kernel | active | + | c90893ea-e73... | cirros-0.3.5-x86_64-uec-ramdisk | active | + +-----------------+---------------------------------+--------+ + + Note the ID of the image that you want to use to create a volume. + + If you want to create a volume to a specific storage backend, you need + to use an image which has *cinder_img_volume_type* property. + In this case, a new volume will be created as *storage_backend1* volume + type. + + .. code-block:: console + + $ openstack image show 98901246-af9d-4b61-bea8-09cc6dc41829 + +------------------+------------------------------------------------------+ + | Field | Value | + +------------------+------------------------------------------------------+ + | checksum | ee1eca47dc88f4879d8a229cc70a07c6 | + | container_format | bare | + | created_at | 2016-10-08T14:59:05Z | + | disk_format | qcow2 | + | file | /v2/images/9fef3b2d-c35d-4b61-bea8-09cc6dc41829/file | + | id | 98901246-af9d-4b61-bea8-09cc6dc41829 | + | min_disk | 0 | + | min_ram | 0 | + | name | cirros-0.3.5-x86_64-uec | + | owner | 8d8ef3cdf2b54c25831cbb409ad9ae86 | + | protected | False | + | schema | /v2/schemas/image | + | size | 13287936 | + | status | active | + | tags | | + | updated_at | 2016-10-19T09:12:52Z | + | virtual_size | None | + | visibility | public | + +------------------+------------------------------------------------------+ + +#. List the available flavors. + + .. code-block:: console + + $ openstack flavor list + +-----+-----------+-------+------+-----------+-------+-----------+ + | ID | Name | RAM | Disk | Ephemeral | VCPUs | Is_Public | + +-----+-----------+-------+------+-----------+-------+-----------+ + | 1 | m1.tiny | 512 | 1 | 0 | 1 | True | + | 2 | m1.small | 2048 | 20 | 0 | 1 | True | + | 3 | m1.medium | 4096 | 40 | 0 | 2 | True | + | 4 | m1.large | 8192 | 80 | 0 | 4 | True | + | 5 | m1.xlarge | 16384 | 160 | 0 | 8 | True | + +-----+-----------+-------+------+-----------+-------+-----------+ + + Note the ID of the flavor that you want to use to create a volume. + +#. To create a bootable volume from an image and launch an instance from + this volume, use the ``--block-device`` parameter. + + For example: + + .. code-block:: console + + $ openstack server create --flavor FLAVOR --block-device \ + source=SOURCE,id=ID,dest=DEST,size=SIZE,shutdown=PRESERVE,bootindex=INDEX \ + NAME + + The parameters are: + + - ``--flavor`` + The flavor ID or name. + + - ``--block-device`` + source=SOURCE,id=ID,dest=DEST,size=SIZE,shutdown=PRESERVE,bootindex=INDEX + + **source=SOURCE** + The type of object used to create the block device. Valid values + are ``volume``, ``snapshot``, ``image``, and ``blank``. + + **id=ID** + The ID of the source object. + + **dest=DEST** + The type of the target virtual device. Valid values are ``volume`` + and ``local``. + + **size=SIZE** + The size of the volume that is created. + + **shutdown={preserve\|remove}** + What to do with the volume when the instance is deleted. + ``preserve`` does not delete the volume. ``remove`` deletes the + volume. + + **bootindex=INDEX** + Orders the boot disks. Use ``0`` to boot from this volume. + + - ``NAME``. The name for the server. + +#. Create a bootable volume from an image. Cinder makes a volume bootable + when ``--image`` parameter is passed. + + .. code-block:: console + + $ openstack volume create --image IMAGE_ID --size SIZE_IN_GB bootable_volume + +#. Create a VM from previously created bootable volume. The volume is not + deleted when the instance is terminated. + + .. code-block:: console + + $ openstack server create --flavor 2 --volume VOLUME_ID \ + --block-device source=volume,id=$VOLUME_ID,dest=volume,size=10,shutdown=preserve,bootindex=0 \ + myInstanceFromVolume + +--------------------------------------+--------------------------------+ + | Field | Value | + +--------------------------------------+--------------------------------+ + | OS-EXT-STS:task_state | scheduling | + | image | Attempt to boot from volume | + | | - no image supplied | + | OS-EXT-STS:vm_state | building | + | OS-EXT-SRV-ATTR:instance_name | instance-00000003 | + | OS-SRV-USG:launched_at | None | + | flavor | m1.small | + | id | 2e65c854-dba9-4f68-8f08-fe3... | + | security_groups | [{u'name': u'default'}] | + | user_id | 352b37f5c89144d4ad053413926... | + | OS-DCF:diskConfig | MANUAL | + | accessIPv4 | | + | accessIPv6 | | + | progress | 0 | + | OS-EXT-STS:power_state | 0 | + | OS-EXT-AZ:availability_zone | nova | + | config_drive | | + | status | BUILD | + | updated | 2014-02-02T13:29:54Z | + | hostId | | + | OS-EXT-SRV-ATTR:host | None | + | OS-SRV-USG:terminated_at | None | + | key_name | None | + | OS-EXT-SRV-ATTR:hypervisor_hostname | None | + | name | myInstanceFromVolume | + | adminPass | TzjqyGsRcJo9 | + | tenant_id | f7ac731cc11f40efbc03a9f9e1d... | + | created | 2014-02-02T13:29:53Z | + | os-extended-volumes:volumes_attached | [{"id": "2fff50ab..."}] | + | metadata | {} | + +--------------------------------------+--------------------------------+ + +#. List volumes to see the bootable volume and its attached + ``myInstanceFromVolume`` instance. + + .. code-block:: console + + $ openstack volume list + +---------------------+-----------------+--------+------+---------------------------------+ + | ID | Display Name | Status | Size | Attached to | + +---------------------+-----------------+--------+------+---------------------------------+ + | c612f739-8592-44c4- | bootable_volume | in-use | 10 | Attached to myInstanceFromVolume| + | b7d4-0fee2fe1da0c | | | | on /dev/vda | + +---------------------+-----------------+--------+------+---------------------------------+ + +.. _Attach_swap_or_ephemeral_disk_to_an_instance: + +Attach swap or ephemeral disk to an instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``nova boot`` ``--swap`` parameter to attach a swap disk on boot +or the ``nova boot`` ``--ephemeral`` parameter to attach an ephemeral +disk on boot. When you terminate the instance, both disks are deleted. + +Boot an instance with a 512 MB swap disk and 2 GB ephemeral disk. + +.. code-block:: console + + $ nova boot --flavor FLAVOR --image IMAGE_ID --swap 512 \ + --ephemeral size=2 NAME + +.. note:: + + The flavor defines the maximum swap and ephemeral disk size. You + cannot exceed these maximum values. diff --git a/doc/source/user/launch-instance-using-ISO-image.rst b/doc/source/user/launch-instance-using-ISO-image.rst new file mode 100644 index 00000000000..2e018b7c0fe --- /dev/null +++ b/doc/source/user/launch-instance-using-ISO-image.rst @@ -0,0 +1,147 @@ +================================== +Launch an instance using ISO image +================================== + +.. _Boot_instance_from_ISO_image: + +Boot an instance from an ISO image +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +OpenStack supports booting instances using ISO images. But before you +make such instances functional, use the :command:`openstack server create` +command with the following parameters to boot an instance: + +.. code-block:: console + + $ openstack server create --image ubuntu-14.04.2-server-amd64.iso \ + --nic net-id = NETWORK_UUID \ + --flavor 2 INSTANCE_NAME + +--------------------------------------+--------------------------------------------+ + | Field | Value | + +--------------------------------------+--------------------------------------------+ + | OS-DCF:diskConfig | MANUAL | + | OS-EXT-AZ:availability_zone | nova | + | OS-EXT-SRV-ATTR:host | - | + | OS-EXT-SRV-ATTR:hypervisor_hostname | - | + | OS-EXT-SRV-ATTR:instance_name | instance-00000004 | + | OS-EXT-STS:power_state | 0 | + | OS-EXT-STS:task_state | scheduling | + | OS-EXT-STS:vm_state | building | + | OS-SRV-USG:launched_at | - | + | OS-SRV-USG:terminated_at | - | + | accessIPv4 | | + | accessIPv6 | | + | adminPass | ZaiYeC8iucgU | + | config_drive | | + | created | 2015-06-01T16:34:50Z | + | flavor | m1.small (2) | + | hostId | | + | id | 1e1797f3-1662-49ff-ae8c-a77e82ee1571 | + | image | ubuntu-14.04.2-server-amd64.iso | + | key_name | - | + | metadata | {} | + | name | INSTANCE_NAME | + | os-extended-volumes:volumes_attached | [] | + | progress | 0 | + | security_groups | default | + | status | BUILD | + | tenant_id | ccef9e62b1e645df98728fb2b3076f27 | + | updated | 2014-05-09T16:34:51Z | + | user_id | fef060ae7bfd4024b3edb97dff59017a | + +--------------------------------------+--------------------------------------------+ + +In this command, ``ubuntu-14.04.2-server-amd64.iso`` is the ISO image, +and ``INSTANCE_NAME`` is the name of the new instance. ``NETWORK_UUID`` +is a valid network id in your system. + +Create a bootable volume for the instance to reside on after shutdown. + +#. Create the volume: + + .. code-block:: console + + $ openstack volume create \ + --size \ + --bootable VOLUME_NAME + +#. Attach the instance to the volume: + + .. code-block:: console + + $ openstack server add volume + INSTANCE_NAME \ + VOLUME_NAME \ + --device /dev/vda + +.. note:: + + You need the Block Storage service to preserve the instance after + shutdown. The ``--block-device`` argument, used with the + legacy :command:`nova boot`, will not work with the OpenStack + :command:`openstack server create` command. Instead, the + :command:`openstack volume create` and + :command:`openstack server add volume` commands create persistent storage. + +After the instance is successfully launched, connect to the instance +using a remote console and follow the instructions to install the +system as using ISO images on regular computers. When the installation +is finished and system is rebooted, the instance asks you again to +install the operating system, which means your instance is not usable. +If you have problems with image creation, please check the +`Virtual Machine Image Guide +`_ +for reference. + +.. _Make_instance_booted_from_ISO_image_functional: + +Make the instances booted from ISO image functional +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now complete the following steps to make your instances created +using ISO image actually functional. + +#. Delete the instance using the following command. + + .. code-block:: console + + $ openstack server delete INSTANCE_NAME + +#. After you delete the instance, the system you have just installed + using your ISO image remains, because the parameter + ``shutdown=preserve`` was set, so run the following command. + + .. code-block:: console + + $ openstack volume list + +--------------------------+-------------------------+-----------+------+-------------+ + | ID | Display Name | Status | Size | Attached to | + +--------------------------+-------------------------+-----------+------+-------------+ + | 8edd7c97-1276-47a5-9563- |dc01d873-d0f1-40b6-bfcc- | available | 10 | | + | 1025f4264e4f | 26a8d955a1d9-blank-vol | | | | + +--------------------------+-------------------------+-----------+------+-------------+ + + You get a list with all the volumes in your system. In this list, + you can find the volume that is attached to your ISO created + instance, with the false bootable property. + +#. Upload the volume to glance. + + .. code-block:: console + + $ openstack image create --volume SOURCE_VOLUME IMAGE_NAME + $ openstack image list + +-------------------+------------+--------+ + | ID | Name | Status | + +-------------------+------------+--------+ + | 74303284-f802-... | IMAGE_NAME | active | + +-------------------+------------+--------+ + + The ``SOURCE_VOLUME`` is the UUID or a name of the volume that is attached to + your ISO created instance, and the ``IMAGE_NAME`` is the name that + you give to your new image. + +#. After the image is successfully uploaded, you can use the new + image to boot instances. + + The instances launched using this image contain the system that + you have just installed using the ISO image. diff --git a/doc/source/user/launch-instances.rst b/doc/source/user/launch-instances.rst new file mode 100644 index 00000000000..15f4786cfa6 --- /dev/null +++ b/doc/source/user/launch-instances.rst @@ -0,0 +1,168 @@ +================ +Launch instances +================ + +Instances are virtual machines that run inside the cloud. + +Before you can launch an instance, gather the following parameters: + +- The **instance source** can be an image, snapshot, or block storage + volume that contains an image or snapshot. + +- A **name** for your instance. + +- The **flavor** for your instance, which defines the compute, memory, + and storage capacity of nova computing instances. A flavor is an + available hardware configuration for a server. It defines the size of + a virtual server that can be launched. + +- Any **user data** files. A user data file is a special key in the + metadata service that holds a file that cloud-aware applications in + the guest instance can access. For example, one application that uses + user data is the + `cloud-init `__ system, + which is an open-source package from Ubuntu that is available on + various Linux distributions and that handles early initialization of + a cloud instance. + +- Access and security credentials, which include one or both of the + following credentials: + + - A **key pair** for your instance, which are SSH credentials that + are injected into images when they are launched. For the key pair + to be successfully injected, the image must contain the + ``cloud-init`` package. Create at least one key pair for each + project. If you already have generated a key pair with an external + tool, you can import it into OpenStack. You can use the key pair + for multiple instances that belong to that project. + + - A **security group** that defines which incoming network traffic + is forwarded to instances. Security groups hold a set of firewall + policies, known as *security group rules*. + +- If needed, you can assign a **floating (public) IP address** to a + running instance. + +- You can also attach a block storage device, or **volume**, for + persistent storage. + +.. note:: + + Instances that use the default security group cannot, by default, be + accessed from any IP address outside of the cloud. If you want those + IP addresses to access the instances, you must modify the rules for + the default security group. + + You can also assign a floating IP address to a running instance to + make it accessible from outside the cloud. See + :doc:`manage-ip-addresses`. + +After you gather the parameters that you need to launch an instance, +you can launch it from an :doc:`image` +or a :doc:`volume`. You can launch an +instance directly from one of the available OpenStack images or from +an image that you have copied to a persistent volume. The OpenStack +Image service provides a pool of images that are accessible to members +of different projects. + +Gather parameters to launch an instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before you begin, source the OpenStack RC file. + +#. Create a flavor. + + .. code-block:: console + + $ openstack flavor create --ram 512 --disk 1 --vcpus 1 m1.tiny + +#. List the available flavors. + + .. code-block:: console + + $ openstack flavor list + + Note the ID of the flavor that you want to use for your instance:: + + +-----+-----------+-------+------+-----------+-------+-----------+ + | ID | Name | RAM | Disk | Ephemeral | VCPUs | Is_Public | + +-----+-----------+-------+------+-----------+-------+-----------+ + | 1 | m1.tiny | 512 | 1 | 0 | 1 | True | + | 2 | m1.small | 2048 | 20 | 0 | 1 | True | + | 3 | m1.medium | 4096 | 40 | 0 | 2 | True | + | 4 | m1.large | 8192 | 80 | 0 | 4 | True | + | 5 | m1.xlarge | 16384 | 160 | 0 | 8 | True | + +-----+-----------+-------+------+-----------+-------+-----------+ + +#. List the available images. + + .. code-block:: console + + $ openstack image list + + Note the ID of the image from which you want to boot your instance:: + + +--------------------------------------+---------------------------------+--------+ + | ID | Name | Status | + +--------------------------------------+---------------------------------+--------+ + | 397e713c-b95b-4186-ad46-6126863ea0a9 | cirros-0.3.5-x86_64-uec | active | + | df430cc2-3406-4061-b635-a51c16e488ac | cirros-0.3.5-x86_64-uec-kernel | active | + | 3cf852bd-2332-48f4-9ae4-7d926d50945e | cirros-0.3.5-x86_64-uec-ramdisk | active | + +--------------------------------------+---------------------------------+--------+ + + You can also filter the image list by using :command:`grep` to find a specific + image, as follows: + + .. code-block:: console + + $ openstack image list | grep 'kernel' + + | df430cc2-3406-4061-b635-a51c16e488ac | cirros-0.3.5-x86_64-uec-kernel | active | + +#. List the available security groups. + + .. code-block:: console + + $ openstack security group list + + .. note:: + + If you are an admin user, this command will list groups for all tenants. + + Note the ID of the security group that you want to use for your instance:: + + +--------------------------------------+---------+------------------------+----------------------------------+ + | ID | Name | Description | Project | + +--------------------------------------+---------+------------------------+----------------------------------+ + | b0d78827-0981-45ef-8561-93aee39bbd9f | default | Default security group | 5669caad86a04256994cdf755df4d3c1 | + | ec02e79e-83e1-48a5-86ad-14ab9a8c375f | default | Default security group | 1eaaf6ede7a24e78859591444abf314a | + +--------------------------------------+---------+------------------------+----------------------------------+ + + If you have not created any security groups, you can assign the instance + to only the default security group. + + You can view rules for a specified security group: + + .. code-block:: console + + $ openstack security group rule list default + +#. List the available key pairs, and note the key pair name that you use for + SSH access. + + .. code-block:: console + + $ openstack keypair list + +Launch an instance +~~~~~~~~~~~~~~~~~~ + +You can launch an instance from various sources. + +.. toctree:: + :maxdepth: 2 + + launch-instance-from-image.rst + launch-instance-from-volume.rst + launch-instance-using-ISO-image.rst + diff --git a/doc/source/user/manage-ip-addresses.rst b/doc/source/user/manage-ip-addresses.rst new file mode 100644 index 00000000000..97732fb07a3 --- /dev/null +++ b/doc/source/user/manage-ip-addresses.rst @@ -0,0 +1,170 @@ +=================== +Manage IP addresses +=================== + +.. todo:: This needs quite a bit of work since nova-network has been + deprecated since Newton, and the network resource related compute + APIs which are for nova-network or that proxy to neutron have been + deprecated, along with their respective CLIs in python-novaclient. + +Each instance has a private, fixed IP address and can also have a +public, or floating IP address. Private IP addresses are used for +communication between instances, and public addresses are used for +communication with networks outside the cloud, including the Internet. + +When you launch an instance, it is automatically assigned a private IP +address that stays the same until you explicitly terminate the instance. +Rebooting an instance has no effect on the private IP address. + +A pool of floating IP addresses, configured by the cloud administrator, +is available in OpenStack Compute. The project quota defines the maximum +number of floating IP addresses that you can allocate to the project. +After you allocate a floating IP address to a project, you can: + +- Associate the floating IP address with an instance of the project. Only one + floating IP address can be allocated to an instance at any given time. + +- Disassociate a floating IP address from an instance in the project. + +- Delete a floating IP from the project which automatically deletes that IP's + associations. + +Use the :command:`openstack` commands to manage floating IP addresses. + +List floating IP address information +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To list all pools that provide floating IP addresses, run: + +.. code-block:: console + + $ openstack floating ip pool list + +--------+ + | name | + +--------+ + | public | + | test | + +--------+ + +.. note:: + + If this list is empty, the cloud administrator must configure a pool + of floating IP addresses. + +To list all floating IP addresses that are allocated to the current project, +run: + +.. code-block:: console + + $ openstack floating ip list + +--------------------------------------+---------------------+------------------+------+ + | ID | Floating IP Address | Fixed IP Address | Port | + +--------------------------------------+---------------------+------------------+------+ + | 760963b2-779c-4a49-a50d-f073c1ca5b9e | 172.24.4.228 | None | None | + | 89532684-13e1-4af3-bd79-f434c9920cc3 | 172.24.4.235 | None | None | + | ea3ebc6d-a146-47cd-aaa8-35f06e1e8c3d | 172.24.4.229 | None | None | + +--------------------------------------+---------------------+------------------+------+ + +For each floating IP address that is allocated to the current project, +the command outputs the floating IP address, the ID for the instance +to which the floating IP address is assigned, the associated fixed IP +address, and the pool from which the floating IP address was +allocated. + +Associate floating IP addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can assign a floating IP address to a project and to an instance. + +#. Run the following command to allocate a floating IP address to the + current project. By default, the floating IP address is allocated from + the public pool. The command outputs the allocated IP address: + + .. code-block:: console + + $ openstack floating ip create public + +---------------------+--------------------------------------+ + | Field | Value | + +---------------------+--------------------------------------+ + | created_at | 2016-11-30T15:02:05Z | + | description | | + | fixed_ip_address | None | + | floating_ip_address | 172.24.4.236 | + | floating_network_id | 0bf90de6-fc0f-4dba-b80d-96670dfb331a | + | headers | | + | id | c70ad74b-2f64-4e60-965e-f24fc12b3194 | + | port_id | None | + | project_id | 5669caad86a04256994cdf755df4d3c1 | + | project_id | 5669caad86a04256994cdf755df4d3c1 | + | revision_number | 1 | + | router_id | None | + | status | DOWN | + | updated_at | 2016-11-30T15:02:05Z | + +---------------------+--------------------------------------+ + +#. List all project instances with which a floating IP address could be + associated. + + .. code-block:: console + + $ openstack server list + +---------------------+------+---------+------------+-------------+------------------+------------+ + | ID | Name | Status | Task State | Power State | Networks | Image Name | + +---------------------+------+---------+------------+-------------+------------------+------------+ + | d5c854f9-d3e5-4f... | VM1 | ACTIVE | - | Running | private=10.0.0.3 | cirros | + | 42290b01-0968-43... | VM2 | SHUTOFF | - | Shutdown | private=10.0.0.4 | centos | + +---------------------+------+---------+------------+-------------+------------------+------------+ + +#. Associate an IP address with an instance in the project, as follows: + + .. code-block:: console + + $ openstack server add floating ip INSTANCE_NAME_OR_ID FLOATING_IP_ADDRESS + + For example: + + .. code-block:: console + + $ openstack server add floating ip VM1 172.24.4.225 + + The instance is now associated with two IP addresses: + + .. code-block:: console + + $ openstack server list + +------------------+------+--------+------------+-------------+-------------------------------+------------+ + | ID | Name | Status | Task State | Power State | Networks | Image Name | + +------------------+------+--------+------------+-------------+-------------------------------+------------+ + | d5c854f9-d3e5... | VM1 | ACTIVE | - | Running | private=10.0.0.3, 172.24.4.225| cirros | + | 42290b01-0968... | VM2 | SHUTOFF| - | Shutdown | private=10.0.0.4 | centos | + +------------------+------+--------+------------+-------------+-------------------------------+------------+ + + After you associate the IP address and configure security group rules + for the instance, the instance is publicly available at the floating IP + address. + + .. note:: + + The :command:`openstack server` command does not allow users to associate a + floating IP address with a specific fixed IP address using the optional + ``--fixed-address`` parameter, which legacy commands required as an + argument. + +Disassociate floating IP addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To disassociate a floating IP address from an instance: + +.. code-block:: console + + $ openstack server remove floating ip INSTANCE_NAME_OR_ID FLOATING_IP_ADDRESS + +To remove the floating IP address from a project: + +.. code-block:: console + + $ openstack floating ip delete FLOATING_IP_ADDRESS + +The IP address is returned to the pool of IP addresses that is available +for all projects. If the IP address is still associated with a running +instance, it is automatically disassociated from that instance. diff --git a/doc/source/user/placement.rst b/doc/source/user/placement.rst new file mode 100644 index 00000000000..21f327579da --- /dev/null +++ b/doc/source/user/placement.rst @@ -0,0 +1,248 @@ +.. + 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. + +=============== + Placement API +=============== + +Overview +======== + +Nova introduced the placement API service in the 14.0.0 Newton release. This +is a separate REST API stack and data model used to track resource provider +inventories and usages, along with different classes of resources. For example, +a resource provider can be a compute node, a shared storage pool, or an IP +allocation pool. The placement service tracks the inventory and usage of each +provider. For example, an instance created on a compute node may be a consumer +of resources such as RAM and CPU from a compute node resource provider, disk +from an external shared storage pool resource provider and IP addresses from +an external IP pool resource provider. + +The types of resources consumed are tracked as **classes**. The service +provides a set of standard resource classes (for example `DISK_GB`, +`MEMORY_MB`, and `VCPU`) and provides the ability to define custom resource +classes as needed. + +Each resource provider may also have a set of traits which describe qualitative +aspects of the resource provider. Traits describe an aspect of a resource +provider that cannot itself be consumed but a workload may wish to specify. For +example, available disk may be solid state drives (SSD). + +References +~~~~~~~~~~ + +The following specifications represent the stages of design and development of +resource providers and the Placement service. Implementation details may have +changed or be partially complete at this time. + +* `Generic Resource Pools `_ +* `Compute Node Inventory `_ +* `Resource Provider Allocations `_ +* `Resource Provider Base Models `_ +* `Nested Resource Providers `_ +* `Custom Resource Classes `_ +* `Scheduler Filters in DB `_ +* `Scheduler claiming resources to the Placement API `_ +* `The Traits API - Manage Traits with ResourceProvider `_ + +Deployment +========== + +The placement-api service must be deployed at some point after you have +upgraded to the 14.0.0 Newton release but before you can upgrade to the 15.0.0 +Ocata release. This is so that the resource tracker in the nova-compute service +can populate resource provider (compute node) inventory and allocation +information which will be used by the nova-scheduler service in Ocata. + +Steps +~~~~~ + +**1. Deploy the API service** + +At this time the placement API code is still in Nova alongside the compute +REST API code (nova-api). So once you have upgraded nova-api to Newton you +already have the placement API code, you just need to install the service. +Nova provides a ``nova-placement-api`` WSGI script for running the service +with Apache. + +.. note:: The placement API service is currently developed within Nova but + it is designed to be as separate as possible from the existing code so + that it can eventually be split into a separate project. + +**2. Synchronize the database** + +In the Newton release the Nova **api** database is the only deployment +option for the placement API service and the resources it manages. After +upgrading the nova-api service for Newton and running the +``nova-manage api_db sync`` command the placement tables will be created. + +.. note:: There are plans to add the ability to run the placement service + with a separate **placement** database that would just have the tables + necessary for that service and not everything else that goes into the + Nova **api** database. + +**3. Create accounts and update the service catalog** + +Create a **placement** service user with an **admin** role in Keystone. + +The placement API is a separate service and thus should be registered under +a **placement** service type in the service catalog as that is what the +resource tracker in the nova-compute node will use to look up the endpoint. + +Devstack sets up the placement service on the default HTTP port (80) with a +``/placement`` prefix instead of using an independent port. + +**4. Configure and restart nova-compute services** + +The 14.0.0 Newton nova-compute service code will begin reporting resource +provider inventory and usage information as soon as the placement API +service is in place and can respond to requests via the endpoint registered +in the service catalog. + +``nova.conf`` on the compute nodes must be updated in the ``[placement]`` +group to contain credentials for making requests from nova-compute to the +placement-api service. + +.. note:: After upgrading nova-compute code to Newton and restarting the + service, the nova-compute service will attempt to make a connection + to the placement API and if that is not yet available a warning will + be logged. The nova-compute service will keep attempting to connect + to the placement API, warning periodically on error until it is + successful. Keep in mind that Placement is optional in Newton, but + required in Ocata, so the placement service should be enabled before + upgrading to Ocata. nova.conf on the compute nodes will need to be + updated in the ``[placement]`` group for credentials to make requests + from nova-compute to the placement-api service. + +References +~~~~~~~~~~ + +The following changes were made to devstack (from oldest to newest) to enable +the placement-api service and can serve as a guide for your own deployment. + +https://review.openstack.org/#/c/342362/ + +https://review.openstack.org/#/c/363335/ + +https://review.openstack.org/#/c/363724/ + + +Upgrade Notes +============= + +The follow sub-sections provide notes on upgrading to a given target release. + +.. note:: + + As a reminder, the :doc:`nova-status upgrade check ` tool + can be used to help determine the status of your deployment and how ready it + is to perform an upgrade. + +Ocata (15.0.0) +~~~~~~~~~~~~~~ + +* The ``nova-compute`` service will fail to start in Ocata unless the + ``[placement]`` section of nova.conf on the compute is configured. As + mentioned in the deployment steps above, the Placement service should be + deployed by this point so the computes can register and start reporting + inventory and allocation information. If the computes are deployed + and configured `before` the Placement service, they will continue to try + and reconnect in a loop so that you do not need to restart the nova-compute + process to talk to the Placement service after the compute is properly + configured. +* The ``nova.scheduler.filter_scheduler.FilterScheduler`` in Ocata will + fallback to not using the Placement service as long as there are older + ``nova-compute`` services running in the deployment. This allows for rolling + upgrades of the computes to not affect scheduling for the FilterScheduler. + However, the fallback mechanism will be removed in the 16.0.0 Pike release + such that the scheduler will make decisions based on the Placement service + and the resource providers (compute nodes) registered there. This means if + the computes are not reporting into Placement by Pike, build requests will + fail with **NoValidHost** errors. +* While the FilterScheduler technically depends on the Placement service + in Ocata, if you deploy the Placement service `after` you upgrade the + ``nova-scheduler`` service to Ocata and restart it, things will still work. + The scheduler will gracefully handle the absence of the Placement service. + However, once all computes are upgraded, the scheduler not being able to make + requests to Placement will result in **NoValidHost** errors. +* It is currently possible to exclude the ``CoreFilter``, ``RamFilter`` and + ``DiskFilter`` from the list of enabled FilterScheduler filters such that + scheduling decisions are not based on CPU, RAM or disk usage. Once all + computes are reporting into the Placement service, however, and the + FilterScheduler starts to use the Placement service for decisions, those + excluded filters are ignored and the scheduler will make requests based on + VCPU, MEMORY_MB and DISK_GB inventory. If you wish to effectively ignore + that type of resource for placement decisions, you will need to adjust the + corresponding ``cpu_allocation_ratio``, ``ram_allocation_ratio``, and/or + ``disk_allocation_ratio`` configuration options to be very high values, e.g. + 9999.0. +* Users of CellsV1 will need to deploy a placement per cell, matching + the scope and cardinality of the regular ``nova-scheduler`` process. + +Pike (16.0.0) +~~~~~~~~~~~~~ + +* The ``nova.scheduler.filter_scheduler.FilterScheduler`` in Pike will + no longer fall back to not using the Placement Service, even if older + computes are running in the deployment. +* The FilterScheduler now requests allocation candidates from the Placement + service during scheduling. The allocation candidates information was + introduced in the Placement API 1.10 microversion, so you should upgrade the + placement service **before** the Nova scheduler service so that the scheduler + can take advantage of the allocation candidate information. + + The scheduler gets the allocation candidates from the placement API and + uses those to get the compute nodes, which come from the cell(s). The + compute nodes are passed through the enabled scheduler filters and weighers. + The scheduler then iterates over this filtered and weighed list of hosts and + attempts to claim resources in the placement API for each instance in the + request. Claiming resources involves finding an allocation candidate that + contains an allocation against the selected host's UUID and asking the + placement API to allocate the requested instance resources. We continue + performing this claim request until success or we run out of allocation + candidates, resulting in a NoValidHost error. + + For a move operation, such as migration, allocations are made in Placement + against both the source and destination compute node. Once the + move operation is complete, the resource tracker in the *nova-compute* + service will adjust the allocations in Placement appropriately. + + For a resize to the same host, allocations are summed on the single compute + node. This could pose a problem if the compute node has limited capacity. + Since resizing to the same host is disabled by default, and generally only + used in testing, this is mentioned for completeness but should not be a + concern for production deployments. + + +REST API +======== + +The placement API service has its own `REST API`_ and data model. One +can get a sample of the REST API via the functional test `gabbits`_. + +.. _`REST API`: https://developer.openstack.org/api-ref/placement/ +.. _gabbits: http://git.openstack.org/cgit/openstack/nova/tree/nova/tests/functional/api/openstack/placement/gabbits + +Microversions +~~~~~~~~~~~~~ + +The placement API uses microversions for making incremental changes to the +API which client requests must opt into. + +It is especially important to keep in mind that nova-compute is a client of +the placement REST API and based on how Nova supports rolling upgrades the +nova-compute service could be Newton level code making requests to an Ocata +placement API, and vice-versa, an Ocata compute service in a cells v2 cell +could be making requests to a Newton placement API. + +.. include:: ../../../nova/api/openstack/placement/rest_api_version_history.rst diff --git a/doc/source/user/quotas.rst b/doc/source/user/quotas.rst new file mode 100644 index 00000000000..e1b54484af6 --- /dev/null +++ b/doc/source/user/quotas.rst @@ -0,0 +1,78 @@ +.. + 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. + +======== + Quotas +======== + +Nova uses a quota system for setting limits on resources such as number of +instances or amount of CPU that a specific project or user can use. + +Quotas are enforced by making a claim, or reservation, on resources when a +request is made, such as creating a new server. If the claim fails, the request +is rejected. If the reservation succeeds then the operation progresses until +such a point that the reservation is either converted into usage (the operation +was successful) or rolled back (the operation failed). + +Typically the quota reservation is made in the nova-api service and the usage +or rollback is performed in the nova-compute service, at least when dealing +with a server creation or move operation. + +Quota limits and usage can be retrieved via the ``limits`` REST API. + +Checking quota +============== + +When calculating limits for a given resource and tenant, the following +checks are made in order: + +* Depending on the resource, is there a tenant-specific limit on the resource + in either the `quotas` or `project_user_quotas` tables in the database? If + so, use that as the limit. You can create these resources by doing:: + + openstack quota set --instances 5 + +* Check to see if there is a hard limit for the given resource in the + `quota_classes` table in the database for the `default` quota class. If so, + use that as the limit. You can modify the default quota limit for a resource + by doing:: + + openstack quota set --class --instances 5 default + +* If the above does not provide a resource limit, then rely on the ``quota_*`` + configuration options for the default limit. + +.. note:: The API sets the limit in the `quota_classes` table. Once a default + limit is set via the `default` quota class, that takes precedence over + any changes to that resource limit in the configuration options. In other + words, once you've changed things via the API, you either have to keep those + synchronized with the configuration values or remove the default limit from + the database manually as there is no REST API for removing quota class + values from the database. + + +Known issues +============ + +TODO: talk about quotas getting out of sync and `how to recover`_ + +.. _how to recover: https://specs.openstack.org/openstack/nova-specs/specs/newton/implemented/refresh-quotas-usage.html + + +Future plans +============ + +TODO: talk about quotas in the `resource counting spec`_ and `nested quotas`_ + +.. _resource counting spec: https://specs.openstack.org/openstack/nova-specs/specs/ocata/approved/cells-count-resources-to-check-quota-in-api.html +.. _nested quotas: https://specs.openstack.org/openstack/nova-specs/specs/mitaka/approved/nested-quota-driver-api.html diff --git a/doc/source/support-matrix.ini b/doc/source/user/support-matrix.ini similarity index 82% rename from doc/source/support-matrix.ini rename to doc/source/user/support-matrix.ini index 427fe54d0df..16a7424745f 100644 --- a/doc/source/support-matrix.ini +++ b/doc/source/user/support-matrix.ini @@ -83,6 +83,7 @@ driver-impl-libvirt-vz-ct=Libvirt Virtuozzo CT driver-impl-vmware=VMware vCenter driver-impl-hyperv=Hyper-V driver-impl-ironic=Ironic +driver-impl-powervm=PowerVM [operation.attach-volume] title=Attach block volume to instance @@ -95,7 +96,7 @@ notes=The attach volume operation provides a means to hotplug hotplug extra storage is for those cases where the instance is considered to be more of a pet than cattle. Therefore this operation is not considered to be mandatory to support. -cli= +cli=nova volume-attach driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -108,12 +109,13 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [operation.detach-volume] title=Detach block volume from instance status=optional notes=See notes for attach volume operation. -cli= +cli=nova volume-detach driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -126,6 +128,83 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing + +[operation.extend-volume] +title=Extend block volume attached to instance +status=optional +notes=The extend volume operation provides a means to extend + the size of an attached volume. This allows volume size + to be expanded without interruption of service. + In a cloud model it would be more typical to just + spin up a new instance with large storage, so the ability to + extend the size of an attached volume is for those cases + where the instance is considered to be more of a pet than cattle. + Therefore this operation is not considered to be mandatory to support. +cli=cinder extend +driver-impl-xenserver=missing +driver-impl-libvirt-kvm-x86=complete +driver-impl-libvirt-kvm-ppc64=unknown +driver-impl-libvirt-kvm-s390x=unknown +driver-impl-libvirt-qemu-x86=complete +driver-impl-libvirt-lxc=missing +driver-impl-libvirt-xen=unknown +driver-impl-vmware=missing +driver-impl-hyperv=missing +driver-impl-ironic=missing +driver-impl-libvirt-vz-vm=unknown +driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing + +[operation.attach-interface] +title=Attach virtual network interface to instance +status=optional +notes=The attach interface operation provides a means to hotplug + additional interfaces to a running instance. Hotplug support + varies between guest OSes and some guests require a reboot for + new interfaces to be detected. This operation allows interface + capabilities to be expanded without interruption of service. + In a cloud model it would be more typical to just spin up a + new instance with more interfaces. +cli=nova interface-attach +driver-impl-xenserver=complete +driver-impl-libvirt-kvm-x86=complete +driver-impl-libvirt-kvm-ppc64=complete +driver-impl-libvirt-kvm-s390x=complete +driver-impl-libvirt-qemu-x86=complete +driver-impl-libvirt-lxc=missing +driver-impl-libvirt-xen=complete +driver-impl-vmware=complete +driver-impl-hyperv=partial +driver-notes-hyperv=Works without issue if instance is off. When + hotplugging, only works if using Windows/Hyper-V Server 2016 and + the instance is a Generation 2 VM. +driver-impl-ironic=complete +driver-impl-libvirt-vz-vm=complete +driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing + +[operation.detach-interface] +title=Detach virtual network interface from instance +status=optional +notes=See notes for attach-interface operation. +cli=nova interface-detach +driver-impl-xenserver=complete +driver-impl-libvirt-kvm-x86=complete +driver-impl-libvirt-kvm-ppc64=complete +driver-impl-libvirt-kvm-s390x=complete +driver-impl-libvirt-qemu-x86=complete +driver-impl-libvirt-lxc=missing +driver-impl-libvirt-xen=complete +driver-impl-vmware=complete +driver-impl-hyperv=complete +driver-notes-hyperv=Works without issue if instance is off. When + hotplugging, only works if using Windows/Hyper-V Server 2016 and + the instance is a Generation 2 VM. +driver-impl-ironic=complete +driver-impl-libvirt-vz-vm=complete +driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [operation.maintenance-mode] title=Set the host in a maintenance mode @@ -135,10 +214,9 @@ notes=This operation allows a host to be placed into maintenance instances to an alternative host and preventing new instances from being launched. This is not considered to be a mandatory operation to support. - The CLI command is "nova host-update ". The driver methods to implement are "host_maintenance_mode" and "set_host_enabled". -cli= +cli=nova host-update driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=missing driver-impl-libvirt-kvm-ppc64=missing @@ -151,6 +229,7 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [operation.evacuate] title=Evacuate instances from a host @@ -176,13 +255,37 @@ driver-impl-hyperv=unknown driver-impl-ironic=unknown driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=unknown + +[operation.rebuild] +title=Rebuild instance +status=optional +notes=A possible use case is additional attributes need to be set + to the instance, nova will purge all existing data from the system + and remakes the VM with given information such as 'metadata' and + 'personalities'. Though this is not considered to be a mandatory + operation to support. +cli=nova rebuild +driver-impl-xenserver=complete +driver-impl-libvirt-kvm-x86=complete +driver-impl-libvirt-kvm-ppc64=complete +driver-impl-libvirt-kvm-s390x=complete +driver-impl-libvirt-qemu-x86=complete +driver-impl-libvirt-lxc=complete +driver-impl-libvirt-xen=complete +driver-impl-vmware=complete +driver-impl-hyperv=complete +driver-impl-ironic=complete +driver-impl-libvirt-vz-vm=complete +driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=unknown [operation.get-guest-info] title=Guest instance status status=mandatory notes=Provides a quick report on information about the guest instance, including the power state, memory allocation, CPU allocation, number - of vCPUs and cummulative CPU execution time. As well as being + of vCPUs and cumulative CPU execution time. As well as being informational, the power state is used by the compute manager for tracking changes in guests. Therefore this operation is considered mandatory to support. @@ -199,6 +302,7 @@ driver-impl-hyperv=complete driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=complete [operation.get-host-info] title=Guest host status @@ -217,6 +321,7 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=complete [operation.live-migrate] title=Live migrate instance across hosts @@ -232,7 +337,7 @@ notes=Live migration provides a way to move an instance off one support for the live migration operation, particularly those built on the container based virtualization. Therefore this operation is not considered mandatory to support. -cli= +cli=nova live-migration ;nova host-evacuate-live driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -244,8 +349,41 @@ driver-impl-vmware=missing driver-notes-vmware=https://bugs.launchpad.net/nova/+bug/1192192 driver-impl-hyperv=complete driver-impl-ironic=missing +driver-impl-libvirt-vz-vm=complete +driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing + +[operation.force-live-migration-to-complete] +title=Force live migration to complete +status=optional +notes=Live migration provides a way to move a running instance to another + compute host. But it can sometimes fail to complete if an instance has + a high rate of memory or disk page access. + This operation provides the user with an option to assist the progress + of the live migration. The mechanism used to complete the live + migration depends on the underlying virtualization subsystem + capabilities. If libvirt/qemu is used and the post-copy feature is + available and enabled then the force complete operation will cause + a switch to post-copy mode. Otherwise the instance will be suspended + until the migration is completed or aborted. +cli=nova live-migration-force-complete +driver-impl-xenserver=missing +driver-impl-libvirt-kvm-x86=complete +driver-notes-libvirt-kvm-x86=Requires libvirt>=1.3.3, qemu>=2.5.0 +driver-impl-libvirt-kvm-ppc64=complete +driver-notes-libvirt-kvm-ppc64=Requires libvirt>=1.3.3, qemu>=2.5.0 +driver-impl-libvirt-kvm-s390x=complete +driver-notes-libvirt-kvm-s390x=Requires libvirt>=1.3.3, qemu>=2.5.0 +driver-impl-libvirt-qemu-x86=complete +driver-notes-libvirt-qemu-x86=Requires libvirt>=1.3.3, qemu>=2.5.0 +driver-impl-libvirt-lxc=missing +driver-impl-libvirt-xen=missing +driver-impl-vmware=missing +driver-impl-hyperv=missing +driver-impl-ironic=missing driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [operation.launch] title=Launch instance @@ -266,6 +404,7 @@ driver-impl-hyperv=complete driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=complete [operation.pause] title=Stop instance CPUs (pause) @@ -279,7 +418,7 @@ notes=Stopping an instances CPUs can be thought of as roughly generally discourage use of this feature and some do not even implement it. Therefore this operation is considered optional to support in drivers. -cli= +cli=nova pause driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -292,6 +431,7 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [operation.reboot] title=Reboot instance @@ -301,7 +441,7 @@ notes=It is reasonable for a guest OS administrator to trigger a graceful reboot requires guest co-operation and a non-graceful reboot can be achieved by a combination of stop+start. Therefore this operation is considered optional. -cli= +cli=nova reboot driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -314,6 +454,7 @@ driver-impl-hyperv=complete driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=complete [operation.rescue] title=Rescue instance @@ -326,7 +467,7 @@ notes=The rescue operation starts an instance in a special an instance breaks the general expectation is that it be thrown away and a new instance created. Therefore this operation is considered optional to support in drivers. -cli= +cli=nova rescue driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -335,10 +476,11 @@ driver-impl-libvirt-qemu-x86=complete driver-impl-libvirt-lxc=missing driver-impl-libvirt-xen=complete driver-impl-vmware=complete -driver-impl-hyperv=missing +driver-impl-hyperv=complete driver-impl-ironic=missing -driver-impl-libvirt-vz-vm=missing -driver-impl-libvirt-vz-ct=missing +driver-impl-libvirt-vz-vm=complete +driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [operation.resize] title=Resize instance @@ -351,7 +493,7 @@ notes=The resize operation allows the user to change a running support the alteration of all relevant config settings for a running instance. Therefore this operation is considered optional to support in drivers. -cli= +cli=nova resize driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -361,16 +503,18 @@ driver-impl-libvirt-lxc=missing driver-impl-libvirt-xen=complete driver-impl-vmware=complete driver-impl-hyperv=complete -driver-impl-ironic=partial -driver-notes-ironic=Only certain ironic drivers support this -driver-impl-libvirt-vz-vm=missing -driver-impl-libvirt-vz-ct=missing +driver-impl-ironic=missing +driver-impl-libvirt-vz-vm=complete +driver-notes-vz-vm=Resizing Virtuozzo instances implies guest filesystem resize also +driver-impl-libvirt-vz-ct=complete +driver-notes-vz-ct=Resizing Virtuozzo instances implies guest filesystem resize also +driver-impl-powervm=unknown [operation.resume] title=Restore instance status=optional notes=See notes for the suspend operation -cli= +cli=nova resume driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -383,32 +527,12 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete - -[operation.service-control.wtf.com] -title=Service control -status=optional -notes=Something something, dark side, something something. - Hard to claim this is mandatory when no one seems to know - what "Service control" refers to in the context of virt - drivers. -cli= -driver-impl-xenserver=complete -driver-impl-libvirt-kvm-x86=complete -driver-impl-libvirt-kvm-ppc64=complete -driver-impl-libvirt-kvm-s390x=complete -driver-impl-libvirt-qemu-x86=complete -driver-impl-libvirt-lxc=missing -driver-impl-libvirt-xen=missing -driver-impl-vmware=complete -driver-impl-hyperv=missing -driver-impl-ironic=missing -driver-impl-libvirt-vz-vm=missing -driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [operation.set-admin-password] title=Set instance admin password status=optional -notes=Provides a mechanism to re(set) the password of the administrator +notes=Provides a mechanism to (re)set the password of the administrator account inside the instance operating system. This requires that the hypervisor has a way to communicate with the running guest operating system. Given the wide range of operating systems in existence it is @@ -419,8 +543,9 @@ notes=Provides a mechanism to re(set) the password of the administrator login to the guest and change the password in the normal manner, so this is just a convenient optimization. Therefore this operation is not considered mandatory for drivers to support. -cli= +cli=nova set-password driver-impl-xenserver=complete +driver-notes-xenserver=Requires XenAPI agent on the guest. driver-impl-libvirt-kvm-x86=complete driver-notes-libvirt-kvm-x86=Requires libvirt>=1.2.16 and hw_qemu_guest_agent. driver-impl-libvirt-kvm-ppc64=missing @@ -432,8 +557,11 @@ driver-impl-libvirt-xen=missing driver-impl-vmware=missing driver-impl-hyperv=missing driver-impl-ironic=missing -driver-impl-libvirt-vz-vm=missing -driver-impl-libvirt-vz-ct=missing +driver-impl-libvirt-vz-vm=complete +driver-notes-libvirt-vz-vm=Requires libvirt>=2.0.0 +driver-impl-libvirt-vz-ct=complete +driver-notes-libvirt-vz-ct=Requires libvirt>=2.0.0 +driver-impl-powervm=missing [operation.snapshot] title=Save snapshot of instance disk @@ -448,7 +576,7 @@ notes=The snapshot operation allows the current state of the that the root disks are ephemeral so the ability to take a snapshot cannot be assumed. Therefore this operation is not considered mandatory to support. -cli= +cli=nova image-create driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -462,6 +590,7 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [operation.suspend] title=Suspend instance @@ -481,7 +610,7 @@ notes=Suspending an instance can be thought of as roughly since with cattle it would be simpler to just terminate the instance instead of suspending. Therefore this operation is considered optional to support. -cli= +cli=nova suspend driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -494,18 +623,20 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [operation.swap-volume] title=Swap block volumes status=optional -notes=The swap volume operation is a mechanism for changing running +notes=The swap volume operation is a mechanism for changing a running instance so that its attached volume(s) are backed by different storage in the host. An alternative to this would be to simply terminate the existing instance and spawn a new instance with the new storage. In other words this operation is primarily targeted towards - the pet use case rather than cattle. Therefore this is considered - optional to support. -cli= + the pet use case rather than cattle, however, it is required for volume + migration to work in the volume service. This is considered optional to + support. +cli=nova volume-update driver-impl-xenserver=missing driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -518,6 +649,7 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [operation.terminate] title=Shutdown instance @@ -526,7 +658,7 @@ notes=The ability to terminate a virtual machine is required in order for a cloud user to stop utilizing resources and thus avoid indefinitely ongoing billing. Therefore this operation is mandatory to support in drivers. -cli= +cli=nova delete driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -543,6 +675,7 @@ driver-impl-hyperv=complete driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=complete [operation.trigger-crash-dump] title=Trigger crash dump @@ -552,7 +685,7 @@ notes=The trigger crash dump operation is a mechanism for triggering injecting an NMI (Non-maskable Interrupt) into the instance. It provides a means to dump the production memory image as a dump file which is useful for users. Therefore this operation is considered optional to support. -cli= +cli=nova trigger-crash-dump driver-impl-xenserver=missing driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -562,15 +695,16 @@ driver-impl-libvirt-lxc=missing driver-impl-libvirt-xen=missing driver-impl-vmware=missing driver-impl-hyperv=missing -driver-impl-ironic=missing +driver-impl-ironic=complete driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [operation.unpause] title=Resume instance CPUs (unpause) status=optional notes=See notes for the "Stop instance CPUs" operation -cli= +cli=nova unpause driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -583,6 +717,7 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [guest.disk.autoconfigure.wtf.com] title=Auto configure disk @@ -602,6 +737,7 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [guest.disk.rate-limit] title=Instance disk I/O limits @@ -612,7 +748,7 @@ notes=The ability to set rate limits on virtual disks allows for operations to the hypervisor with its default settings, instead of doing fine grained tuning. Therefore this is not considered to be an mandatory configuration to support. -cli= +cli=nova limits driver-impl-xenserver=missing driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -625,6 +761,7 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [guest.setup.configdrive] title=Config drive support @@ -634,7 +771,7 @@ notes=The config drive provides an information channel into administrator password, file injection, registration of SSH keys, etc. Since cloud images typically ship with all login methods locked, a mechanism to set the administrator - password of keys is required to get login access. Alternatives + password or keys is required to get login access. Alternatives include the metadata service and disk injection. At least one of the guest setup mechanisms is required to be supported by drivers, in order to enable login access. @@ -651,6 +788,7 @@ driver-impl-hyperv=complete driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [guest.setup.inject.file] title=Inject files into disk image @@ -677,6 +815,7 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [guest.setup.inject.networking] title=Inject guest networking config @@ -688,7 +827,7 @@ notes=This allows for static networking configuration (IP configured in the guest OS which is a non-trivial problem considering the vast number of operating system types. The problem of configuring networking is better solved by DHCP - or by obtaining static config via the metadata service or + or by obtaining static config via config drive. Therefore this operation is considered optional to support. cli= @@ -708,6 +847,7 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [console.rdp] title=Remote desktop over RDP @@ -720,7 +860,7 @@ notes=This allows the administrator to interact with the graphical easier consumption. Therefore support for this operation is not mandatory, however, a driver is required to support at least one of the listed console access operations. -cli= +cli=nova get-rdp-console driver-impl-xenserver=missing driver-impl-libvirt-kvm-x86=missing driver-impl-libvirt-kvm-ppc64=missing @@ -733,6 +873,7 @@ driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [console.serial.log] title=View serial console logs @@ -746,7 +887,7 @@ notes=This allows the administrator to query the logs of data only support graphical consoles. Therefore support for this operation is not mandatory, however, a driver is required to support at least one of the listed console access operations. -cli= +cli=nova console-log driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=missing @@ -754,11 +895,12 @@ driver-impl-libvirt-kvm-s390x=complete driver-impl-libvirt-qemu-x86=complete driver-impl-libvirt-lxc=missing driver-impl-libvirt-xen=complete -driver-impl-vmware=complete +driver-impl-vmware=missing driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [console.serial.interactive] title=Remote interactive serial console @@ -782,12 +924,11 @@ driver-impl-libvirt-qemu-x86=unknown driver-impl-libvirt-lxc=unknown driver-impl-libvirt-xen=unknown driver-impl-vmware=missing -driver-impl-hyperv=missing -driver-notes-hyperv=Will be complete when this review is merged: - https://review.openstack.org/#/c/145004/ -driver-impl-ironic=missing +driver-impl-hyperv=complete +driver-impl-ironic=complete driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [console.spice] title=Remote desktop over SPICE @@ -800,7 +941,7 @@ notes=This allows the administrator to interact with the graphical easier consumption. Therefore support for this operation is not mandatory, however, a driver is required to support at least one of the listed console access operations. -cli= +cli=nova get-spice-console driver-impl-xenserver=missing driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=missing @@ -813,6 +954,7 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [console.vnc] title=Remote desktop over VNC @@ -825,7 +967,7 @@ notes=This allows the administrator to interact with the graphical easier consumption. Therefore support for this operation is not mandatory, however, a driver is required to support at least one of the listed console access operations. -cli= +cli=nova get-vnc-console driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=missing @@ -838,6 +980,7 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=complete [storage.block] title=Block storage support @@ -847,7 +990,7 @@ notes=Block storage provides instances with direct attached As an alternative to direct attached disks, an instance may choose to use network based persistent storage. OpenStack provides object storage via the Swift service, or a traditional filesystem - such as as NFS/GlusterFS may be used. Some types of instances may + such as NFS may be used. Some types of instances may not require persistent storage at all, being simple transaction processing systems reading requests & sending results to and from the network. Therefore support for this configuration is not @@ -862,9 +1005,10 @@ driver-impl-libvirt-lxc=complete driver-impl-libvirt-xen=complete driver-impl-vmware=complete driver-impl-hyperv=complete -driver-impl-ironic=missing +driver-impl-ironic=complete driver-impl-libvirt-vz-vm=partial driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [storage.block.backend.fibrechannel] title=Block storage over fibre channel @@ -882,10 +1026,11 @@ driver-impl-libvirt-qemu-x86=complete driver-impl-libvirt-lxc=complete driver-impl-libvirt-xen=complete driver-impl-vmware=missing -driver-impl-hyperv=missing +driver-impl-hyperv=complete driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [storage.block.backend.iscsi] title=Block storage over iSCSI @@ -907,9 +1052,10 @@ driver-impl-libvirt-lxc=complete driver-impl-libvirt-xen=complete driver-impl-vmware=complete driver-impl-hyperv=complete -driver-impl-ironic=missing +driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [storage.block.backend.iscsi.auth.chap] title=CHAP authentication for iSCSI @@ -926,11 +1072,12 @@ driver-impl-libvirt-kvm-s390x=complete driver-impl-libvirt-qemu-x86=complete driver-impl-libvirt-lxc=complete driver-impl-libvirt-xen=complete -driver-impl-vmware=complete +driver-impl-vmware=missing driver-impl-hyperv=complete -driver-impl-ironic=missing +driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing [storage.image] title=Image storage support @@ -941,7 +1088,7 @@ notes=This refers to the ability to boot an instance from an image there would be no way to get block volumes populated and reliance on external PXE servers is out of scope. Therefore this is considered a mandatory storage feature to support. -cli= +cli=nova boot --image driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete driver-impl-libvirt-kvm-ppc64=complete @@ -954,6 +1101,7 @@ driver-impl-hyperv=complete driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=complete [networking.firewallrules] title=Network firewall rules @@ -972,6 +1120,7 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [networking.routing] title=Network routing @@ -990,6 +1139,7 @@ driver-impl-hyperv=missing driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [networking.securitygroups] title=Network security groups @@ -999,8 +1149,8 @@ notes=The security groups feature provides a way to define rules on a compute host. This would prevent actions such as MAC and IP address spoofing, or the ability to setup rogue DHCP servers. In a private cloud environment this may be considered to be a - superfluous requirement. Thereforce this is considered to be - an optional configuration to support. + superfluous requirement. Therefore this is considered to be an + optional configuration to support. cli= driver-impl-xenserver=complete driver-impl-libvirt-kvm-x86=complete @@ -1015,11 +1165,12 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [networking.topology.flat] title=Flat networking status=choice(networking.topology) -notes=Provide network conenctivity to guests using a +notes=Provide network connectivity to guests using a flat topology across all compute nodes. At least one of the networking configurations is mandatory to support in the drivers. @@ -1036,6 +1187,7 @@ driver-impl-hyperv=complete driver-impl-ironic=complete driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [networking.topology.vlan] title=VLAN networking @@ -1056,6 +1208,7 @@ driver-impl-hyperv=missing driver-impl-ironic=missing driver-impl-libvirt-vz-vm=complete driver-impl-libvirt-vz-ct=complete +driver-impl-powervm=missing [operation.uefi-boot] title=uefi boot @@ -1075,3 +1228,4 @@ driver-impl-ironic=partial driver-notes-ironic=depends on hardware support driver-impl-libvirt-vz-vm=missing driver-impl-libvirt-vz-ct=missing +driver-impl-powervm=missing diff --git a/doc/source/support-matrix.rst b/doc/source/user/support-matrix.rst similarity index 97% rename from doc/source/support-matrix.rst rename to doc/source/user/support-matrix.rst index ccf9090f478..d3d6e9ff49d 100644 --- a/doc/source/support-matrix.rst +++ b/doc/source/user/support-matrix.rst @@ -5,7 +5,7 @@ Feature Support Matrix .. warning:: Please note, while this document is still being maintained, this is slowly being updated to re-group and classify features using the definitions - described in here: :doc:`feature_classification` + described in here: :doc:`feature-classification` When considering which capabilities should be marked as mandatory the following general guiding principles were applied diff --git a/doc/source/user/upgrade.rst b/doc/source/user/upgrade.rst new file mode 100644 index 00000000000..0eb0ef05638 --- /dev/null +++ b/doc/source/user/upgrade.rst @@ -0,0 +1,332 @@ +.. + Copyright 2014 Rackspace + 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. + +Upgrades +======== + +Nova aims to provide upgrades with minimal downtime. + +Firstly, the data plane. There should be no VM downtime when you upgrade +Nova. Nova has had this since the early days, with the exception of +some nova-network related services. + +Secondly, we want no downtime during upgrades of the Nova control plane. +This document is trying to describe how we can achieve that. + +Once we have introduced the key concepts relating to upgrade, we will +introduce the process needed for a no downtime upgrade of nova. + + +Minimal Downtime Upgrade Process +-------------------------------- + + +Plan your upgrade +''''''''''''''''' + +* Read and ensure you understand the release notes for the next release. + +* You should ensure all required steps from the previous upgrade have been + completed, such as data migrations. + +* Make a backup of your database. Nova does not support downgrading of the + database. Hence, in case of upgrade failure, restoring database from backup + is the only choice. + +* During upgrade be aware that there will be additional load on nova-conductor. + You may find you need to add extra nova-conductor workers to deal with the + additional upgrade related load. + + +Rolling upgrade process +''''''''''''''''''''''' + +To reduce downtime, the compute services can be upgraded in a rolling fashion. It +means upgrading a few services at a time. This results in a condition where +both old (N) and new (N+1) nova-compute services co-exist for a certain time +period. Note that, there is no upgrade of the hypervisor here, this is just +upgrading the nova services. If reduced downtime is not a concern (or lower +complexity is desired), all services may be taken down and restarted at the +same time. + +#. Before maintenance window: + + * Start the process with the controller node. Install the code for the next + version of Nova, either in a venv or a separate control plane node, + including all the python dependencies. + + * Using the newly installed nova code, run the DB sync. + (``nova-manage db sync``; ``nova-manage api_db sync``). These schema + change operations should have minimal or no effect on performance, and + should not cause any operations to fail. + + * At this point, new columns and tables may exist in the database. These + DB schema changes are done in a way that both the N and N+1 release can + perform operations against the same schema. + +#. During maintenance window: + + * For maximum safety (no failed API operations), gracefully shutdown all + the services (i.e. SIG_TERM) except nova-compute. + + * Start all services on the new code, with + ``[upgrade_levels]compute=auto`` in nova.conf. It is safest to + start nova-conductor first and nova-api last. Note that you may + use a static alias name instead of ``auto``, such as + ``[upgrade_levels]compute=``. Also note that this step is + only required if compute services are not upgraded in lock-step + with the control services. + + * If desired, gracefully shutdown nova-compute (i.e. SIG_TERM) + services in small batches, then start the new version of the code + with: ``[upgrade_levels]compute=auto``. If this batch-based approach + is used, only a few compute nodes will have any delayed API + actions, and to ensure there is enough capacity online to service + any boot requests that happen during this time. + +#. After maintenance window: + + * Once all services are running the new code, double check in the DB that + there are no old orphaned service records using `nova service-list`. + + * Now that all services are upgraded, we need to send the SIG_HUP signal, so all + the services clear any cached service version data. When a new service + starts, it automatically detects which version of the compute RPC protocol + to use, and it can decide if it is safe to do any online data migrations. + Note, if you used a static value for the upgrade_level, such as + ``[upgrade_levels]compute=``, you must update nova.conf to remove + that configuration value and do a full service restart. + + * Now all the services are upgraded and signaled, the system is able to use + the latest version of the RPC protocol and can access all of the + features in the new release. + + * Once all the services are running the latest version of the code, and all + the services are aware they all have been upgraded, it is safe to + transform the data in the database into its new format. While some of this + work happens on demand when the system reads a database row that needs + updating, we must get all the data transformed into the current version + before the next upgrade. Additionally, some data may not be transformed + automatically so performing the data migration is necessary to avoid + performance degradation due to compatibility routines. + + * This process can put significant extra write load on the + database. Complete all online data migrations using: + ``nova-manage db online_data_migrations --limit ``. Note + that you can use the limit argument to reduce the load this + operation will place on the database, which allows you to run a + small chunk of the migrations until all of the work is done. Each + time it is run, it will show summary of completed and remaining + records. You run this command until you see completed and + remaining records as zeros. The chunk size you should use depend + on your infrastructure and how much additional load you can + impose on the database. To reduce load, perform smaller batches + with delays between chunks. To reduce time to completion, run + larger batches. + + * At this point, you must also ensure you update the configuration, to stop + using any deprecated features or options, and perform any required work + to transition to alternative features. All the deprecated options should + be supported for one cycle, but should be removed before your next + upgrade is performed. + + +Current Database Upgrade Types +------------------------------ + +Currently Nova has 2 types of database upgrades that are in use. + +#. Schema Migrations +#. Data Migrations + + +Schema Migrations +'''''''''''''''''' + +Schema migrations are defined in +``nova/db/sqlalchemy/migrate_repo/versions`` and in +``nova/db/sqlalchemy/api_migrations/migrate_repo/versions``. They are +the routines that transform our database structure, which should be +additive and able to be applied to a running system before service +code has been upgraded. + + +Data Migrations +''''''''''''''''' + +Online data migrations occur in two places: + +#. Inline migrations that occur as part of normal run-time + activity as data is read in the old format and written in the + new format +#. Background online migrations that are performed using + ``nova-manage`` to complete transformations that will not occur + incidentally due to normal runtime activity. + +An example of online data migrations are the flavor migrations done as part +of Nova object version 1.18. This included a transient migration of flavor +storage from one database location to another. + +:emphasis:`Note: Database downgrades are not supported.` + +Migration policy: +''''''''''''''''' + +The following guidelines for schema and data migrations are followed in order +to ease upgrades: + +* Additive schema migrations - In general, almost all schema migrations should + be additive. Put simply, they should only create elements like columns, + indices, and tables. + +* Subtractive schema migrations - To remove an element like a column or table + during the N release cycle: + + #. The element must be deprecated and retained for backward compatibility. + (This allows for graceful upgrade from N to N+1.) + + #. Data migration, by the objects layer, must completely migrate data from + the old version of the schema to the new version. + + * `Data migration example + `_ + * `Data migration enforcement example + `_ + (for sqlalchemy migrate/deprecated scripts): + + #. The column can then be removed with a migration at the start of N+2. + +* All schema migrations should be idempotent. (For example, a migration + should check if an element exists in the schema before attempting to add + it.) This logic comes for free in the autogenerated workflow of + the online migrations. + +* Constraints - When adding a foreign or unique key constraint, the schema + migration code needs to handle possible problems with data before applying + the constraint. (Example: A unique constraint must clean up duplicate + records before applying said constraint.) + +* Data migrations - As mentioned above, data migrations will be done in an + online fashion by custom code in the object layer that handles moving data + between the old and new portions of the schema. In addition, for each type + of data migration performed, there should exist a nova-manage option for an + operator to manually request that rows be migrated. + + * See `flavor migration spec + `_ + for an example of data migrations in the object layer. + +*Future* work - + #. Adding plumbing to enforce that relevant data migrations are completed + before running `contract` in the expand/migrate/contract schema migration + workflow. A potential solution would be for `contract` to run a gating + test for each specific subtract operation to determine if the operation + can be completed. + +Concepts +-------- + +Here are the key concepts you need to know before reading the section on the +upgrade process: + +RPC version pinning + Through careful RPC versioning, newer nodes are able to talk to older + nova-compute nodes. When upgrading control plane nodes, we can pin them + at an older version of the compute RPC API, until all the compute nodes + are able to be upgraded. + https://wiki.openstack.org/wiki/RpcMajorVersionUpdates + + .. note:: + + This does not apply to cells v1 deployments since cells v1 does not + support rolling upgrades. It is assumed that cells v1 deployments are + upgraded in lockstep so n-1 cells compatibility does not work. + + The procedure for rolling upgrades with multiple cells v2 cells is not + yet determined. + +Online Configuration Reload + During the upgrade, we pin new serves at the older RPC version. When all + services are updated to use newer code, we need to unpin them so we are + able to use any new functionality. + To avoid having to restart the service, using the current SIGHUP signal + handling, or otherwise, ideally we need a way to update the currently + running process to use the latest configuration. + +Graceful service shutdown + Many nova services are python processes listening for messages on a + AMQP queue, including nova-compute. When sending the process the SIGTERM + the process stops getting new work from its queue, completes any + outstanding work, then terminates. During this process, messages can be + left on the queue for when the python process starts back up. + This gives us a way to shutdown a service using older code, and start + up a service using newer code with minimal impact. If its a service that + can have multiple workers, like nova-conductor, you can usually add the + new workers before the graceful shutdown of the old workers. In the case + of singleton services, like nova-compute, some actions could be delayed + during the restart, but ideally no actions should fail due to the restart. + NOTE: while this is true for the RabbitMQ RPC backend, we need to confirm + what happens for other RPC backends. + +API load balancer draining + When upgrading API nodes, you can make your load balancer only send new + connections to the newer API nodes, allowing for a seamless update of your + API nodes. + +Expand/Contract DB Migrations + Modern databases are able to make many schema changes while you are still + writing to the database. Taking this a step further, we can make all DB + changes by first adding the new structures, expanding. Then you can slowly + move all the data into a new location and format. Once that is complete, + you can drop bits of the scheme that are no long needed, + i.e. contract. This happens multiple cycles after we have stopped + using a particular piece of schema, and can happen in a schema + migration without affecting runtime code. + +Online Data Migrations using objects + In Kilo we are moving all data migration into the DB objects code. + When trying to migrate data in the database from the old format to the + new format, this is done in the object code when reading or saving things + that are in the old format. For records that are not updated, you need to + run a background process to convert those records into the newer format. + This process must be completed before you contract the database schema. + +DB prune deleted rows + Currently resources are soft deleted in the main database, so users are able + to track instances in the DB that are created and destroyed in production. + However, most people have a data retention policy, of say 30 days or 90 + days after which they will want to delete those entries. Not deleting + those entries affects DB performance as indices grow very large and data + migrations take longer as there is more data to migrate. + +nova-conductor object backports + RPC pinning ensures new services can talk to the older service's method + signatures. But many of the parameters are objects that may well be too + new for the old service to understand, so you are able to send the object + back to the nova-conductor to be downgraded to a version the older service + can understand. + + +Testing +------- + +Once we have all the pieces in place, we hope to move the Grenade testing +to follow this new pattern. + +The current tests only cover the existing upgrade process where: + +* old computes can run with new control plane +* but control plane is turned off for DB migrations diff --git a/doc/source/user/user-data.rst b/doc/source/user/user-data.rst new file mode 100644 index 00000000000..7e71482c531 --- /dev/null +++ b/doc/source/user/user-data.rst @@ -0,0 +1,22 @@ +============================== +Provide user data to instances +============================== + +*User data* is a blob of data that the user can specify when they launch an +instance. The instance can access this data through the metadata service or +config drive. Commonly used to pass a shell script that the instance runs on +boot. + +For example, one application that uses user data is the +`cloud-init `__ system, +which is an open-source package from Ubuntu that is available on various +Linux distributions and which handles early initialization of a cloud +instance. + +You can place user data in a local file and pass it through the +``--user-data `` parameter at instance creation. + +.. code-block:: console + + $ openstack server create --image ubuntu-cloudimage --flavor 1 \ + --user-data mydata.file VM_INSTANCE diff --git a/doc/source/user/vendordata.rst b/doc/source/user/vendordata.rst new file mode 100644 index 00000000000..e108e3cad52 --- /dev/null +++ b/doc/source/user/vendordata.rst @@ -0,0 +1,122 @@ +Vendordata +========== + +Nova presents configuration information to instances it starts via a mechanism +called metadata. This metadata is made available via either a configdrive, or +the metadata service. These mechanisms are widely used via helpers such as +cloud-init to specify things like the root password the instance should use. +There are three separate groups of people who need to be able to specify +metadata for an instance. + +User provided data +------------------ + +The user who booted the instance can pass metadata to the instance in several +ways. For authentication keypairs, the keypairs functionality of the Nova APIs +can be used to upload a key and then specify that key during the Nova boot API +request. For less structured data, a small opaque blob of data may be passed +via the user-data feature of the Nova API. Examples of such unstructured data +would be the puppet role that the instance should use, or the HTTP address of a +server to fetch post-boot configuration information from. + +Nova provided data +------------------ + +Nova itself needs to pass information to the instance via its internal +implementation of the metadata system. Such information includes the network +configuration for the instance, as well as the requested hostname for the +instance. This happens by default and requires no configuration by the user or +deployer. + +Deployer provided data +---------------------- + +There is however a third type of data. It is possible that the deployer of +OpenStack needs to pass data to an instance. It is also possible that this data +is not known to the user starting the instance. An example might be a +cryptographic token to be used to register the instance with Active Directory +post boot -- the user starting the instance should not have access to Active +Directory to create this token, but the Nova deployment might have permissions +to generate the token on the user's behalf. + +Nova supports a mechanism to add "vendordata" to the metadata handed to +instances. This is done by loading named modules, which must appear in the nova +source code. We provide two such modules: + +- StaticJSON: a module which can include the contents of a static JSON file + loaded from disk. This can be used for things which don't change between + instances, such as the location of the corporate puppet server. + +- DynamicJSON: a module which will make a request to an external REST service + to determine what metadata to add to an instance. This is how we recommend + you generate things like Active Directory tokens which change per instance. + +Tell me more about DynamicJSON +============================== + +To use DynamicJSON, you configure it like this: + +- Add "DynamicJSON" to the vendordata_providers configuration option. This can + also include "StaticJSON" if you'd like. +- Specify the REST services to be contacted to generate metadata in the + vendordata_dynamic_targets configuration option. There can be more than one + of these, but note that they will be queried once per metadata request from + the instance, which can mean a fair bit of traffic depending on your + configuration and the configuration of the instance. + +The format for an entry in vendordata_dynamic_targets is like this: + + @ + +Where name is a short string not including the '@' character, and where the +URL can include a port number if so required. An example would be:: + + testing@http://127.0.0.1:125 + +Metadata fetched from this target will appear in the metadata service at a +new file called vendordata2.json, with a path (either in the metadata service +URL or in the configdrive) like this: + + openstack/2016-10-06/vendor_data2.json + +For each dynamic target, there will be an entry in the JSON file named after +that target. For example:: + + { + "testing": { + "value1": 1, + "value2": 2, + "value3": "three" + } + } + +Do not specify the same name more than once. If you do, we will ignore +subsequent uses of a previously used name. + +The following data is passed to your REST service as a JSON encoded POST: + ++-------------+-------------------------------------------------+ +| Key | Description | ++=============+=================================================+ +| project-id | The ID of the project that owns this instance. | ++-------------+-------------------------------------------------+ +| instance-id | The UUID of this instance. | ++-------------+-------------------------------------------------+ +| image-id | The ID of the image used to boot this instance. | ++-------------+-------------------------------------------------+ +| user-data | As specified by the user at boot time. | ++-------------+-------------------------------------------------+ +| hostname | The hostname of the instance. | ++-------------+-------------------------------------------------+ +| metadata | As specified by the user at boot time. | ++-------------+-------------------------------------------------+ + +Deployment considerations +========================= + +Nova provides authentication to external metadata services in order to provide +some level of certainty that the request came from nova. This is done by +providing a service token with the request -- you can then just deploy your +metadata service with the keystone authentication WSGI middleware. This is +configured using the keystone authentication parameters in the +``vendordata_dynamic_auth`` configuration group. diff --git a/doc/source/user/wsgi.rst b/doc/source/user/wsgi.rst new file mode 100644 index 00000000000..6b314b4832e --- /dev/null +++ b/doc/source/user/wsgi.rst @@ -0,0 +1,36 @@ +Using WSGI with Nova +==================== + +Though the compute and metadata APIs can be run using independent scripts that +provide eventlet-based HTTP servers, it is generally considered more performant +and flexible to run them using a generic HTTP server that supports WSGI_ (such +as Apache_ or nginx_). + +The nova project provides two automatically generated entry points that +support this: ``nova-api-wsgi`` and ``nova-metadata-wsgi``. These read +``nova.conf`` and ``api-paste.ini`` and generate the required module-level +``application`` that most WSGI servers require. If nova is installed using pip, +these two scripts will be installed into whatever the expected ``bin`` +directory is for the environment. + +The new scripts replace older experimental scripts that could be found in the +``nova/wsgi`` directory of the code repository. The new scripts are *not* +experimental. + +When running the compute and metadata services with WSGI, sharing the compute +and metadata service in the same process is not supported (as it is in the +eventlet-based scripts). + +In devstack as of May 2017, the compute and metadata APIs are hosted by a +Apache communicating with uwsgi_ via mod_proxy_uwsgi_. Inspecting the +configuration created there can provide some guidance on one option for +managing the WSGI scripts. It is important to remember, however, that one of +the major features of using WSGI is that there are many different ways to host +a WSGI application. Different servers make different choices about performance +and configurability. + +.. _WSGI: https://www.python.org/dev/peps/pep-3333/ +.. _apache: http://httpd.apache.org/ +.. _nginx: http://nginx.org/en/ +.. _uwsgi: https://uwsgi-docs.readthedocs.io/ +.. _mod_proxy_uwsgi: http://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi diff --git a/doc/source/vmstates.rst b/doc/source/vmstates.rst deleted file mode 100644 index 978fe5223e7..00000000000 --- a/doc/source/vmstates.rst +++ /dev/null @@ -1,141 +0,0 @@ -Virtual Machine States and Transitions -======================================= - -The following diagrams and tables show the required virtual machine (VM) -states and task states for various commands issued by the user. - -Allowed State Transitions --------------------------- - -.. graphviz:: - - digraph states { - graph [pad=".35", ranksep="0.65", nodesep="0.55", concentrate=true]; - node [fontsize=10 fontname="Monospace"]; - edge [arrowhead="normal", arrowsize="0.8"]; - label="All states are allowed to transition to DELETED and ERROR."; - forcelabels=true; - labelloc=bottom; - labeljust=left; - - /* states */ - building [label="BUILDING"] - active [label="ACTIVE"] - paused [label="PAUSED"] - suspended [label="SUSPENDED"] - stopped [label="STOPPED"] - rescued [label="RESCUED"] - resized [label="RESIZED"] - soft_deleted [label="SOFT_DELETED"] - shelved [label="SHELVED"] - shelved_offloaded [label="SHELVED_OFFLOADED"] - deleted [label="DELETED", color="red"] - error [label="ERROR", color="red"] - - /* transitions [action] */ - building -> active - - active -> active [headport=nw, tailport=ne] // manual layout - active -> soft_deleted [tailport=e] // prevent arrowhead overlap - active -> suspended - active -> paused [tailport=w] // prevent arrowhead overlap - active -> stopped - active -> shelved - active -> shelved_offloaded - active -> rescued - active -> resized - - soft_deleted -> active [headport=e] // prevent arrowhead overlap - - suspended -> active - suspended -> shelved - suspended -> shelved_offloaded - - paused -> active - paused -> shelved - paused -> shelved_offloaded - - stopped -> active - stopped -> stopped [headport=nw, tailport=ne] // manual layout - stopped -> resized - stopped -> rescued - stopped -> shelved - stopped -> shelved_offloaded - - resized -> active - - rescued -> active - - shelved -> shelved_offloaded - shelved -> active - - shelved_offloaded -> active - } - - -Requirements for Commands -------------------------- - -================== ================== ==================== ================ -Command Req'd VM States Req'd Task States Target State -================== ================== ==================== ================ -pause Active, Shutoff, Resize Verify, unset Paused - Rescued -unpause Paused N/A Active -suspend Active, Shutoff N/A Suspended -resume Suspended N/A Active -rescue Active, Shutoff Resize Verify, unset Rescued -unrescue Rescued N/A Active -set admin password Active N/A Active -rebuild Active, Shutoff Resize Verify, unset Active -force delete Soft Deleted N/A Deleted -restore Soft Deleted N/A Active -soft delete Active, Shutoff, N/A Soft Deleted - Error -delete Active, Shutoff, N/A Deleted - Building, Rescued, - Error -backup Active, Shutoff N/A Active, Shutoff -snapshot Active, Shutoff N/A Active, Shutoff -start Shutoff, Stopped N/A Active -stop Active, Shutoff, Resize Verify, unset Stopped - Rescued -reboot Active, Shutoff, Resize Verify, unset Active - Rescued -resize Active, Shutoff Resize Verify, unset Resized -revert resize Active, Shutoff Resize Verify, unset Active -confirm resize Active, Shutoff Resize Verify, unset Active -================== ================== ==================== ================ - -VM states and Possible Commands -------------------------------- - -============ ================================================================= -VM State Commands -============ ================================================================= -Paused unpause -Suspended resume -Active set admin password, suspend, pause, rescue, rebuild, soft delete, - delete, backup, snapshot, stop, reboot, resize, revert resize, - confirm resize -Shutoff suspend, pause, rescue, rebuild, soft delete, delete, backup, - start, snapshot, stop, reboot, resize, revert resize, - confirm resize -Rescued unrescue, pause -Stopped rescue, delete, start -Soft Deleted force delete, restore -Error soft delete, delete -Building delete -Rescued delete, stop, reboot -============ ================================================================= - - -Create Instance States ----------------------- - -The following diagram shows the sequence of VM states, task states, and -power states when a new VM instance is created. - -.. image:: ./images/create_vm_states.svg - :alt: Sequence of VM states, task states, and power states when a new - VM instance is created. diff --git a/etc/nova/README-nova.conf.txt b/etc/nova/README-nova.conf.txt index b42c614d5fc..f574b2a63ff 100644 --- a/etc/nova/README-nova.conf.txt +++ b/etc/nova/README-nova.conf.txt @@ -1,4 +1,8 @@ -To generate the sample nova.conf file, run the following -command from the top level of the nova directory: +To generate the sample nova.conf file, run the following command from the top +level of the nova directory: -tox -egenconfig + tox -egenconfig + +For a pre-generated example of the latest nova.conf, see: + + http://docs.openstack.org/developer/nova/sample_config.html diff --git a/etc/nova/README-policy.yaml.txt b/etc/nova/README-policy.yaml.txt new file mode 100644 index 00000000000..f0188aa9141 --- /dev/null +++ b/etc/nova/README-policy.yaml.txt @@ -0,0 +1,4 @@ +To generate the sample policy.yaml file, run the following command from the top +level of the nova directory: + + tox -egenpolicy diff --git a/etc/nova/api-paste.ini b/etc/nova/api-paste.ini index 951ae72c5e7..a0563938007 100644 --- a/etc/nova/api-paste.ini +++ b/etc/nova/api-paste.ini @@ -18,15 +18,6 @@ paste.app_factory = nova.api.metadata.handler:MetadataRequestHandler.factory [composite:osapi_compute] use = call:nova.api.openstack.urlmap:urlmap_factory /: oscomputeversions -# starting in Liberty the v21 implementation replaces the v2 -# implementation and is suggested that you use it as the default. If -# this causes issues with your clients you can rollback to the -# *frozen* v2 api by commenting out the above stanza and using the -# following instead:: -# /v2: openstack_compute_api_legacy_v2 -# if rolling back to v2 fixes your issue please file a critical bug -# at - https://bugs.launchpad.net/nova/+bugs -# # v21 is an exactly feature match for v2, except it has more stringent # input validation on the wsgi surface (prevents fuzzing early on the # API). It also provides new features via API microversions which are @@ -35,26 +26,22 @@ use = call:nova.api.openstack.urlmap:urlmap_factory /v2: openstack_compute_api_v21_legacy_v2_compatible /v2.1: openstack_compute_api_v21 -# NOTE: this is deprecated in favor of openstack_compute_api_v21_legacy_v2_compatible -[composite:openstack_compute_api_legacy_v2] -use = call:nova.api.auth:pipeline_factory -noauth2 = cors compute_req_id faultwrap sizelimit noauth2 legacy_ratelimit osapi_compute_app_legacy_v2 -keystone = cors compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_ratelimit osapi_compute_app_legacy_v2 -keystone_nolimit = cors compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_legacy_v2 - [composite:openstack_compute_api_v21] use = call:nova.api.auth:pipeline_factory_v21 -noauth2 = cors compute_req_id faultwrap sizelimit noauth2 osapi_compute_app_v21 -keystone = cors compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v21 +noauth2 = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler noauth2 osapi_compute_app_v21 +keystone = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler authtoken keystonecontext osapi_compute_app_v21 [composite:openstack_compute_api_v21_legacy_v2_compatible] use = call:nova.api.auth:pipeline_factory_v21 -noauth2 = cors compute_req_id faultwrap sizelimit noauth2 legacy_v2_compatible osapi_compute_app_v21 -keystone = cors compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_v2_compatible osapi_compute_app_v21 +noauth2 = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler noauth2 legacy_v2_compatible osapi_compute_app_v21 +keystone = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler authtoken keystonecontext legacy_v2_compatible osapi_compute_app_v21 [filter:request_id] paste.filter_factory = oslo_middleware:RequestId.factory +[filter:request_log] +paste.filter_factory = nova.api.openstack.requestlog:RequestLog.factory + [filter:compute_req_id] paste.filter_factory = nova.api.compute_req_id:ComputeReqIdMiddleware.factory @@ -64,23 +51,23 @@ paste.filter_factory = nova.api.openstack:FaultWrapper.factory [filter:noauth2] paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory -[filter:legacy_ratelimit] -paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory +[filter:osprofiler] +paste.filter_factory = nova.profiler:WsgiMiddleware.factory [filter:sizelimit] paste.filter_factory = oslo_middleware:RequestBodySizeLimiter.factory +[filter:http_proxy_to_wsgi] +paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory + [filter:legacy_v2_compatible] paste.filter_factory = nova.api.openstack:LegacyV2CompatibleWrapper.factory -[app:osapi_compute_app_legacy_v2] -paste.app_factory = nova.api.openstack.compute:APIRouter.factory - [app:osapi_compute_app_v21] paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory [pipeline:oscomputeversions] -pipeline = faultwrap oscomputeversionapp +pipeline = cors faultwrap request_log http_proxy_to_wsgi oscomputeversionapp [app:oscomputeversionapp] paste.app_factory = nova.api.openstack.compute.versions:Versions.factory diff --git a/etc/nova/nova-config-generator.conf b/etc/nova/nova-config-generator.conf index 49e7abc8d96..0f0f81bc756 100644 --- a/etc/nova/nova-config-generator.conf +++ b/etc/nova/nova-config-generator.conf @@ -1,23 +1,14 @@ [DEFAULT] output_file = etc/nova/nova.conf.sample -wrap_width = 79 -namespace = nova +wrap_width = 80 namespace = nova.conf -namespace = nova.api -namespace = nova.cache_utils -namespace = nova.cells -namespace = nova.compute -namespace = nova.network -namespace = nova.network.neutronv2 -namespace = nova.virt -namespace = oslo.cache namespace = oslo.log namespace = oslo.messaging namespace = oslo.policy namespace = oslo.service.periodic_task namespace = oslo.service.service -namespace = oslo.service.sslutils namespace = oslo.db +namespace = oslo.db.concurrency namespace = oslo.middleware namespace = oslo.concurrency namespace = keystonemiddleware.auth_token diff --git a/etc/nova/nova-policy-generator.conf b/etc/nova/nova-policy-generator.conf new file mode 100644 index 00000000000..0f02a950afa --- /dev/null +++ b/etc/nova/nova-policy-generator.conf @@ -0,0 +1,3 @@ +[DEFAULT] +output_file = etc/nova/policy.yaml.sample +namespace = nova diff --git a/etc/nova/policy.json b/etc/nova/policy.json deleted file mode 100644 index dc35f0c2044..00000000000 --- a/etc/nova/policy.json +++ /dev/null @@ -1,491 +0,0 @@ -{ - "context_is_admin": "role:admin", - "admin_or_owner": "is_admin:True or project_id:%(project_id)s", - "default": "rule:admin_or_owner", - - "cells_scheduler_filter:TargetCellFilter": "is_admin:True", - - "compute:create": "rule:admin_or_owner", - "compute:create:attach_network": "rule:admin_or_owner", - "compute:create:attach_volume": "rule:admin_or_owner", - "compute:create:forced_host": "is_admin:True", - - "compute:get": "rule:admin_or_owner", - "compute:get_all": "rule:admin_or_owner", - "compute:get_all_tenants": "is_admin:True", - - "compute:update": "rule:admin_or_owner", - - "compute:get_instance_metadata": "rule:admin_or_owner", - "compute:get_all_instance_metadata": "rule:admin_or_owner", - "compute:get_all_instance_system_metadata": "rule:admin_or_owner", - "compute:update_instance_metadata": "rule:admin_or_owner", - "compute:delete_instance_metadata": "rule:admin_or_owner", - - "compute:get_diagnostics": "rule:admin_or_owner", - "compute:get_instance_diagnostics": "rule:admin_or_owner", - - "compute:start": "rule:admin_or_owner", - "compute:stop": "rule:admin_or_owner", - - "compute:lock": "rule:admin_or_owner", - "compute:unlock": "rule:admin_or_owner", - "compute:unlock_override": "rule:admin_api", - - "compute:get_vnc_console": "rule:admin_or_owner", - "compute:get_spice_console": "rule:admin_or_owner", - "compute:get_rdp_console": "rule:admin_or_owner", - "compute:get_serial_console": "rule:admin_or_owner", - "compute:get_mks_console": "rule:admin_or_owner", - "compute:get_console_output": "rule:admin_or_owner", - - "compute:reset_network": "rule:admin_or_owner", - "compute:inject_network_info": "rule:admin_or_owner", - "compute:add_fixed_ip": "rule:admin_or_owner", - "compute:remove_fixed_ip": "rule:admin_or_owner", - - "compute:attach_volume": "rule:admin_or_owner", - "compute:detach_volume": "rule:admin_or_owner", - "compute:swap_volume": "rule:admin_or_owner", - - "compute:attach_interface": "rule:admin_or_owner", - "compute:detach_interface": "rule:admin_or_owner", - - "compute:set_admin_password": "rule:admin_or_owner", - - "compute:rescue": "rule:admin_or_owner", - "compute:unrescue": "rule:admin_or_owner", - - "compute:suspend": "rule:admin_or_owner", - "compute:resume": "rule:admin_or_owner", - - "compute:pause": "rule:admin_or_owner", - "compute:unpause": "rule:admin_or_owner", - - "compute:shelve": "rule:admin_or_owner", - "compute:shelve_offload": "rule:admin_or_owner", - "compute:unshelve": "rule:admin_or_owner", - - "compute:snapshot": "rule:admin_or_owner", - "compute:snapshot_volume_backed": "rule:admin_or_owner", - "compute:backup": "rule:admin_or_owner", - - "compute:resize": "rule:admin_or_owner", - "compute:confirm_resize": "rule:admin_or_owner", - "compute:revert_resize": "rule:admin_or_owner", - - "compute:rebuild": "rule:admin_or_owner", - "compute:reboot": "rule:admin_or_owner", - "compute:delete": "rule:admin_or_owner", - "compute:soft_delete": "rule:admin_or_owner", - "compute:force_delete": "rule:admin_or_owner", - - "compute:security_groups:add_to_instance": "rule:admin_or_owner", - "compute:security_groups:remove_from_instance": "rule:admin_or_owner", - - "compute:restore": "rule:admin_or_owner", - - "compute:volume_snapshot_create": "rule:admin_or_owner", - "compute:volume_snapshot_delete": "rule:admin_or_owner", - - "admin_api": "is_admin:True", - "compute_extension:accounts": "rule:admin_api", - "compute_extension:admin_actions": "rule:admin_api", - "compute_extension:admin_actions:pause": "rule:admin_or_owner", - "compute_extension:admin_actions:unpause": "rule:admin_or_owner", - "compute_extension:admin_actions:suspend": "rule:admin_or_owner", - "compute_extension:admin_actions:resume": "rule:admin_or_owner", - "compute_extension:admin_actions:lock": "rule:admin_or_owner", - "compute_extension:admin_actions:unlock": "rule:admin_or_owner", - "compute_extension:admin_actions:resetNetwork": "rule:admin_api", - "compute_extension:admin_actions:injectNetworkInfo": "rule:admin_api", - "compute_extension:admin_actions:createBackup": "rule:admin_or_owner", - "compute_extension:admin_actions:migrateLive": "rule:admin_api", - "compute_extension:admin_actions:resetState": "rule:admin_api", - "compute_extension:admin_actions:migrate": "rule:admin_api", - "compute_extension:aggregates": "rule:admin_api", - "compute_extension:agents": "rule:admin_api", - "compute_extension:attach_interfaces": "rule:admin_or_owner", - "compute_extension:baremetal_nodes": "rule:admin_api", - "compute_extension:cells": "rule:admin_api", - "compute_extension:cells:create": "rule:admin_api", - "compute_extension:cells:delete": "rule:admin_api", - "compute_extension:cells:update": "rule:admin_api", - "compute_extension:cells:sync_instances": "rule:admin_api", - "compute_extension:certificates": "rule:admin_or_owner", - "compute_extension:cloudpipe": "rule:admin_api", - "compute_extension:cloudpipe_update": "rule:admin_api", - "compute_extension:config_drive": "rule:admin_or_owner", - "compute_extension:console_output": "rule:admin_or_owner", - "compute_extension:consoles": "rule:admin_or_owner", - "compute_extension:createserverext": "rule:admin_or_owner", - "compute_extension:deferred_delete": "rule:admin_or_owner", - "compute_extension:disk_config": "rule:admin_or_owner", - "compute_extension:evacuate": "rule:admin_api", - "compute_extension:extended_server_attributes": "rule:admin_api", - "compute_extension:extended_status": "rule:admin_or_owner", - "compute_extension:extended_availability_zone": "rule:admin_or_owner", - "compute_extension:extended_ips": "rule:admin_or_owner", - "compute_extension:extended_ips_mac": "rule:admin_or_owner", - "compute_extension:extended_vif_net": "rule:admin_or_owner", - "compute_extension:extended_volumes": "rule:admin_or_owner", - "compute_extension:fixed_ips": "rule:admin_api", - "compute_extension:flavor_access": "rule:admin_or_owner", - "compute_extension:flavor_access:addTenantAccess": "rule:admin_api", - "compute_extension:flavor_access:removeTenantAccess": "rule:admin_api", - "compute_extension:flavor_disabled": "rule:admin_or_owner", - "compute_extension:flavor_rxtx": "rule:admin_or_owner", - "compute_extension:flavor_swap": "rule:admin_or_owner", - "compute_extension:flavorextradata": "rule:admin_or_owner", - "compute_extension:flavorextraspecs:index": "rule:admin_or_owner", - "compute_extension:flavorextraspecs:show": "rule:admin_or_owner", - "compute_extension:flavorextraspecs:create": "rule:admin_api", - "compute_extension:flavorextraspecs:update": "rule:admin_api", - "compute_extension:flavorextraspecs:delete": "rule:admin_api", - "compute_extension:flavormanage": "rule:admin_api", - "compute_extension:floating_ip_dns": "rule:admin_or_owner", - "compute_extension:floating_ip_pools": "rule:admin_or_owner", - "compute_extension:floating_ips": "rule:admin_or_owner", - "compute_extension:floating_ips_bulk": "rule:admin_api", - "compute_extension:fping": "rule:admin_or_owner", - "compute_extension:fping:all_tenants": "rule:admin_api", - "compute_extension:hide_server_addresses": "is_admin:False", - "compute_extension:hosts": "rule:admin_api", - "compute_extension:hypervisors": "rule:admin_api", - "compute_extension:image_size": "rule:admin_or_owner", - "compute_extension:instance_actions": "rule:admin_or_owner", - "compute_extension:instance_actions:events": "rule:admin_api", - "compute_extension:instance_usage_audit_log": "rule:admin_api", - "compute_extension:keypairs": "rule:admin_or_owner", - "compute_extension:keypairs:index": "rule:admin_or_owner", - "compute_extension:keypairs:show": "rule:admin_or_owner", - "compute_extension:keypairs:create": "rule:admin_or_owner", - "compute_extension:keypairs:delete": "rule:admin_or_owner", - "compute_extension:multinic": "rule:admin_or_owner", - "compute_extension:networks": "rule:admin_api", - "compute_extension:networks:view": "rule:admin_or_owner", - "compute_extension:networks_associate": "rule:admin_api", - "compute_extension:os-tenant-networks": "rule:admin_or_owner", - "compute_extension:quotas:show": "rule:admin_or_owner", - "compute_extension:quotas:update": "rule:admin_api", - "compute_extension:quotas:delete": "rule:admin_api", - "compute_extension:quota_classes": "rule:admin_or_owner", - "compute_extension:rescue": "rule:admin_or_owner", - "compute_extension:security_group_default_rules": "rule:admin_api", - "compute_extension:security_groups": "rule:admin_or_owner", - "compute_extension:server_diagnostics": "rule:admin_api", - "compute_extension:server_groups": "rule:admin_or_owner", - "compute_extension:server_password": "rule:admin_or_owner", - "compute_extension:server_usage": "rule:admin_or_owner", - "compute_extension:services": "rule:admin_api", - "compute_extension:shelve": "rule:admin_or_owner", - "compute_extension:shelveOffload": "rule:admin_api", - "compute_extension:simple_tenant_usage:show": "rule:admin_or_owner", - "compute_extension:simple_tenant_usage:list": "rule:admin_api", - "compute_extension:unshelve": "rule:admin_or_owner", - "compute_extension:users": "rule:admin_api", - "compute_extension:virtual_interfaces": "rule:admin_or_owner", - "compute_extension:virtual_storage_arrays": "rule:admin_or_owner", - "compute_extension:volumes": "rule:admin_or_owner", - "compute_extension:volume_attachments:index": "rule:admin_or_owner", - "compute_extension:volume_attachments:show": "rule:admin_or_owner", - "compute_extension:volume_attachments:create": "rule:admin_or_owner", - "compute_extension:volume_attachments:update": "rule:admin_or_owner", - "compute_extension:volume_attachments:delete": "rule:admin_or_owner", - "compute_extension:volumetypes": "rule:admin_or_owner", - "compute_extension:availability_zone:list": "rule:admin_or_owner", - "compute_extension:availability_zone:detail": "rule:admin_api", - "compute_extension:used_limits_for_admin": "rule:admin_api", - "compute_extension:migrations:index": "rule:admin_api", - "compute_extension:os-assisted-volume-snapshots:create": "rule:admin_api", - "compute_extension:os-assisted-volume-snapshots:delete": "rule:admin_api", - "compute_extension:console_auth_tokens": "rule:admin_api", - "compute_extension:os-server-external-events:create": "rule:admin_api", - - "network:get_all": "rule:admin_or_owner", - "network:get": "rule:admin_or_owner", - "network:create": "rule:admin_or_owner", - "network:delete": "rule:admin_or_owner", - "network:associate": "rule:admin_or_owner", - "network:disassociate": "rule:admin_or_owner", - "network:get_vifs_by_instance": "rule:admin_or_owner", - "network:allocate_for_instance": "rule:admin_or_owner", - "network:deallocate_for_instance": "rule:admin_or_owner", - "network:validate_networks": "rule:admin_or_owner", - "network:get_instance_uuids_by_ip_filter": "rule:admin_or_owner", - "network:get_instance_id_by_floating_address": "rule:admin_or_owner", - "network:setup_networks_on_host": "rule:admin_or_owner", - "network:get_backdoor_port": "rule:admin_or_owner", - - "network:get_floating_ip": "rule:admin_or_owner", - "network:get_floating_ip_pools": "rule:admin_or_owner", - "network:get_floating_ip_by_address": "rule:admin_or_owner", - "network:get_floating_ips_by_project": "rule:admin_or_owner", - "network:get_floating_ips_by_fixed_address": "rule:admin_or_owner", - "network:allocate_floating_ip": "rule:admin_or_owner", - "network:associate_floating_ip": "rule:admin_or_owner", - "network:disassociate_floating_ip": "rule:admin_or_owner", - "network:release_floating_ip": "rule:admin_or_owner", - "network:migrate_instance_start": "rule:admin_or_owner", - "network:migrate_instance_finish": "rule:admin_or_owner", - - "network:get_fixed_ip": "rule:admin_or_owner", - "network:get_fixed_ip_by_address": "rule:admin_or_owner", - "network:add_fixed_ip_to_instance": "rule:admin_or_owner", - "network:remove_fixed_ip_from_instance": "rule:admin_or_owner", - "network:add_network_to_project": "rule:admin_or_owner", - "network:get_instance_nw_info": "rule:admin_or_owner", - - "network:get_dns_domains": "rule:admin_or_owner", - "network:add_dns_entry": "rule:admin_or_owner", - "network:modify_dns_entry": "rule:admin_or_owner", - "network:delete_dns_entry": "rule:admin_or_owner", - "network:get_dns_entries_by_address": "rule:admin_or_owner", - "network:get_dns_entries_by_name": "rule:admin_or_owner", - "network:create_private_dns_domain": "rule:admin_or_owner", - "network:create_public_dns_domain": "rule:admin_or_owner", - "network:delete_dns_domain": "rule:admin_or_owner", - "network:attach_external_network": "rule:admin_api", - "network:get_vif_by_mac_address": "rule:admin_or_owner", - - "os_compute_api:servers:detail:get_all_tenants": "is_admin:True", - "os_compute_api:servers:index:get_all_tenants": "is_admin:True", - "os_compute_api:servers:confirm_resize": "rule:admin_or_owner", - "os_compute_api:servers:create": "rule:admin_or_owner", - "os_compute_api:servers:create:attach_network": "rule:admin_or_owner", - "os_compute_api:servers:create:attach_volume": "rule:admin_or_owner", - "os_compute_api:servers:create:forced_host": "rule:admin_api", - "os_compute_api:servers:delete": "rule:admin_or_owner", - "os_compute_api:servers:update": "rule:admin_or_owner", - "os_compute_api:servers:detail": "rule:admin_or_owner", - "os_compute_api:servers:index": "rule:admin_or_owner", - "os_compute_api:servers:reboot": "rule:admin_or_owner", - "os_compute_api:servers:rebuild": "rule:admin_or_owner", - "os_compute_api:servers:resize": "rule:admin_or_owner", - "os_compute_api:servers:revert_resize": "rule:admin_or_owner", - "os_compute_api:servers:show": "rule:admin_or_owner", - "os_compute_api:servers:show:host_status": "rule:admin_api", - "os_compute_api:servers:create_image": "rule:admin_or_owner", - "os_compute_api:servers:create_image:allow_volume_backed": "rule:admin_or_owner", - "os_compute_api:servers:start": "rule:admin_or_owner", - "os_compute_api:servers:stop": "rule:admin_or_owner", - "os_compute_api:servers:trigger_crash_dump": "rule:admin_or_owner", - "os_compute_api:servers:migrations:force_complete": "rule:admin_api", - "os_compute_api:servers:migrations:delete": "rule:admin_api", - "os_compute_api:servers:discoverable": "@", - "os_compute_api:servers:migrations:index": "rule:admin_api", - "os_compute_api:servers:migrations:show": "rule:admin_api", - "os_compute_api:os-access-ips:discoverable": "@", - "os_compute_api:os-access-ips": "rule:admin_or_owner", - "os_compute_api:os-admin-actions": "rule:admin_api", - "os_compute_api:os-admin-actions:discoverable": "@", - "os_compute_api:os-admin-actions:reset_network": "rule:admin_api", - "os_compute_api:os-admin-actions:inject_network_info": "rule:admin_api", - "os_compute_api:os-admin-actions:reset_state": "rule:admin_api", - "os_compute_api:os-admin-password": "rule:admin_or_owner", - "os_compute_api:os-admin-password:discoverable": "@", - "os_compute_api:os-aggregates:discoverable": "@", - "os_compute_api:os-aggregates:index": "rule:admin_api", - "os_compute_api:os-aggregates:create": "rule:admin_api", - "os_compute_api:os-aggregates:show": "rule:admin_api", - "os_compute_api:os-aggregates:update": "rule:admin_api", - "os_compute_api:os-aggregates:delete": "rule:admin_api", - "os_compute_api:os-aggregates:add_host": "rule:admin_api", - "os_compute_api:os-aggregates:remove_host": "rule:admin_api", - "os_compute_api:os-aggregates:set_metadata": "rule:admin_api", - "os_compute_api:os-agents": "rule:admin_api", - "os_compute_api:os-agents:discoverable": "@", - "os_compute_api:os-attach-interfaces": "rule:admin_or_owner", - "os_compute_api:os-attach-interfaces:discoverable": "@", - "os_compute_api:os-baremetal-nodes": "rule:admin_api", - "os_compute_api:os-baremetal-nodes:discoverable": "@", - "os_compute_api:os-block-device-mapping-v1:discoverable": "@", - "os_compute_api:os-cells": "rule:admin_api", - "os_compute_api:os-cells:create": "rule:admin_api", - "os_compute_api:os-cells:delete": "rule:admin_api", - "os_compute_api:os-cells:update": "rule:admin_api", - "os_compute_api:os-cells:sync_instances": "rule:admin_api", - "os_compute_api:os-cells:discoverable": "@", - "os_compute_api:os-certificates:create": "rule:admin_or_owner", - "os_compute_api:os-certificates:show": "rule:admin_or_owner", - "os_compute_api:os-certificates:discoverable": "@", - "os_compute_api:os-cloudpipe": "rule:admin_api", - "os_compute_api:os-cloudpipe:discoverable": "@", - "os_compute_api:os-config-drive": "rule:admin_or_owner", - "os_compute_api:os-consoles:discoverable": "@", - "os_compute_api:os-consoles:create": "rule:admin_or_owner", - "os_compute_api:os-consoles:delete": "rule:admin_or_owner", - "os_compute_api:os-consoles:index": "rule:admin_or_owner", - "os_compute_api:os-consoles:show": "rule:admin_or_owner", - "os_compute_api:os-console-output:discoverable": "@", - "os_compute_api:os-console-output": "rule:admin_or_owner", - "os_compute_api:os-remote-consoles": "rule:admin_or_owner", - "os_compute_api:os-remote-consoles:discoverable": "@", - "os_compute_api:os-create-backup:discoverable": "@", - "os_compute_api:os-create-backup": "rule:admin_or_owner", - "os_compute_api:os-deferred-delete": "rule:admin_or_owner", - "os_compute_api:os-deferred-delete:discoverable": "@", - "os_compute_api:os-disk-config": "rule:admin_or_owner", - "os_compute_api:os-disk-config:discoverable": "@", - "os_compute_api:os-evacuate": "rule:admin_api", - "os_compute_api:os-evacuate:discoverable": "@", - "os_compute_api:os-extended-server-attributes": "rule:admin_api", - "os_compute_api:os-extended-server-attributes:discoverable": "@", - "os_compute_api:os-extended-status": "rule:admin_or_owner", - "os_compute_api:os-extended-status:discoverable": "@", - "os_compute_api:os-extended-availability-zone": "rule:admin_or_owner", - "os_compute_api:os-extended-availability-zone:discoverable": "@", - "os_compute_api:extensions": "rule:admin_or_owner", - "os_compute_api:extensions:discoverable": "@", - "os_compute_api:extension_info:discoverable": "@", - "os_compute_api:os-extended-volumes": "rule:admin_or_owner", - "os_compute_api:os-extended-volumes:discoverable": "@", - "os_compute_api:os-fixed-ips": "rule:admin_api", - "os_compute_api:os-fixed-ips:discoverable": "@", - "os_compute_api:os-flavor-access": "rule:admin_or_owner", - "os_compute_api:os-flavor-access:discoverable": "@", - "os_compute_api:os-flavor-access:remove_tenant_access": "rule:admin_api", - "os_compute_api:os-flavor-access:add_tenant_access": "rule:admin_api", - "os_compute_api:os-flavor-rxtx": "rule:admin_or_owner", - "os_compute_api:os-flavor-rxtx:discoverable": "@", - "os_compute_api:flavors": "rule:admin_or_owner", - "os_compute_api:flavors:discoverable": "@", - "os_compute_api:os-flavor-extra-specs:discoverable": "@", - "os_compute_api:os-flavor-extra-specs:index": "rule:admin_or_owner", - "os_compute_api:os-flavor-extra-specs:show": "rule:admin_or_owner", - "os_compute_api:os-flavor-extra-specs:create": "rule:admin_api", - "os_compute_api:os-flavor-extra-specs:update": "rule:admin_api", - "os_compute_api:os-flavor-extra-specs:delete": "rule:admin_api", - "os_compute_api:os-flavor-manage:discoverable": "@", - "os_compute_api:os-flavor-manage": "rule:admin_api", - "os_compute_api:os-floating-ip-dns": "rule:admin_or_owner", - "os_compute_api:os-floating-ip-dns:discoverable": "@", - "os_compute_api:os-floating-ip-dns:domain:update": "rule:admin_api", - "os_compute_api:os-floating-ip-dns:domain:delete": "rule:admin_api", - "os_compute_api:os-floating-ip-pools": "rule:admin_or_owner", - "os_compute_api:os-floating-ip-pools:discoverable": "@", - "os_compute_api:os-floating-ips": "rule:admin_or_owner", - "os_compute_api:os-floating-ips:discoverable": "@", - "os_compute_api:os-floating-ips-bulk": "rule:admin_api", - "os_compute_api:os-floating-ips-bulk:discoverable": "@", - "os_compute_api:os-fping": "rule:admin_or_owner", - "os_compute_api:os-fping:discoverable": "@", - "os_compute_api:os-fping:all_tenants": "rule:admin_api", - "os_compute_api:os-hide-server-addresses": "is_admin:False", - "os_compute_api:os-hide-server-addresses:discoverable": "@", - "os_compute_api:os-hosts": "rule:admin_api", - "os_compute_api:os-hosts:discoverable": "@", - "os_compute_api:os-hypervisors": "rule:admin_api", - "os_compute_api:os-hypervisors:discoverable": "@", - "os_compute_api:images:discoverable": "@", - "os_compute_api:image-size": "rule:admin_or_owner", - "os_compute_api:image-size:discoverable": "@", - "os_compute_api:os-instance-actions": "rule:admin_or_owner", - "os_compute_api:os-instance-actions:discoverable": "@", - "os_compute_api:os-instance-actions:events": "rule:admin_api", - "os_compute_api:os-instance-usage-audit-log": "rule:admin_api", - "os_compute_api:os-instance-usage-audit-log:discoverable": "@", - "os_compute_api:ips:discoverable": "@", - "os_compute_api:ips:index": "rule:admin_or_owner", - "os_compute_api:ips:show": "rule:admin_or_owner", - "os_compute_api:os-keypairs:discoverable": "@", - "os_compute_api:os-keypairs": "rule:admin_or_owner", - "os_compute_api:os-keypairs:index": "rule:admin_api or user_id:%(user_id)s", - "os_compute_api:os-keypairs:show": "rule:admin_api or user_id:%(user_id)s", - "os_compute_api:os-keypairs:create": "rule:admin_api or user_id:%(user_id)s", - "os_compute_api:os-keypairs:delete": "rule:admin_api or user_id:%(user_id)s", - "os_compute_api:limits:discoverable": "@", - "os_compute_api:limits": "rule:admin_or_owner", - "os_compute_api:os-lock-server:discoverable": "@", - "os_compute_api:os-lock-server:lock": "rule:admin_or_owner", - "os_compute_api:os-lock-server:unlock": "rule:admin_or_owner", - "os_compute_api:os-lock-server:unlock:unlock_override": "rule:admin_api", - "os_compute_api:os-migrate-server:discoverable": "@", - "os_compute_api:os-migrate-server:migrate": "rule:admin_api", - "os_compute_api:os-migrate-server:migrate_live": "rule:admin_api", - "os_compute_api:os-multinic": "rule:admin_or_owner", - "os_compute_api:os-multinic:discoverable": "@", - "os_compute_api:os-networks": "rule:admin_api", - "os_compute_api:os-networks:view": "rule:admin_or_owner", - "os_compute_api:os-networks:discoverable": "@", - "os_compute_api:os-networks-associate": "rule:admin_api", - "os_compute_api:os-networks-associate:discoverable": "@", - "os_compute_api:os-pause-server:discoverable": "@", - "os_compute_api:os-pause-server:pause": "rule:admin_or_owner", - "os_compute_api:os-pause-server:unpause": "rule:admin_or_owner", - "os_compute_api:os-pci:pci_servers": "rule:admin_or_owner", - "os_compute_api:os-pci:discoverable": "@", - "os_compute_api:os-pci:index": "rule:admin_api", - "os_compute_api:os-pci:detail": "rule:admin_api", - "os_compute_api:os-pci:show": "rule:admin_api", - "os_compute_api:os-personality:discoverable": "@", - "os_compute_api:os-preserve-ephemeral-rebuild:discoverable": "@", - "os_compute_api:os-quota-sets:discoverable": "@", - "os_compute_api:os-quota-sets:show": "rule:admin_or_owner", - "os_compute_api:os-quota-sets:defaults": "@", - "os_compute_api:os-quota-sets:update": "rule:admin_api", - "os_compute_api:os-quota-sets:delete": "rule:admin_api", - "os_compute_api:os-quota-sets:detail": "rule:admin_api", - "os_compute_api:os-quota-class-sets:update": "rule:admin_api", - "os_compute_api:os-quota-class-sets:show": "is_admin:True or quota_class:%(quota_class)s", - "os_compute_api:os-quota-class-sets:discoverable": "@", - "os_compute_api:os-rescue": "rule:admin_or_owner", - "os_compute_api:os-rescue:discoverable": "@", - "os_compute_api:os-scheduler-hints:discoverable": "@", - "os_compute_api:os-security-group-default-rules:discoverable": "@", - "os_compute_api:os-security-group-default-rules": "rule:admin_api", - "os_compute_api:os-security-groups": "rule:admin_or_owner", - "os_compute_api:os-security-groups:discoverable": "@", - "os_compute_api:os-server-diagnostics": "rule:admin_api", - "os_compute_api:os-server-diagnostics:discoverable": "@", - "os_compute_api:os-server-password": "rule:admin_or_owner", - "os_compute_api:os-server-password:discoverable": "@", - "os_compute_api:os-server-usage": "rule:admin_or_owner", - "os_compute_api:os-server-usage:discoverable": "@", - "os_compute_api:os-server-groups": "rule:admin_or_owner", - "os_compute_api:os-server-groups:discoverable": "@", - "os_compute_api:os-services": "rule:admin_api", - "os_compute_api:os-services:discoverable": "@", - "os_compute_api:server-metadata:discoverable": "@", - "os_compute_api:server-metadata:index": "rule:admin_or_owner", - "os_compute_api:server-metadata:show": "rule:admin_or_owner", - "os_compute_api:server-metadata:delete": "rule:admin_or_owner", - "os_compute_api:server-metadata:create": "rule:admin_or_owner", - "os_compute_api:server-metadata:update": "rule:admin_or_owner", - "os_compute_api:server-metadata:update_all": "rule:admin_or_owner", - "os_compute_api:os-shelve:shelve": "rule:admin_or_owner", - "os_compute_api:os-shelve:shelve:discoverable": "@", - "os_compute_api:os-shelve:shelve_offload": "rule:admin_api", - "os_compute_api:os-simple-tenant-usage:discoverable": "@", - "os_compute_api:os-simple-tenant-usage:show": "rule:admin_or_owner", - "os_compute_api:os-simple-tenant-usage:list": "rule:admin_api", - "os_compute_api:os-suspend-server:discoverable": "@", - "os_compute_api:os-suspend-server:suspend": "rule:admin_or_owner", - "os_compute_api:os-suspend-server:resume": "rule:admin_or_owner", - "os_compute_api:os-tenant-networks": "rule:admin_or_owner", - "os_compute_api:os-tenant-networks:discoverable": "@", - "os_compute_api:os-shelve:unshelve": "rule:admin_or_owner", - "os_compute_api:os-user-data:discoverable": "@", - "os_compute_api:os-virtual-interfaces": "rule:admin_or_owner", - "os_compute_api:os-virtual-interfaces:discoverable": "@", - "os_compute_api:os-volumes": "rule:admin_or_owner", - "os_compute_api:os-volumes:discoverable": "@", - "os_compute_api:os-volumes-attachments:index": "rule:admin_or_owner", - "os_compute_api:os-volumes-attachments:show": "rule:admin_or_owner", - "os_compute_api:os-volumes-attachments:create": "rule:admin_or_owner", - "os_compute_api:os-volumes-attachments:update": "rule:admin_or_owner", - "os_compute_api:os-volumes-attachments:delete": "rule:admin_or_owner", - "os_compute_api:os-volumes-attachments:discoverable": "@", - "os_compute_api:os-availability-zone:list": "rule:admin_or_owner", - "os_compute_api:os-availability-zone:discoverable": "@", - "os_compute_api:os-availability-zone:detail": "rule:admin_api", - "os_compute_api:os-used-limits": "rule:admin_api", - "os_compute_api:os-used-limits:discoverable": "@", - "os_compute_api:os-migrations:index": "rule:admin_api", - "os_compute_api:os-migrations:discoverable": "@", - "os_compute_api:os-assisted-volume-snapshots:create": "rule:admin_api", - "os_compute_api:os-assisted-volume-snapshots:delete": "rule:admin_api", - "os_compute_api:os-assisted-volume-snapshots:discoverable": "@", - "os_compute_api:os-console-auth-tokens": "rule:admin_api", - "os_compute_api:os-server-external-events:create": "rule:admin_api" -} diff --git a/etc/nova/rootwrap.d/compute.filters b/etc/nova/rootwrap.d/compute.filters index c846b89ecda..33e0360b256 100644 --- a/etc/nova/rootwrap.d/compute.filters +++ b/etc/nova/rootwrap.d/compute.filters @@ -14,7 +14,6 @@ tune2fs: CommandFilter, tune2fs, root # nova/virt/disk/api.py: 'mount', '-o', 'bind', src, target # nova/virt/xenapi/vm_utils.py: 'mount', '-t', 'ext2,ext3,ext4,reiserfs'.. # nova/virt/configdrive.py: 'mount', device, mountdir -# nova/virt/libvirt/volume.py: 'mount', '-t', 'sofs' ... mount: CommandFilter, mount, root # nova/virt/disk/mount/api.py: 'umount', mapped_device @@ -39,15 +38,18 @@ blkid: CommandFilter, blkid, root blockdev: RegExpFilter, blockdev, root, blockdev, (--getsize64|--flushbufs), /dev/.* # nova/virt/disk/vfs/localfs.py: 'tee', canonpath +# nova/virt/libvirt/guest.py: 'tee', +# nova/virt/libvirt/vif.py: utils.execute('tee', tee: CommandFilter, tee, root # nova/virt/disk/vfs/localfs.py: 'mkdir', canonpath mkdir: CommandFilter, mkdir, root # nova/virt/disk/vfs/localfs.py: 'chown' -# nova/virt/libvirt/connection.py: 'chown', os.getuid( console_log -# nova/virt/libvirt/connection.py: 'chown', os.getuid( console_log -# nova/virt/libvirt/connection.py: 'chown', 'root', basepath('disk') +# nova/virt/libvirt/utils.py: def chown(): execute('chown', owner, path, +# nova/virt/libvirt/driver.py: 'chown', os.getuid( console_log +# nova/virt/libvirt/driver.py: 'chown', os.getuid( console_log +# nova/virt/libvirt/driver.py: 'chown', 'root', basepath('disk') chown: CommandFilter, chown, root # nova/virt/disk/vfs/localfs.py: 'chmod' @@ -86,6 +88,9 @@ tunctl: CommandFilter, tunctl, root # nova/network/linux_net.py: 'ovs-vsctl', .... ovs-vsctl: CommandFilter, ovs-vsctl, root +# nova/network/linux_net.py: 'ivs-ctl', .... +ivs-ctl: CommandFilter, ivs-ctl, root + # nova/virt/libvirt/vif.py: 'vrouter-port-control', ... vrouter-port-control: CommandFilter, vrouter-port-control, root @@ -98,7 +103,7 @@ mm-ctl: CommandFilter, mm-ctl, root # nova/network/linux_net.py: 'ovs-ofctl', .... ovs-ofctl: CommandFilter, ovs-ofctl, root -# nova/virt/libvirt/connection.py: 'dd', if=%s % virsh_output, ... +# nova/virt/libvirt/driver.py: 'dd', if=%s % virsh_output, ... dd: CommandFilter, dd, root # nova/virt/xenapi/volume_utils.py: 'iscsiadm', '-m', ... @@ -183,7 +188,7 @@ mkfs.ext3: CommandFilter, mkfs.ext3, root mkfs.ext4: CommandFilter, mkfs.ext4, root mkfs.ntfs: CommandFilter, mkfs.ntfs, root -# nova/virt/libvirt/connection.py: +# nova/virt/libvirt/driver.py: lvremove: CommandFilter, lvremove, root # nova/virt/libvirt/utils.py: @@ -195,7 +200,7 @@ lvs: CommandFilter, lvs, root # nova/virt/libvirt/utils.py: vgs: CommandFilter, vgs, root -# nova/utils.py:read_file_as_root: 'cat', file_path +# nova/utils.py: read_file_as_root: 'cat', file_path # (called from nova/virt/disk/vfs/localfs.py:VFSLocalFS.read_file) read_passwd: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/passwd read_shadow: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/shadow @@ -214,16 +219,12 @@ drv_cfg: CommandFilter, /opt/emc/scaleio/sdc/bin/drv_cfg, root, /opt/emc/scaleio # Need to pull in os-brick os-brick.filters file instead and clean # out stale brick values from this file. scsi_id: CommandFilter, /lib/udev/scsi_id, root +# os_brick.privileged.default oslo.privsep context +# This line ties the superuser privs with the config files, context name, +# and (implicitly) the actual python code invoked. +privsep-rootwrap: RegExpFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, os_brick.privileged.default, --privsep_sock_path, /tmp/.* -# nova/storage/linuxscsi.py: sg_scan device -sg_scan: CommandFilter, sg_scan, root - -# nova/volume/encryptors/cryptsetup.py: -# nova/volume/encryptors/luks.py: -ln: RegExpFilter, ln, root, ln, --symbolic, --force, /dev/mapper/.*, .* - -# nova/volume/encryptors.py: -# nova/virt/libvirt/dmcrypt.py: +# nova/virt/libvirt/storage/dmcrypt.py: cryptsetup: CommandFilter, cryptsetup, root # nova/virt/xenapi/vm_utils.py: @@ -235,17 +236,24 @@ rbd: CommandFilter, rbd, root # nova/virt/libvirt/utils.py: 'shred', '-n3', '-s%d' % volume_size, path shred: CommandFilter, shred, root -# nova/virt/libvirt/volume.py: 'cp', '/dev/stdin', delete_control.. +# nova/virt/libvirt/volume/volume.py: 'cp', '/dev/stdin', delete_control.. cp: CommandFilter, cp, root # nova/virt/xenapi/vm_utils.py: sync: CommandFilter, sync, root # nova/virt/libvirt/imagebackend.py: -ploop: CommandFilter, ploop, root +ploop: RegExpFilter, ploop, root, ploop, restore-descriptor, .* +prl_disk_tool: RegExpFilter, prl_disk_tool, root, prl_disk_tool, resize, --size, .*M$, --resize_partition, --hdd, .* + +# nova/virt/libvirt/utils.py: +ploop: RegExpFilter, ploop, root, ploop, init, -s, .*, -f, .*, -t, .*, .* # nova/virt/libvirt/utils.py: 'xend', 'status' xend: CommandFilter, xend, root # nova/virt/libvirt/utils.py: touch: CommandFilter, touch, root + +# nova/virt/libvirt/volume/vzstorage.py +pstorage-mount: CommandFilter, pstorage-mount, root diff --git a/gate/README b/gate/README new file mode 100644 index 00000000000..b7df206546b --- /dev/null +++ b/gate/README @@ -0,0 +1,4 @@ +These are hooks to be used by the OpenStack infra test system. These scripts +may be called by certain jobs at important times to do extra testing, setup, +etc. They are really only relevant within the scope of the OpenStack infra +system and are not expected to be useful to anyone else. diff --git a/gate/post_test_hook.sh b/gate/post_test_hook.sh new file mode 100755 index 00000000000..5f72eb378d7 --- /dev/null +++ b/gate/post_test_hook.sh @@ -0,0 +1,21 @@ +#!/bin/bash -x + +MANAGE="/usr/local/bin/nova-manage" + +function archive_deleted_rows { + # NOTE(danms): Run this a few times to make sure that we end + # up with nothing more to archive + for i in `seq 30`; do + $MANAGE db archive_deleted_rows --verbose --max_rows 1000 + RET=$? + if [[ $RET -gt 1 ]]; then + echo Archiving failed with result $RET + return $RET + elif [[ $RET -eq 0 ]]; then + echo Archiving Complete + break; + fi + done +} + +archive_deleted_rows diff --git a/nova/__init__.py b/nova/__init__.py index 21403ab570a..228c89d33dc 100644 --- a/nova/__init__.py +++ b/nova/__init__.py @@ -27,4 +27,9 @@ os.environ['EVENTLET_NO_GREENDNS'] = 'yes' +# NOTE(rpodolyaka): import oslo_service first, so that it makes eventlet hub +# use a monotonic clock to avoid issues with drifts of system time (see +# LP 1510234 for details) +import oslo_service # noqa + import eventlet # noqa diff --git a/nova/api/auth.py b/nova/api/auth.py index 0c8fe7c282b..c59b2d502d8 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -16,41 +16,19 @@ """ -from oslo_config import cfg from oslo_log import log as logging -from oslo_middleware import request_id +from oslo_log import versionutils from oslo_serialization import jsonutils import webob.dec import webob.exc +import nova.conf from nova import context from nova.i18n import _ from nova import wsgi -auth_opts = [ - cfg.BoolOpt('api_rate_limit', - default=False, - help='Whether to use per-user rate limiting for the api. ' - 'This option is only used by v2 api. Rate limiting ' - 'is removed from v2.1 api.'), - cfg.StrOpt('auth_strategy', - default='keystone', - choices=('keystone', 'noauth2'), - help=''' -The strategy to use for auth: keystone or noauth2. noauth2 is designed for -testing only, as it does no actual credential checking. noauth2 provides -administrative credentials only if 'admin' is specified as the username. -'''), - cfg.BoolOpt('use_forwarded_for', - default=False, - help='Treat X-Forwarded-For as the canonical remote address. ' - 'Only enable this if you have a sanitizing proxy.'), -] - -CONF = cfg.CONF -CONF.register_opts(auth_opts) - +CONF = nova.conf.CONF LOG = logging.getLogger(__name__) @@ -65,18 +43,17 @@ def _load_pipeline(loader, pipeline): def pipeline_factory(loader, global_conf, **local_conf): """A paste pipeline replica that keys off of auth_strategy.""" - - pipeline = local_conf[CONF.auth_strategy] - if not CONF.api_rate_limit: - limit_name = CONF.auth_strategy + '_nolimit' - pipeline = local_conf.get(limit_name, pipeline) - pipeline = pipeline.split() - return _load_pipeline(loader, pipeline) + versionutils.report_deprecated_feature( + LOG, + "The legacy V2 API code tree has been removed in Newton. " + "Please remove legacy v2 API entry from api-paste.ini, and use " + "V2.1 API or V2.1 API compat mode instead" + ) def pipeline_factory_v21(loader, global_conf, **local_conf): """A paste pipeline replica that keys off of auth_strategy.""" - return _load_pipeline(loader, local_conf[CONF.auth_strategy].split()) + return _load_pipeline(loader, local_conf[CONF.api.auth_strategy].split()) class InjectContext(wsgi.Middleware): @@ -97,32 +74,9 @@ class NovaKeystoneContext(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): - user_id = req.headers.get('X_USER') - user_id = req.headers.get('X_USER_ID', user_id) - if user_id is None: - LOG.debug("Neither X_USER_ID nor X_USER found in request") - return webob.exc.HTTPUnauthorized() - - roles = self._get_roles(req) - - if 'X_TENANT_ID' in req.headers: - # This is the new header since Keystone went to ID/Name - project_id = req.headers['X_TENANT_ID'] - else: - # This is for legacy compatibility - project_id = req.headers['X_TENANT'] - project_name = req.headers.get('X_TENANT_NAME') - user_name = req.headers.get('X_USER_NAME') - - req_id = req.environ.get(request_id.ENV_REQUEST_ID) - - # Get the auth token - auth_token = req.headers.get('X_AUTH_TOKEN', - req.headers.get('X_STORAGE_TOKEN')) - # Build a context, including the auth_token... remote_address = req.remote_addr - if CONF.use_forwarded_for: + if CONF.api.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) service_catalog = None @@ -138,22 +92,15 @@ def __call__(self, req): # middleware in newer versions. user_auth_plugin = req.environ.get('keystone.token_auth') - ctx = context.RequestContext(user_id, - project_id, - user_name=user_name, - project_name=project_name, - roles=roles, - auth_token=auth_token, - remote_address=remote_address, - service_catalog=service_catalog, - request_id=req_id, - user_auth_plugin=user_auth_plugin) + ctx = context.RequestContext.from_environ( + req.environ, + user_auth_plugin=user_auth_plugin, + remote_address=remote_address, + service_catalog=service_catalog) + + if ctx.user_id is None: + LOG.debug("Neither X_USER_ID nor X_USER found in request") + return webob.exc.HTTPUnauthorized() req.environ['nova.context'] = ctx return self.application - - def _get_roles(self, req): - """Get the list of roles.""" - - roles = req.headers.get('X_ROLES', '') - return [r.strip() for r in roles.split(',')] diff --git a/nova/api/compute_req_id.py b/nova/api/compute_req_id.py index 21985807c33..0b9301b5178 100644 --- a/nova/api/compute_req_id.py +++ b/nova/api/compute_req_id.py @@ -15,30 +15,22 @@ """Middleware that ensures x-compute-request-id -Using this middleware provides a convenient way to attach the -x-compute-request-id to only v2 responses. Previously, this header was set in -api/openstack/wsgi.py +Nova's notion of request-id tracking predates any common idea, so the +original version of this header in OpenStack was +x-compute-request-id. Eventually we got oslo, and all other projects +implemented this with x-openstack-request-id. + +However, x-compute-request-id was always part of our contract. The +following migrates us to use x-openstack-request-id as well, by using +the common middleware. -Responses for v2.1 API are taken care of by the request_id middleware provided -in oslo. """ -from oslo_context import context -from oslo_middleware import base -import webob.dec +from oslo_middleware import request_id -ENV_REQUEST_ID = 'openstack.request_id' HTTP_RESP_HEADER_REQUEST_ID = 'x-compute-request-id' -class ComputeReqIdMiddleware(base.Middleware): - - @webob.dec.wsgify - def __call__(self, req): - req_id = context.generate_request_id() - req.environ[ENV_REQUEST_ID] = req_id - response = req.get_response(self.application) - if HTTP_RESP_HEADER_REQUEST_ID not in response.headers: - response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, req_id) - return response +class ComputeReqIdMiddleware(request_id.RequestId): + compat_headers = [HTTP_RESP_HEADER_REQUEST_ID] diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index f9c0a9a599d..e69de29bb2d 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -1,85 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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 webob.dec -import webob.exc - -from oslo_log import log as logging - -from nova.i18n import _LW -from nova import wsgi - -LOG = logging.getLogger(__name__) - -_DEPRECATED_MIDDLEWARE = ( - '%s has been deprecated and removed from Nova in Mitaka. ' - 'You will need to remove lines referencing it in your paste.ini before ' - 'upgrade to Newton or your cloud will break.') - -_DEPRECATION_MESSAGE = ('The in tree EC2 API has been removed in Mitaka. ' - 'Please remove entries from api-paste.ini') - -# NOTE(sdague): this whole file is safe to remove in Newton. We just -# needed a release cycle for it. - - -class DeprecatedMiddleware(wsgi.Middleware): - def __init__(self, *args, **kwargs): - super(DeprecatedMiddleware, self).__init__(args[0]) - LOG.warn(_LW(_DEPRECATED_MIDDLEWARE % type(self).__name__)) # noqa - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - # deprecated middleware needs to be a no op, not an exception - return req.get_response(self.application) - - -class FaultWrapper(DeprecatedMiddleware): - pass - - -class Lockout(DeprecatedMiddleware): - pass - - -class EC2KeystoneAuth(DeprecatedMiddleware): - pass - - -class NoAuth(DeprecatedMiddleware): - pass - - -class Requestify(DeprecatedMiddleware): - pass - - -class Authorizer(DeprecatedMiddleware): - pass - - -class RequestLogging(DeprecatedMiddleware): - pass - - -class Validator(DeprecatedMiddleware): - pass - - -class Executor(wsgi.Application): - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - return webob.exc.HTTPNotFound(explanation=_DEPRECATION_MESSAGE) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 893227e030e..ea88ec0376e 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -17,17 +17,15 @@ from oslo_log import log as logging from oslo_log import versionutils -from nova.i18n import _LW - LOG = logging.getLogger(__name__) class CloudController(object): def __init__(self): versionutils.report_deprecated_feature( - LOG, - _LW('The in tree EC2 API has been removed in Mitaka. ' - 'Please remove entries from api-paste.ini and use ' - 'the OpenStack ec2-api project ' - 'http://git.openstack.org/cgit/openstack/ec2-api/') + LOG, + 'The in tree EC2 API has been removed in Mitaka. ' + 'Please remove entries from api-paste.ini and use ' + 'the OpenStack ec2-api project ' + 'http://git.openstack.org/cgit/openstack/ec2-api/' ) diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py index 3f9af9b7fa9..3134e309e51 100644 --- a/nova/api/ec2/ec2utils.py +++ b/nova/api/ec2/ec2utils.py @@ -26,7 +26,6 @@ from nova import context from nova import exception from nova.i18n import _ -from nova.i18n import _LI from nova.network import model as network_model from nova import objects from nova.objects import base as obj_base @@ -289,7 +288,7 @@ def parse_strtime(strtime): timeutils.is_newer_than(query_time, expires) return False except ValueError: - LOG.info(_LI("Timestamp is invalid.")) + LOG.info("Timestamp is invalid.") return True diff --git a/nova/api/manager.py b/nova/api/manager.py index f254475cc2d..32f800e1b5a 100644 --- a/nova/api/manager.py +++ b/nova/api/manager.py @@ -16,6 +16,7 @@ from nova import manager from nova.network import driver +from nova import utils class MetadataManager(manager.Manager): @@ -26,5 +27,10 @@ class MetadataManager(manager.Manager): """ def __init__(self, *args, **kwargs): super(MetadataManager, self).__init__(*args, **kwargs) - self.network_driver = driver.load_network_driver() - self.network_driver.metadata_accept() + + if not utils.is_neutron(): + # NOTE(mikal): we only add iptables rules if we're running + # under nova-network. This code should go away when the + # deprecation of nova-network is complete. + self.network_driver = driver.load_network_driver() + self.network_driver.metadata_accept() diff --git a/nova/api/metadata/base.py b/nova/api/metadata/base.py index 2722dfc9a7d..f83bcf8e6c1 100644 --- a/nova/api/metadata/base.py +++ b/nova/api/metadata/base.py @@ -16,12 +16,11 @@ """Instance Metadata information.""" -import base64 import os import posixpath -from oslo_config import cfg from oslo_log import log as logging +from oslo_serialization import base64 from oslo_serialization import jsonutils from oslo_utils import importutils from oslo_utils import timeutils @@ -29,34 +28,24 @@ from nova.api.ec2 import ec2utils from nova.api.metadata import password -from nova import availability_zones as az +from nova.api.metadata import vendordata +from nova.api.metadata import vendordata_dynamic +from nova.api.metadata import vendordata_json from nova import block_device from nova.cells import opts as cells_opts from nova.cells import rpcapi as cells_rpcapi +import nova.conf from nova import context +from nova import exception from nova import network from nova.network.security_group import openstack_driver from nova import objects -from nova.objects import keypair as keypair_obj +from nova.objects import virt_device_metadata as metadata_obj from nova import utils from nova.virt import netutils -metadata_opts = [ - cfg.StrOpt('config_drive_skip_versions', - default=('1.0 2007-01-19 2007-03-01 2007-08-29 2007-10-10 ' - '2007-12-15 2008-02-01 2008-09-01'), - help='List of metadata versions to skip placing into the ' - 'config drive'), - cfg.StrOpt('vendordata_driver', - default='nova.api.metadata.vendordata_json.JsonFileVendorData', - help='DEPRECATED: Driver to use for vendor data', - deprecated_for_removal=True), -] - -CONF = cfg.CONF -CONF.register_opts(metadata_opts) -CONF.import_opt('dhcp_domain', 'nova.network.manager') +CONF = nova.conf.CONF VERSIONS = [ '1.0', @@ -70,16 +59,28 @@ '2009-04-04', ] +# NOTE(mikal): think of these strings as version numbers. They traditionally +# correlate with OpenStack release dates, with all the changes for a given +# release bundled into a single version. Note that versions in the future are +# hidden from the listing, but can still be requested explicitly, which is +# required for testing purposes. We know this isn't great, but its inherited +# from EC2, which this needs to be compatible with. FOLSOM = '2012-08-10' GRIZZLY = '2013-04-04' HAVANA = '2013-10-17' LIBERTY = '2015-10-15' +NEWTON_ONE = '2016-06-30' +NEWTON_TWO = '2016-10-06' +OCATA = '2017-02-22' OPENSTACK_VERSIONS = [ FOLSOM, GRIZZLY, HAVANA, LIBERTY, + NEWTON_ONE, + NEWTON_TWO, + OCATA, ] VERSION = "version" @@ -87,6 +88,7 @@ CONTENT_DIR = "content" MD_JSON_NAME = "meta_data.json" VD_JSON_NAME = "vendor_data.json" +VD2_JSON_NAME = "vendor_data2.json" NW_JSON_NAME = "network_data.json" UD_NAME = "user_data" PASS_NAME = "password" @@ -108,7 +110,8 @@ class InstanceMetadata(object): """Instance metadata.""" def __init__(self, instance, address=None, content=None, extra_md=None, - network_info=None, vd_driver=None, network_metadata=None): + network_info=None, vd_driver=None, network_metadata=None, + request_context=None): """Creation of this object should basically cover all time consuming collection. Methods after that should not cause time delays due to network operations or lengthy cpu operations. @@ -121,13 +124,22 @@ def __init__(self, instance, address=None, content=None, extra_md=None, ctxt = context.get_admin_context() + # NOTE(danms): Sanitize the instance to limit the amount of stuff + # inside that may not pickle well (i.e. context). We also touch + # some of the things we'll lazy load later to make sure we keep their + # values in what we cache. + instance.ec2_ids + instance.keypairs + instance.device_metadata + instance = objects.Instance.obj_from_primitive( + instance.obj_to_primitive()) + # The default value of mimeType is set to MIME_TYPE_TEXT_PLAIN self.set_mimetype(MIME_TYPE_TEXT_PLAIN) self.instance = instance self.extra_md = extra_md - self.availability_zone = az.get_instance_availability_zone(ctxt, - instance) + self.availability_zone = instance.get('availability_zone') secgroup_api = openstack_driver.get_openstack_security_group_driver() self.security_groups = secgroup_api.get_instance_security_groups( @@ -136,7 +148,7 @@ def __init__(self, instance, address=None, content=None, extra_md=None, self.mappings = _format_instance_mapping(ctxt, instance) if instance.user_data is not None: - self.userdata_raw = base64.b64decode(instance.user_data) + self.userdata_raw = base64.decode_as_bytes(instance.user_data) else: self.userdata_raw = None @@ -194,6 +206,19 @@ def __init__(self, instance, address=None, content=None, extra_md=None, self.route_configuration = None + # NOTE(mikal): the decision to not pass extra_md here like we + # do to the StaticJSON driver is deliberate. extra_md will + # contain the admin password for the instance, and we shouldn't + # pass that to external services. + self.vendordata_providers = { + 'StaticJSON': vendordata_json.JsonFileVendorData( + instance=instance, address=address, + extra_md=extra_md, network_info=network_info), + 'DynamicJSON': vendordata_dynamic.DynamicVendorData( + instance=instance, address=address, + network_info=network_info, context=request_context) + } + def _route_configuration(self): if self.route_configuration: return self.route_configuration @@ -201,6 +226,7 @@ def _route_configuration(self): path_handlers = {UD_NAME: self._user_data, PASS_NAME: self._password, VD_JSON_NAME: self._vendor_data, + VD2_JSON_NAME: self._vendor_data2, MD_JSON_NAME: self._metadata_as_json, NW_JSON_NAME: self._network_data, VERSION: self._handle_version, @@ -313,25 +339,40 @@ def _metadata_as_json(self, version, path): metadata.update(self.extra_md) if self.network_config: metadata['network_config'] = self.network_config - if self.instance.key_name: - metadata['public_keys'] = { - self.instance.key_name: self.instance.key_data - } + if self.instance.key_name: if cells_opts.get_cell_type() == 'compute': cells_api = cells_rpcapi.CellsAPI() - keypair = cells_api.get_keypair_at_top( - context.get_admin_context(), self.instance.user_id, - self.instance.key_name) + try: + keypair = cells_api.get_keypair_at_top( + context.get_admin_context(), self.instance.user_id, + self.instance.key_name) + except exception.KeypairNotFound: + # NOTE(lpigueir): If keypair was deleted, treat + # it like it never had any + keypair = None + else: + keypairs = self.instance.keypairs + # NOTE(mriedem): It's possible for the keypair to be deleted + # before it was migrated to the instance_extra table, in which + # case lazy-loading instance.keypairs will handle the 404 and + # just set an empty KeyPairList object on the instance. + keypair = keypairs[0] if keypairs else None + + if keypair: + metadata['public_keys'] = { + keypair.name: keypair.public_key, + } + + metadata['keys'] = [ + {'name': keypair.name, + 'type': keypair.type, + 'data': keypair.public_key} + ] else: - keypair = keypair_obj.KeyPair.get_by_name( - context.get_admin_context(), self.instance.user_id, - self.instance.key_name) - metadata['keys'] = [ - {'name': keypair.name, - 'type': keypair.type, - 'data': keypair.public_key} - ] + LOG.debug("Unable to find keypair for instance with " + "key name '%s'.", self.instance.key_name, + instance=self.instance) metadata['hostname'] = self._get_hostname() metadata['name'] = self.instance.display_name @@ -339,14 +380,89 @@ def _metadata_as_json(self, version, path): metadata['availability_zone'] = self.availability_zone if self._check_os_version(GRIZZLY, version): - metadata['random_seed'] = base64.b64encode(os.urandom(512)) + metadata['random_seed'] = base64.encode_as_text(os.urandom(512)) if self._check_os_version(LIBERTY, version): metadata['project_id'] = self.instance.project_id + if self._check_os_version(NEWTON_ONE, version): + metadata['devices'] = self._get_device_metadata(version) + self.set_mimetype(MIME_TYPE_APPLICATION_JSON) return jsonutils.dump_as_bytes(metadata) + def _get_device_metadata(self, version): + """Build a device metadata dict based on the metadata objects. This is + done here in the metadata API as opposed to in the objects themselves + because the metadata dict is part of the guest API and thus must be + controlled. + """ + device_metadata_list = [] + vif_vlans_supported = self._check_os_version(OCATA, version) + if self.instance.device_metadata is not None: + for device in self.instance.device_metadata.devices: + device_metadata = {} + bus = 'none' + address = 'none' + + if 'bus' in device: + # TODO(artom/mriedem) It would be nice if we had something + # more generic, like a type identifier or something, built + # into these types of objects, like a get_meta_type() + # abstract method on the base DeviceBus class. + if isinstance(device.bus, metadata_obj.PCIDeviceBus): + bus = 'pci' + elif isinstance(device.bus, metadata_obj.USBDeviceBus): + bus = 'usb' + elif isinstance(device.bus, metadata_obj.SCSIDeviceBus): + bus = 'scsi' + elif isinstance(device.bus, metadata_obj.IDEDeviceBus): + bus = 'ide' + elif isinstance(device.bus, metadata_obj.XenDeviceBus): + bus = 'xen' + else: + LOG.debug('Metadata for device with unknown bus %s ' + 'has not been included in the ' + 'output', device.bus.__class__.__name__) + continue + if 'address' in device.bus: + address = device.bus.address + + if isinstance(device, metadata_obj.NetworkInterfaceMetadata): + vlan = None + if vif_vlans_supported and 'vlan' in device: + vlan = device.vlan + + # Skip devices without tags on versions that + # don't support vlans + if not (vlan or 'tags' in device): + continue + + device_metadata['type'] = 'nic' + device_metadata['mac'] = device.mac + if vlan: + device_metadata['vlan'] = vlan + elif isinstance(device, metadata_obj.DiskMetadata): + device_metadata['type'] = 'disk' + # serial and path are optional parameters + if 'serial' in device: + device_metadata['serial'] = device.serial + if 'path' in device: + device_metadata['path'] = device.path + else: + LOG.debug('Metadata for device of unknown type %s has not ' + 'been included in the ' + 'output', device.__class__.__name__) + continue + + device_metadata['bus'] = bus + device_metadata['address'] = address + if 'tags' in device: + device_metadata['tags'] = device.tags + + device_metadata_list.append(device_metadata) + return device_metadata_list + def _handle_content(self, path_tokens): if len(path_tokens) == 1: raise KeyError("no listing for %s" % "/".join(path_tokens)) @@ -365,6 +481,8 @@ def _handle_version(self, version, path): ret.append(VD_JSON_NAME) if self._check_os_version(LIBERTY, version): ret.append(NW_JSON_NAME) + if self._check_os_version(NEWTON_TWO, version): + ret.append(VD2_JSON_NAME) return ret @@ -386,7 +504,43 @@ def _password(self, version, path): def _vendor_data(self, version, path): if self._check_os_version(HAVANA, version): self.set_mimetype(MIME_TYPE_APPLICATION_JSON) - return jsonutils.dump_as_bytes(self.vddriver.get()) + + # NOTE(mikal): backwards compatibility... If the deployer has + # specified providers, and one of those providers is StaticJSON, + # then do that thing here. Otherwise, if the deployer has + # specified an old style driver here, then use that. This second + # bit can be removed once old style vendordata is fully deprecated + # and removed. + if (CONF.api.vendordata_providers and + 'StaticJSON' in CONF.api.vendordata_providers): + return jsonutils.dump_as_bytes( + self.vendordata_providers['StaticJSON'].get()) + else: + # TODO(mikal): when we removed the old style vendordata + # drivers, we need to remove self.vddriver as well. + return jsonutils.dump_as_bytes(self.vddriver.get()) + + raise KeyError(path) + + def _vendor_data2(self, version, path): + if self._check_os_version(NEWTON_TWO, version): + self.set_mimetype(MIME_TYPE_APPLICATION_JSON) + + j = {} + for provider in CONF.api.vendordata_providers: + if provider == 'StaticJSON': + j['static'] = self.vendordata_providers['StaticJSON'].get() + else: + values = self.vendordata_providers[provider].get() + for key in list(values): + if key in j: + LOG.warning('Removing duplicate metadata key: %s', + key, instance=self.instance) + del values[key] + j.update(values) + + return jsonutils.dump_as_bytes(j) + raise KeyError(path) def _check_version(self, required, requested, versions=VERSIONS): @@ -430,7 +584,7 @@ def lookup(self, path): if OPENSTACK_VERSIONS != versions: LOG.debug("future versions %s hidden in version list", [v for v in OPENSTACK_VERSIONS - if v not in versions]) + if v not in versions], instance=self.instance) versions += ["latest"] else: versions = VERSIONS + ["latest"] @@ -450,7 +604,7 @@ def metadata_for_config_drive(self): """Yields (path, value) tuples for metadata elements.""" # EC2 style metadata for version in VERSIONS + ["latest"]: - if version in CONF.config_drive_skip_versions.split(' '): + if version in CONF.api.config_drive_skip_versions.split(' '): continue data = self.get_ec2_metadata(version) @@ -484,7 +638,12 @@ def metadata_for_config_drive(self): path = 'openstack/%s/%s' % (version, NW_JSON_NAME) yield (path, self.lookup(path)) - for (cid, content) in six.iteritems(self.content): + if self._check_version(NEWTON_TWO, version, + ALL_OPENSTACK_VERSIONS): + path = 'openstack/%s/%s' % (version, VD2_JSON_NAME) + yield (path, self.lookup(path)) + + for (cid, content) in self.content.items(): yield ('%s/%s/%s' % ("openstack", CONTENT_DIR, cid), content) @@ -518,24 +677,11 @@ def handle_path(self, path_tokens): return path_handler(version, path) -class VendorDataDriver(object): - """The base VendorData Drivers should inherit from.""" - - def __init__(self, *args, **kwargs): - """Init method should do all expensive operations.""" - self._data = {} - - def get(self): - """Return a dictionary of primitives to be rendered in metadata - - :return: A dictionary or primitives. - """ - return self._data - - def get_metadata_by_address(address): ctxt = context.get_admin_context() fixed_ip = network.API().get_fixed_ip_by_address(ctxt, address) + LOG.info('Fixed IP %(ip)s translates to instance UUID %(uuid)s', + {'ip': address, 'uuid': fixed_ip['instance_uuid']}) return get_metadata_by_instance_id(fixed_ip['instance_uuid'], address, @@ -544,11 +690,23 @@ def get_metadata_by_address(address): def get_metadata_by_instance_id(instance_id, address, ctxt=None): ctxt = ctxt or context.get_admin_context() - instance = objects.Instance.get_by_uuid( - ctxt, instance_id, expected_attrs=['ec2_ids', 'flavor', 'info_cache', - 'metadata', 'system_metadata', - 'security_groups']) - return InstanceMetadata(instance, address) + attrs = ['ec2_ids', 'flavor', 'info_cache', + 'metadata', 'system_metadata', + 'security_groups', 'keypairs', + 'device_metadata'] + try: + im = objects.InstanceMapping.get_by_instance_uuid(ctxt, instance_id) + except exception.InstanceMappingNotFound: + LOG.warning('Instance mapping for %(uuid)s not found; ' + 'cell setup is incomplete', {'uuid': instance_id}) + instance = objects.Instance.get_by_uuid(ctxt, instance_id, + expected_attrs=attrs) + return InstanceMetadata(instance, address) + + with context.target_cell(ctxt, im.cell_mapping) as cctxt: + instance = objects.Instance.get_by_uuid(cctxt, instance_id, + expected_attrs=attrs) + return InstanceMetadata(instance, address) def _format_instance_mapping(ctxt, instance): @@ -575,6 +733,8 @@ def ec2_md_print(data): return output[:-1] elif isinstance(data, list): return '\n'.join(data) + elif isinstance(data, (bytes, six.text_type)): + return data else: return str(data) @@ -592,3 +752,8 @@ def find_path_in_tree(data, path_tokens): raise KeyError("/".join(path_tokens[0:i])) data = data[path_tokens[i]] return data + + +# NOTE(mikal): this alias is to stop old style vendordata plugins from breaking +# post refactor. It should be removed when we finish deprecating those plugins. +VendorDataDriver = vendordata.VendorDataDriver diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py index 7e9c36aaf11..409ed157f9a 100644 --- a/nova/api/metadata/handler.py +++ b/nova/api/metadata/handler.py @@ -19,8 +19,8 @@ import hmac import os -from oslo_config import cfg from oslo_log import log as logging +from oslo_utils import encodeutils from oslo_utils import secretutils as secutils import six import webob.dec @@ -28,43 +28,14 @@ from nova.api.metadata import base from nova import cache_utils +import nova.conf from nova import context as nova_context from nova import exception from nova.i18n import _ -from nova.i18n import _LE -from nova.i18n import _LW from nova.network.neutronv2 import api as neutronapi from nova import wsgi -CONF = cfg.CONF -CONF.import_opt('use_forwarded_for', 'nova.api.auth') - -metadata_proxy_opts = [ - cfg.BoolOpt( - 'service_metadata_proxy', - default=False, - help='Set flag to indicate Neutron will proxy metadata requests and ' - 'resolve instance ids.'), - cfg.StrOpt( - 'metadata_proxy_shared_secret', - default='', secret=True, - help='Shared secret to validate proxies Neutron metadata requests'), -] - -metadata_opts = [ - cfg.IntOpt('metadata_cache_expiration', - default=15, - help='Time in seconds to cache metadata; 0 to disable ' - 'metadata caching entirely (not recommended). Increasing' - 'this should improve response times of the metadata API ' - 'when under heavy load. Higher values may increase memory' - 'usage and result in longer times for host metadata ' - 'changes to take effect.') -] - -CONF.register_opts(metadata_proxy_opts, 'neutron') -CONF.register_opts(metadata_opts) - +CONF = nova.conf.CONF LOG = logging.getLogger(__name__) @@ -73,7 +44,12 @@ class MetadataRequestHandler(wsgi.Application): def __init__(self): self._cache = cache_utils.get_client( - expiration_time=CONF.metadata_cache_expiration) + expiration_time=CONF.api.metadata_cache_expiration) + if (CONF.neutron.service_metadata_proxy and + not CONF.neutron.metadata_proxy_shared_secret): + LOG.warning("metadata_proxy_shared_secret is not configured, " + "the metadata information returned by the proxy " + "cannot be trusted") def get_metadata_by_remote_address(self, address): if not address: @@ -90,7 +66,7 @@ def get_metadata_by_remote_address(self, address): except exception.NotFound: return None - if CONF.metadata_cache_expiration > 0: + if CONF.api.metadata_cache_expiration > 0: self._cache.set(cache_key, data) return data @@ -107,7 +83,7 @@ def get_metadata_by_instance_id(self, instance_id, address): except exception.NotFound: return None - if CONF.metadata_cache_expiration > 0: + if CONF.api.metadata_cache_expiration > 0: self._cache.set(cache_key, data) return data @@ -116,7 +92,7 @@ def get_metadata_by_instance_id(self, instance_id, address): def __call__(self, req): if os.path.normpath(req.path_info) == "/": resp = base.ec2_md_print(base.VERSIONS + ["latest"]) - req.response.body = resp + req.response.body = encodeutils.to_utf8(resp) req.response.content_type = base.MIME_TYPE_TEXT_PLAIN return req.response @@ -128,9 +104,9 @@ def __call__(self, req): else: if req.headers.get('X-Instance-ID'): LOG.warning( - _LW("X-Instance-ID present in request headers. The " - "'service_metadata_proxy' option must be " - "enabled to process this header.")) + "X-Instance-ID present in request headers. The " + "'service_metadata_proxy' option must be " + "enabled to process this header.") meta_data = self._handle_remote_ip_request(req) if meta_data is None: @@ -145,23 +121,20 @@ def __call__(self, req): return data(req, meta_data) resp = base.ec2_md_print(data) - if isinstance(resp, six.text_type): - req.response.text = resp - else: - req.response.body = resp + req.response.body = encodeutils.to_utf8(resp) req.response.content_type = meta_data.get_mimetype() return req.response def _handle_remote_ip_request(self, req): remote_address = req.remote_addr - if CONF.use_forwarded_for: + if CONF.api.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) try: meta_data = self.get_metadata_by_remote_address(remote_address) except Exception: - LOG.exception(_LE('Failed to get metadata for IP: %s'), + LOG.exception('Failed to get metadata for IP %s', remote_address) msg = _('An unknown error has occurred. ' 'Please try your request again.') @@ -169,7 +142,7 @@ def _handle_remote_ip_request(self, req): explanation=six.text_type(msg)) if meta_data is None: - LOG.error(_LE('Failed to get metadata for IP: %s'), + LOG.error('Failed to get metadata for IP %s: no metadata', remote_address) return meta_data @@ -240,10 +213,10 @@ def _get_instance_id_from_lb(self, provider_id, instance_address): network_id=md_networks, fields=['device_id', 'tenant_id'])['ports'][0] except Exception as e: - LOG.error(_LE('Failed to get instance id for metadata ' - 'request, provider %(provider)s ' - 'networks %(networks)s ' - 'requester %(requester)s. Error: %(error)s'), + LOG.error('Failed to get instance id for metadata ' + 'request, provider %(provider)s ' + 'networks %(networks)s ' + 'requester %(requester)s. Error: %(error)s', {'provider': provider_id, 'networks': md_networks, 'requester': instance_address, @@ -287,16 +260,17 @@ def _handle_instance_id_request_from_lb(self, req): def _validate_shared_secret(self, requestor_id, signature, requestor_address): expected_signature = hmac.new( - CONF.neutron.metadata_proxy_shared_secret, - requestor_id, hashlib.sha256).hexdigest() + encodeutils.to_utf8(CONF.neutron.metadata_proxy_shared_secret), + encodeutils.to_utf8(requestor_id), + hashlib.sha256).hexdigest() if not secutils.constant_time_compare(expected_signature, signature): if requestor_id: - LOG.warning(_LW('X-Instance-ID-Signature: %(signature)s does ' - 'not match the expected value: ' - '%(expected_signature)s for id: ' - '%(requestor_id)s. Request From: ' - '%(requestor_address)s'), + LOG.warning('X-Instance-ID-Signature: %(signature)s does ' + 'not match the expected value: ' + '%(expected_signature)s for id: ' + '%(requestor_id)s. Request From: ' + '%(requestor_address)s', {'signature': signature, 'expected_signature': expected_signature, 'requestor_id': requestor_id, @@ -310,7 +284,7 @@ def _get_meta_by_instance_id(self, instance_id, tenant_id, remote_address): meta_data = self.get_metadata_by_instance_id(instance_id, remote_address) except Exception: - LOG.exception(_LE('Failed to get metadata for instance id: %s'), + LOG.exception('Failed to get metadata for instance id: %s', instance_id) msg = _('An unknown error has occurred. ' 'Please try your request again.') @@ -318,11 +292,11 @@ def _get_meta_by_instance_id(self, instance_id, tenant_id, remote_address): explanation=six.text_type(msg)) if meta_data is None: - LOG.error(_LE('Failed to get metadata for instance id: %s'), + LOG.error('Failed to get metadata for instance id: %s', instance_id) elif meta_data.instance.project_id != tenant_id: - LOG.warning(_LW("Tenant_id %(tenant_id)s does not match tenant_id " - "of instance %(instance_id)s."), + LOG.warning("Tenant_id %(tenant_id)s does not match tenant_id " + "of instance %(instance_id)s.", {'tenant_id': tenant_id, 'instance_id': instance_id}) # causes a 404 to be raised meta_data = None diff --git a/nova/api/metadata/password.py b/nova/api/metadata/password.py index 01457ac7214..96531e43a7c 100644 --- a/nova/api/metadata/password.py +++ b/nova/api/metadata/password.py @@ -13,10 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. +import six from six.moves import range from webob import exc from nova import context +from nova import exception from nova.i18n import _ from nova import objects from nova import utils @@ -42,6 +44,9 @@ def convert_password(context, password): Password is stored with the keys 'password_0' -> 'password_3'. """ password = password or '' + if six.PY3 and isinstance(password, bytes): + password = password.decode('utf-8') + meta = {} for i in range(CHUNKS): meta['password_%d' % i] = password[:CHUNK_LENGTH] @@ -63,7 +68,12 @@ def handle_password(req, meta_data): msg = _("Request is too large.") raise exc.HTTPBadRequest(explanation=msg) - instance = objects.Instance.get_by_uuid(ctxt, meta_data.uuid) + im = objects.InstanceMapping.get_by_instance_uuid(ctxt, meta_data.uuid) + with context.target_cell(ctxt, im.cell_mapping) as cctxt: + try: + instance = objects.Instance.get_by_uuid(cctxt, meta_data.uuid) + except exception.InstanceNotFound as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) instance.system_metadata.update(convert_password(ctxt, req.body)) instance.save() else: diff --git a/nova/api/metadata/vendordata.py b/nova/api/metadata/vendordata.py new file mode 100644 index 00000000000..e58b07c9c8c --- /dev/null +++ b/nova/api/metadata/vendordata.py @@ -0,0 +1,30 @@ +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + + +class VendorDataDriver(object): + """The base VendorData Drivers should inherit from.""" + + def __init__(self, *args, **kwargs): + """Init method should do all expensive operations.""" + self._data = {} + + def get(self): + """Return a dictionary of primitives to be rendered in metadata + + :return: A dictionary of primitives. + """ + return self._data diff --git a/nova/api/metadata/vendordata_dynamic.py b/nova/api/metadata/vendordata_dynamic.py new file mode 100644 index 00000000000..2db9a478e6a --- /dev/null +++ b/nova/api/metadata/vendordata_dynamic.py @@ -0,0 +1,148 @@ +# Copyright 2016 Rackspace Australia +# 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. + +"""Render vendordata as stored fetched from REST microservices.""" + +import sys + +from keystoneauth1 import exceptions as ks_exceptions +from keystoneauth1 import loading as ks_loading +from oslo_log import log as logging +from oslo_serialization import jsonutils +import six + +from nova.api.metadata import vendordata +import nova.conf + +CONF = nova.conf.CONF +LOG = logging.getLogger(__name__) + +_SESSION = None +_ADMIN_AUTH = None + + +def _load_ks_session(conf): + """Load session. + + This is either an authenticated session or a requests session, depending on + what's configured. + """ + global _ADMIN_AUTH + global _SESSION + + if not _ADMIN_AUTH: + _ADMIN_AUTH = ks_loading.load_auth_from_conf_options( + conf, nova.conf.vendordata.vendordata_group.name) + + if not _ADMIN_AUTH: + LOG.warning('Passing insecure dynamic vendordata requests ' + 'because of missing or incorrect service account ' + 'configuration.') + + if not _SESSION: + _SESSION = ks_loading.load_session_from_conf_options( + conf, nova.conf.vendordata.vendordata_group.name, + auth=_ADMIN_AUTH) + + return _SESSION + + +class DynamicVendorData(vendordata.VendorDataDriver): + def __init__(self, context=None, instance=None, address=None, + network_info=None): + # NOTE(mikal): address and network_info are unused, but can't be + # removed / renamed as this interface is shared with the static + # JSON plugin. + self.context = context + self.instance = instance + # We only create the session if we make a request. + self.session = None + + def _do_request(self, service_name, url): + if self.session is None: + self.session = _load_ks_session(CONF) + try: + body = {'project-id': self.instance.project_id, + 'instance-id': self.instance.uuid, + 'image-id': self.instance.image_ref, + 'user-data': self.instance.user_data, + 'hostname': self.instance.hostname, + 'metadata': self.instance.metadata, + 'boot-roles': self.instance.system_metadata.get( + 'boot_roles', '')} + headers = {'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'openstack-nova-vendordata'} + + # SSL verification + verify = url.startswith('https://') + + if verify and CONF.api.vendordata_dynamic_ssl_certfile: + verify = CONF.api.vendordata_dynamic_ssl_certfile + + timeout = (CONF.api.vendordata_dynamic_connect_timeout, + CONF.api.vendordata_dynamic_read_timeout) + + res = self.session.request(url, 'POST', data=jsonutils.dumps(body), + verify=verify, headers=headers, + timeout=timeout) + if res and res.text: + # TODO(mikal): Use the Cache-Control response header to do some + # sensible form of caching here. + return jsonutils.loads(res.text) + + return {} + + except (TypeError, ValueError, + ks_exceptions.connection.ConnectionError, + ks_exceptions.http.HttpError) as e: + LOG.warning('Error from dynamic vendordata service ' + '%(service_name)s at %(url)s: %(error)s', + {'service_name': service_name, + 'url': url, + 'error': e}, + instance=self.instance) + if CONF.api.vendordata_dynamic_failure_fatal: + six.reraise(type(e), e, sys.exc_info()[2]) + + return {} + + def get(self): + j = {} + + for target in CONF.api.vendordata_dynamic_targets: + # NOTE(mikal): a target is composed of the following: + # name@url + # where name is the name to use in the metadata handed to + # instances, and url is the URL to fetch it from + if target.find('@') == -1: + LOG.warning('Vendordata target %(target)s lacks a name. ' + 'Skipping', + {'target': target}, instance=self.instance) + continue + + tokens = target.split('@') + name = tokens[0] + url = '@'.join(tokens[1:]) + + if name in j: + LOG.warning('Vendordata already contains an entry named ' + '%(target)s. Skipping', + {'target': target}, instance=self.instance) + continue + + j[name] = self._do_request(name, url) + + return j diff --git a/nova/api/metadata/vendordata_json.py b/nova/api/metadata/vendordata_json.py index 92976685b08..4339c69962b 100644 --- a/nova/api/metadata/vendordata_json.py +++ b/nova/api/metadata/vendordata_json.py @@ -17,41 +17,36 @@ import errno -from oslo_config import cfg from oslo_log import log as logging from oslo_serialization import jsonutils -from nova.api.metadata import base -from nova.i18n import _LW +from nova.api.metadata import vendordata +import nova.conf -file_opt = cfg.StrOpt('vendordata_jsonfile_path', - help='File to load JSON formatted vendor data from') - -CONF = cfg.CONF -CONF.register_opt(file_opt) +CONF = nova.conf.CONF LOG = logging.getLogger(__name__) -class JsonFileVendorData(base.VendorDataDriver): +class JsonFileVendorData(vendordata.VendorDataDriver): def __init__(self, *args, **kwargs): super(JsonFileVendorData, self).__init__(*args, **kwargs) data = {} - fpath = CONF.vendordata_jsonfile_path - logprefix = "%s[%s]:" % (file_opt.name, fpath) + fpath = CONF.api.vendordata_jsonfile_path + logprefix = "vendordata_jsonfile_path[%s]:" % fpath if fpath: try: - with open(fpath, "r") as fp: + with open(fpath, "rb") as fp: data = jsonutils.load(fp) except IOError as e: if e.errno == errno.ENOENT: - LOG.warning(_LW("%(logprefix)s file does not exist"), + LOG.warning("%(logprefix)s file does not exist", {'logprefix': logprefix}) else: - LOG.warning(_LW("%(logprefix)s unexpected IOError when " - "reading"), {'logprefix': logprefix}) - raise e + LOG.warning("%(logprefix)s unexpected IOError when " + "reading", {'logprefix': logprefix}) + raise except ValueError: - LOG.warning(_LW("%(logprefix)s failed to load json"), + LOG.warning("%(logprefix)s failed to load json", {'logprefix': logprefix}) raise diff --git a/nova/api/metadata/wsgi.py b/nova/api/metadata/wsgi.py new file mode 100644 index 00000000000..d2f33c2d6fe --- /dev/null +++ b/nova/api/metadata/wsgi.py @@ -0,0 +1,20 @@ +# 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. +"""WSGI application entry-point for Nova Metadata API, installed by pbr.""" + +from nova.api.openstack import wsgi_app + +NAME = "metadata" + + +def init_application(): + return wsgi_app.init_application(NAME) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 5ed7b48a1c1..823609a01b1 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -18,76 +18,21 @@ WSGI middleware for OpenStack API controllers. """ -from oslo_config import cfg from oslo_log import log as logging import routes -import six -import stevedore import webob.dec import webob.exc -from nova.api.openstack import extensions from nova.api.openstack import wsgi -from nova import exception -from nova.i18n import _ -from nova.i18n import _LC -from nova.i18n import _LE -from nova.i18n import _LI -from nova.i18n import _LW +import nova.conf from nova.i18n import translate from nova import notifications from nova import utils from nova import wsgi as base_wsgi -api_opts = [ - cfg.BoolOpt('enabled', - default=True, - help='DEPRECATED: Whether the V2.1 API is enabled or not. ' - 'This option will be removed in the near future.', - deprecated_for_removal=True, deprecated_group='osapi_v21'), - cfg.ListOpt('extensions_blacklist', - default=[], - help='DEPRECATED: A list of v2.1 API extensions to never ' - 'load. Specify the extension aliases here. ' - 'This option will be removed in the near future. ' - 'After that point you have to run all of the API.', - deprecated_for_removal=True, deprecated_group='osapi_v21'), - cfg.ListOpt('extensions_whitelist', - default=[], - help='DEPRECATED: If the list is not empty then a v2.1 ' - 'API extension will only be loaded if it exists in this ' - 'list. Specify the extension aliases here. ' - 'This option will be removed in the near future. ' - 'After that point you have to run all of the API.', - deprecated_for_removal=True, deprecated_group='osapi_v21'), - cfg.StrOpt('project_id_regex', - help='DEPRECATED: The validation regex for project_ids ' - 'used in urls. This defaults to [0-9a-f\-]+ if not set, ' - 'which matches normal uuids created by keystone.', - deprecated_for_removal=True, deprecated_group='osapi_v21') -] -api_opts_group = cfg.OptGroup(name='osapi_v21', title='API v2.1 Options') - LOG = logging.getLogger(__name__) -CONF = cfg.CONF -CONF.register_group(api_opts_group) -CONF.register_opts(api_opts, api_opts_group) - -# List of v21 API extensions which are considered to form -# the core API and so must be present -# TODO(cyeoh): Expand this list as the core APIs are ported to v21 -API_V21_CORE_EXTENSIONS = set(['os-consoles', - 'extensions', - 'os-flavor-extra-specs', - 'os-flavor-manage', - 'flavors', - 'ips', - 'os-keypairs', - 'os-flavor-access', - 'server-metadata', - 'servers', - 'versions']) +CONF = nova.conf.CONF class FaultWrapper(base_wsgi.Middleware): @@ -104,7 +49,7 @@ def status_to_type(status): status, webob.exc.HTTPInternalServerError)() def _error(self, inner, req): - LOG.exception(_LE("Caught error: %s"), six.text_type(inner)) + LOG.exception("Caught error: %s", inner) safe = getattr(inner, 'safe', False) headers = getattr(inner, 'headers', None) @@ -113,7 +58,7 @@ def _error(self, inner, req): status = 500 msg_dict = dict(url=req.url, status=status) - LOG.info(_LI("%(url)s returned with HTTP %(status)d"), msg_dict) + LOG.info("%(url)s returned with HTTP %(status)d", msg_dict) outer = self.status_to_type(status) if headers: outer.headers = headers @@ -145,11 +90,14 @@ class LegacyV2CompatibleWrapper(base_wsgi.Middleware): def _filter_request_headers(self, req): """For keeping same behavior with v2 API, ignores microversions - HTTP header X-OpenStack-Nova-API-Version in the request. + HTTP headers X-OpenStack-Nova-API-Version and OpenStack-API-Version + in the request. """ if wsgi.API_VERSION_REQUEST_HEADER in req.headers: del req.headers[wsgi.API_VERSION_REQUEST_HEADER] + if wsgi.LEGACY_API_VERSION_REQUEST_HEADER in req.headers: + del req.headers[wsgi.LEGACY_API_VERSION_REQUEST_HEADER] return req def _filter_response_headers(self, response): @@ -159,13 +107,16 @@ def _filter_response_headers(self, response): if wsgi.API_VERSION_REQUEST_HEADER in response.headers: del response.headers[wsgi.API_VERSION_REQUEST_HEADER] + if wsgi.LEGACY_API_VERSION_REQUEST_HEADER in response.headers: + del response.headers[wsgi.LEGACY_API_VERSION_REQUEST_HEADER] if 'Vary' in response.headers: vary_headers = response.headers['Vary'].split(',') filtered_vary = [] for vary in vary_headers: vary = vary.strip() - if vary == wsgi.API_VERSION_REQUEST_HEADER: + if (vary == wsgi.API_VERSION_REQUEST_HEADER or + vary == wsgi.LEGACY_API_VERSION_REQUEST_HEADER): continue filtered_vary.append(vary) if filtered_vary: @@ -200,7 +151,7 @@ def connect(self, *args, **kargs): class ProjectMapper(APIMapper): - def resource(self, member_name, collection_name, **kwargs): + def _get_project_id_token(self): # NOTE(sdague): project_id parameter is only valid if its hex # or hex + dashes (note, integers are a subset of this). This # is required to hand our overlaping routes issues. @@ -208,7 +159,10 @@ def resource(self, member_name, collection_name, **kwargs): if CONF.osapi_v21.project_id_regex: project_id_regex = CONF.osapi_v21.project_id_regex - project_id_token = '{project_id:%s}' % project_id_regex + return '{project_id:%s}' % project_id_regex + + def resource(self, member_name, collection_name, **kwargs): + project_id_token = self._get_project_id_token() if 'parent_resource' not in kwargs: kwargs['path_prefix'] = '%s/' % project_id_token else: @@ -239,6 +193,20 @@ def resource(self, member_name, collection_name, **kwargs): collection_name, **kwargs) + def create_route(self, path, method, controller, action): + project_id_token = self._get_project_id_token() + + # while we transition away from project IDs in the API URIs, create + # additional routes that include the project_id + self.connect('/%s%s' % (project_id_token, path), + conditions=dict(method=[method]), + controller=controller, + action=action) + self.connect(path, + conditions=dict(method=[method]), + controller=controller, + action=action) + class PlainMapper(APIMapper): def resource(self, member_name, collection_name, **kwargs): @@ -250,280 +218,3 @@ def resource(self, member_name, collection_name, **kwargs): routes.Mapper.resource(self, member_name, collection_name, **kwargs) - - -class APIRouter(base_wsgi.Router): - """Routes requests on the OpenStack API to the appropriate controller - and method. - """ - ExtensionManager = None # override in subclasses - - @classmethod - def factory(cls, global_config, **local_config): - """Simple paste factory, :class:`nova.wsgi.Router` doesn't have one.""" - return cls() - - def __init__(self, ext_mgr=None, init_only=None): - if ext_mgr is None: - if self.ExtensionManager: - ext_mgr = self.ExtensionManager() - else: - raise Exception(_("Must specify an ExtensionManager class")) - - mapper = ProjectMapper() - self.resources = {} - self._setup_routes(mapper, ext_mgr, init_only) - self._setup_ext_routes(mapper, ext_mgr, init_only) - self._setup_extensions(ext_mgr) - super(APIRouter, self).__init__(mapper) - - def _setup_ext_routes(self, mapper, ext_mgr, init_only): - for resource in ext_mgr.get_resources(): - LOG.debug('Extending resource: %s', - resource.collection) - - if init_only is not None and resource.collection not in init_only: - continue - - inherits = None - if resource.inherits: - inherits = self.resources.get(resource.inherits) - if not resource.controller: - resource.controller = inherits.controller - wsgi_resource = wsgi.Resource(resource.controller, - inherits=inherits) - self.resources[resource.collection] = wsgi_resource - kargs = dict( - controller=wsgi_resource, - collection=resource.collection_actions, - member=resource.member_actions) - - if resource.parent: - kargs['parent_resource'] = resource.parent - - mapper.resource(resource.collection, resource.collection, **kargs) - - if resource.custom_routes_fn: - resource.custom_routes_fn(mapper, wsgi_resource) - - def _setup_extensions(self, ext_mgr): - for extension in ext_mgr.get_controller_extensions(): - collection = extension.collection - controller = extension.controller - - msg_format_dict = {'collection': collection, - 'ext_name': extension.extension.name} - if collection not in self.resources: - LOG.warning(_LW('Extension %(ext_name)s: Cannot extend ' - 'resource %(collection)s: No such resource'), - msg_format_dict) - continue - - LOG.debug('Extension %(ext_name)s extended resource: ' - '%(collection)s', - msg_format_dict) - - resource = self.resources[collection] - resource.register_actions(controller) - resource.register_extensions(controller) - - def _setup_routes(self, mapper, ext_mgr, init_only): - raise NotImplementedError() - - -class APIRouterV21(base_wsgi.Router): - """Routes requests on the OpenStack v2.1 API to the appropriate controller - and method. - """ - - @classmethod - def factory(cls, global_config, **local_config): - """Simple paste factory, :class:`nova.wsgi.Router` doesn't have one.""" - return cls() - - @staticmethod - def api_extension_namespace(): - return 'nova.api.v21.extensions' - - def __init__(self, init_only=None, v3mode=False): - # TODO(cyeoh): bp v3-api-extension-framework. Currently load - # all extensions but eventually should be able to exclude - # based on a config file - # TODO(oomichi): We can remove v3mode argument after moving all v3 APIs - # to v2.1. - def _check_load_extension(ext): - if (self.init_only is None or ext.obj.alias in - self.init_only) and isinstance(ext.obj, - extensions.V21APIExtensionBase): - - # Check whitelist is either empty or if not then the extension - # is in the whitelist - if (not CONF.osapi_v21.extensions_whitelist or - ext.obj.alias in CONF.osapi_v21.extensions_whitelist): - - # Check the extension is not in the blacklist - blacklist = CONF.osapi_v21.extensions_blacklist - if ext.obj.alias not in blacklist: - return self._register_extension(ext) - return False - - if not CONF.osapi_v21.enabled: - LOG.info(_LI("V2.1 API has been disabled by configuration")) - LOG.warning(_LW("In the M release you must run the v2.1 API.")) - return - - if (CONF.osapi_v21.extensions_blacklist or - CONF.osapi_v21.extensions_whitelist): - LOG.warning( - _LW('In the M release you must run all of the API. ' - 'The concept of API extensions will be removed from ' - 'the codebase to ensure there is a single Compute API.')) - - self.init_only = init_only - LOG.debug("v21 API Extension Blacklist: %s", - CONF.osapi_v21.extensions_blacklist) - LOG.debug("v21 API Extension Whitelist: %s", - CONF.osapi_v21.extensions_whitelist) - - in_blacklist_and_whitelist = set( - CONF.osapi_v21.extensions_whitelist).intersection( - CONF.osapi_v21.extensions_blacklist) - if len(in_blacklist_and_whitelist) != 0: - LOG.warning(_LW("Extensions in both blacklist and whitelist: %s"), - list(in_blacklist_and_whitelist)) - - self.api_extension_manager = stevedore.enabled.EnabledExtensionManager( - namespace=self.api_extension_namespace(), - check_func=_check_load_extension, - invoke_on_load=True, - invoke_kwds={"extension_info": self.loaded_extension_info}) - - if v3mode: - mapper = PlainMapper() - else: - mapper = ProjectMapper() - - self.resources = {} - - # NOTE(cyeoh) Core API support is rewritten as extensions - # but conceptually still have core - if list(self.api_extension_manager): - # NOTE(cyeoh): Stevedore raises an exception if there are - # no plugins detected. I wonder if this is a bug. - self._register_resources_check_inherits(mapper) - self.api_extension_manager.map(self._register_controllers) - - missing_core_extensions = self.get_missing_core_extensions( - self.loaded_extension_info.get_extensions().keys()) - if not self.init_only and missing_core_extensions: - LOG.critical(_LC("Missing core API extensions: %s"), - missing_core_extensions) - raise exception.CoreAPIMissing( - missing_apis=missing_core_extensions) - - LOG.info(_LI("Loaded extensions: %s"), - sorted(self.loaded_extension_info.get_extensions().keys())) - super(APIRouterV21, self).__init__(mapper) - - def _register_resources_list(self, ext_list, mapper): - for ext in ext_list: - self._register_resources(ext, mapper) - - def _register_resources_check_inherits(self, mapper): - ext_has_inherits = [] - ext_no_inherits = [] - - for ext in self.api_extension_manager: - for resource in ext.obj.get_resources(): - if resource.inherits: - ext_has_inherits.append(ext) - break - else: - ext_no_inherits.append(ext) - - self._register_resources_list(ext_no_inherits, mapper) - self._register_resources_list(ext_has_inherits, mapper) - - @staticmethod - def get_missing_core_extensions(extensions_loaded): - extensions_loaded = set(extensions_loaded) - missing_extensions = API_V21_CORE_EXTENSIONS - extensions_loaded - return list(missing_extensions) - - @property - def loaded_extension_info(self): - raise NotImplementedError() - - def _register_extension(self, ext): - raise NotImplementedError() - - def _register_resources(self, ext, mapper): - """Register resources defined by the extensions - - Extensions define what resources they want to add through a - get_resources function - """ - - handler = ext.obj - LOG.debug("Running _register_resources on %s", ext.obj) - - for resource in handler.get_resources(): - LOG.debug('Extended resource: %s', resource.collection) - - inherits = None - if resource.inherits: - inherits = self.resources.get(resource.inherits) - if not resource.controller: - resource.controller = inherits.controller - wsgi_resource = wsgi.ResourceV21(resource.controller, - inherits=inherits) - self.resources[resource.collection] = wsgi_resource - kargs = dict( - controller=wsgi_resource, - collection=resource.collection_actions, - member=resource.member_actions) - - if resource.parent: - kargs['parent_resource'] = resource.parent - - # non core-API plugins use the collection name as the - # member name, but the core-API plugins use the - # singular/plural convention for member/collection names - if resource.member_name: - member_name = resource.member_name - else: - member_name = resource.collection - mapper.resource(member_name, resource.collection, - **kargs) - - if resource.custom_routes_fn: - resource.custom_routes_fn(mapper, wsgi_resource) - - def _register_controllers(self, ext): - """Register controllers defined by the extensions - - Extensions define what resources they want to add through - a get_controller_extensions function - """ - - handler = ext.obj - LOG.debug("Running _register_controllers on %s", ext.obj) - - for extension in handler.get_controller_extensions(): - ext_name = extension.extension.name - collection = extension.collection - controller = extension.controller - - if collection not in self.resources: - LOG.warning(_LW('Extension %(ext_name)s: Cannot extend ' - 'resource %(collection)s: No such resource'), - {'ext_name': ext_name, 'collection': collection}) - continue - - LOG.debug('Extension %(ext_name)s extending resource: ' - '%(collection)s', - {'ext_name': ext_name, 'collection': collection}) - - resource = self.resources[collection] - resource.register_actions(controller) - resource.register_extensions(controller) diff --git a/nova/api/openstack/api_version_request.py b/nova/api/openstack/api_version_request.py index 1e0d59f544d..0be8c905b01 100644 --- a/nova/api/openstack/api_version_request.py +++ b/nova/api/openstack/api_version_request.py @@ -71,18 +71,83 @@ * 2.24 - Add API to cancel a running live migration * 2.25 - Make block_migration support 'auto' and remove disk_over_commit for os-migrateLive. - + * 2.26 - Adds support of server tags + * 2.27 - Adds support for new-style microversion headers while + keeping support for the original style. + * 2.28 - Changes compute_node.cpu_info from string to object + * 2.29 - Add a force flag in evacuate request body and change the + behaviour for the host flag by calling the scheduler. + * 2.30 - Add a force flag in live-migrate request body and change the + behaviour for the host flag by calling the scheduler. + * 2.31 - Fix os-console-auth-tokens to work for all console types. + * 2.32 - Add tag to networks and block_device_mapping_v2 in server boot + request body. + * 2.33 - Add pagination support for hypervisors. + * 2.34 - Checks before live-migration are made in asynchronous way. + os-Migratelive Action does not throw badRequest in case of + pre-checks failure. Verification result is available over + instance-actions. + * 2.35 - Adds keypairs pagination support. + * 2.36 - Deprecates all the API which proxy to another service and fping + API. + * 2.37 - Adds support for auto-allocating networking, otherwise known as + "Get me a Network". Also enforces server.networks.uuid to be in + UUID format. + * 2.38 - Add a condition to return HTTPBadRequest if invalid status is + provided for listing servers. + * 2.39 - Deprecates image-metadata proxy API + * 2.40 - Adds simple tenant usage pagination support. + * 2.41 - Return uuid attribute for aggregates. + * 2.42 - In the context of device tagging at instance boot time, + re-introduce the tag attribute that, due to bugs, was lost + starting with version 2.33 for block devices and starting with + version 2.37 for network interfaces. + * 2.43 - Deprecate os-hosts API + * 2.44 - The servers action addFixedIp, removeFixedIp, addFloatingIp, + removeFloatingIp and os-virtual-interfaces APIs are deprecated. + * 2.45 - The createImage and createBackup APIs no longer return a Location + header in the response for the snapshot image, they now return a + json dict in the response body with an image_id key and uuid + value. + * 2.46 - Return ``X-OpenStack-Request-ID`` header on requests. + * 2.47 - When displaying server details, display the flavor as a dict + rather than a link. If the user is prevented from retrieving + the flavor extra-specs by policy, simply omit the field from + the output. + * 2.48 - Standardize VM diagnostics info. + * 2.49 - Support tagged attachment of network interfaces and block devices. + * 2.50 - Exposes ``server_groups`` and ``server_group_members`` keys in + GET & PUT ``os-quota-class-sets`` APIs response. + Also filter out Network related quotas from + ``os-quota-class-sets`` API + * 2.51 - Adds new event name to external-events (volume-extended). Also, + non-admins can see instance action event details except for the + traceback field. + * 2.52 - Adds support for applying tags when creating a server. + * 2.53 - Service and compute node (hypervisor) database ids are hidden. + The os-services and os-hypervisors APIs now return a uuid in the + id field, and takes a uuid in requests. PUT and GET requests + and responses are also changed. """ # The minimum and maximum versions of the API supported # The default api version request is defined to be the -# the minimum version of the API supported. +# minimum version of the API supported. # Note(cyeoh): This only applies for the v2.1 API once microversions # support is fully merged. It does not affect the V2 API. _MIN_API_VERSION = "2.1" -_MAX_API_VERSION = "2.25" +_MAX_API_VERSION = "2.53" DEFAULT_API_VERSION = _MIN_API_VERSION +# Almost all proxy APIs which related to network, images and baremetal +# were deprecated from 2.36. +MAX_PROXY_API_SUPPORT_VERSION = '2.35' +MIN_WITHOUT_PROXY_API_SUPPORT_VERSION = '2.36' + +# Starting from microversion 2.39 also image-metadata proxy API is deprecated. +MAX_IMAGE_META_PROXY_API_VERSION = '2.38' +MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION = '2.39' + # NOTE(cyeoh): min and max versions declared as functions so we can # mock them for unittests. Do not use the constants directly anywhere @@ -105,7 +170,7 @@ def is_supported(req, min_version=_MIN_API_VERSION, :param max_version: maximum version of API needed for correct request processing - :returns True if request satisfies minimal and maximum API version + :returns: True if request satisfies minimal and maximum API version requirements. False in other case. """ diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index bbaf74d20de..a3bcd0cad15 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -14,16 +14,15 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg import webob.dec import webob.exc from nova.api.openstack import wsgi +import nova.conf from nova import context from nova import wsgi as base_wsgi -CONF = cfg.CONF -CONF.import_opt('use_forwarded_for', 'nova.api.auth') +CONF = nova.conf.CONF class NoAuthMiddlewareBase(base_wsgi.Middleware): @@ -51,7 +50,7 @@ def base_call(self, req, project_id_in_path, always_admin=True): user_id, _sep, project_id = token.partition(':') project_id = project_id or user_id remote_address = getattr(req, 'remote_address', '127.0.0.1') - if CONF.use_forwarded_for: + if CONF.api.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) is_admin = always_admin or (user_id == 'admin') ctx = context.RequestContext(user_id, diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 3caa9e1496b..9274fe25946 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -18,7 +18,6 @@ import itertools import re -from oslo_config import cfg from oslo_log import log as logging from oslo_utils import strutils import six @@ -27,31 +26,15 @@ from webob import exc from nova.compute import task_states -from nova.compute import utils as compute_utils from nova.compute import vm_states import nova.conf from nova import exception from nova.i18n import _ -from nova.i18n import _LE -from nova.i18n import _LW from nova import objects from nova import quota from nova import utils -osapi_opts = [ - cfg.IntOpt('osapi_max_limit', - default=1000, - help='The maximum number of items returned in a single ' - 'response from a collection resource'), - cfg.StrOpt('osapi_compute_link_prefix', - help='Base URL that will be presented to users in links ' - 'to the OpenStack Compute API'), - cfg.StrOpt('osapi_glance_link_prefix', - help='Base URL that will be presented to users in links ' - 'to glance resources'), -] CONF = nova.conf.CONF -CONF.register_opts(osapi_opts) LOG = logging.getLogger(__name__) QUOTAS = quota.QUOTAS @@ -132,9 +115,9 @@ def status_from_state(vm_state, task_state='default'): task_map = _STATE_MAP.get(vm_state, dict(default='UNKNOWN')) status = task_map.get(task_state, task_map['default']) if status == "UNKNOWN": - LOG.error(_LE("status is UNKNOWN from vm_state=%(vm_state)s " - "task_state=%(task_state)s. Bad upgrade or db " - "corrupted?"), + LOG.error("status is UNKNOWN from vm_state=%(vm_state)s " + "task_state=%(task_state)s. Bad upgrade or db " + "corrupted?", {'vm_state': vm_state, 'task_state': task_state}) return status @@ -146,8 +129,8 @@ def task_and_vm_state_from_status(statuses): vm_states = set() task_states = set() lower_statuses = [status.lower() for status in statuses] - for state, task_map in six.iteritems(_STATE_MAP): - for task_state, mapped_state in six.iteritems(task_map): + for state, task_map in _STATE_MAP.items(): + for task_state, mapped_state in task_map.items(): status_string = mapped_state if status_string.lower() in lower_statuses: vm_states.add(state) @@ -208,6 +191,8 @@ def get_pagination_params(request): params['page_size'] = _get_int_param(request, 'page_size') if 'marker' in request.GET: params['marker'] = _get_marker_param(request) + if 'offset' in request.GET: + params['offset'] = _get_int_param(request, 'offset') return params @@ -226,7 +211,7 @@ def _get_marker_param(request): return request.GET['marker'] -def limited(items, request, max_limit=CONF.osapi_max_limit): +def limited(items, request): """Return a slice of items according to requested offset and limit. :param items: A sliceable entity @@ -236,28 +221,21 @@ def limited(items, request, max_limit=CONF.osapi_max_limit): 'limit' is not specified, 0, or > max_limit, we default to max_limit. Negative values for either offset or limit will cause exc.HTTPBadRequest() exceptions to be raised. - :kwarg max_limit: The maximum number of items to return from 'items' """ - offset = request.GET.get("offset", 0) - limit = request.GET.get('limit', max_limit) - - try: - offset = utils.validate_integer(offset, "offset", min_value=0) - limit = utils.validate_integer(limit, "limit", min_value=0) - except exception.InvalidInput as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) + params = get_pagination_params(request) + offset = params.get('offset', 0) + limit = CONF.api.max_limit + limit = min(limit, params.get('limit') or limit) - limit = min(max_limit, limit or max_limit) - range_end = offset + limit - return items[offset:range_end] + return items[offset:(offset + limit)] -def get_limit_and_marker(request, max_limit=CONF.osapi_max_limit): - """get limited parameter from request.""" +def get_limit_and_marker(request): + """Get limited parameter from request.""" params = get_pagination_params(request) - limit = params.get('limit', max_limit) - limit = min(max_limit, limit) - marker = params.get('marker') + limit = CONF.api.max_limit + limit = min(limit, params.get('limit', limit)) + marker = params.get('marker', None) return limit, marker @@ -309,19 +287,6 @@ def check_img_metadata_properties_quota(context, metadata): expl = _("Image metadata limit exceeded") raise webob.exc.HTTPForbidden(explanation=expl) - # check the key length. - if isinstance(metadata, dict): - for key, value in six.iteritems(metadata): - if len(key) == 0: - expl = _("Image metadata key cannot be blank") - raise webob.exc.HTTPBadRequest(explanation=expl) - if len(key) > 255: - expl = _("Image metadata key too long") - raise webob.exc.HTTPBadRequest(explanation=expl) - else: - expl = _("Invalid image metadata") - raise webob.exc.HTTPBadRequest(explanation=expl) - def get_networks_for_instance_from_nw_info(nw_info): networks = collections.OrderedDict() @@ -357,7 +322,7 @@ def get_networks_for_instance(context, instance): 'mac_address': 'aa:aa:aa:aa:aa:aa'}]}, ...} """ - nw_info = compute_utils.get_nw_info_for_instance(instance) + nw_info = instance.get_network_info() return get_networks_for_instance_from_nw_info(nw_info) @@ -383,9 +348,9 @@ def raise_http_conflict_for_instance_invalid_state(exc, action, server_id): def check_snapshots_enabled(f): @functools.wraps(f) def inner(*args, **kwargs): - if not CONF.allow_instance_snapshots: - LOG.warning(_LW('Rejecting snapshot request, snapshots currently' - ' disabled')) + if not CONF.api.allow_instance_snapshots: + LOG.warning('Rejecting snapshot request, snapshots currently' + ' disabled') msg = _("Instance snapshots are not permitted at this time.") raise webob.exc.HTTPBadRequest(explanation=msg) return f(*args, **kwargs) @@ -415,7 +380,7 @@ def _get_project_id(self, request): otherwise """ project_id = request.environ["nova.context"].project_id - if project_id in request.url: + if project_id and project_id in request.url: return project_id return '' @@ -433,7 +398,7 @@ def _get_links(self, request, identifier, collection_name): def _get_next_link(self, request, identifier, collection_name): """Return href string with proper limit and marker params.""" - params = request.params.copy() + params = collections.OrderedDict(sorted(request.params.items())) params["marker"] = identifier prefix = self._update_compute_link_prefix(request.application_url) url = url_join(prefix, @@ -465,15 +430,15 @@ def _get_collection_links(self, id_key="uuid"): """Retrieve 'next' link, if applicable. This is included if: 1) 'limit' param is specified and equals the number of items. - 2) 'limit' param is specified but it exceeds CONF.osapi_max_limit, - in this case the number of items is CONF.osapi_max_limit. + 2) 'limit' param is specified but it exceeds CONF.api.max_limit, + in this case the number of items is CONF.api.max_limit. 3) 'limit' param is NOT specified but the number of items is - CONF.osapi_max_limit. + CONF.api.max_limit. """ links = [] max_items = min( - int(request.params.get("limit", CONF.osapi_max_limit)), - CONF.osapi_max_limit) + int(request.params.get("limit", CONF.api.max_limit)), + CONF.api.max_limit) if max_items and max_items == len(items): last_item = items[-1] if id_key in last_item: @@ -500,19 +465,16 @@ def _update_link_prefix(self, orig_url, prefix): return urlparse.urlunsplit(url_parts).rstrip('/') def _update_glance_link_prefix(self, orig_url): - return self._update_link_prefix(orig_url, - CONF.osapi_glance_link_prefix) + return self._update_link_prefix(orig_url, CONF.api.glance_link_prefix) def _update_compute_link_prefix(self, orig_url): - return self._update_link_prefix(orig_url, - CONF.osapi_compute_link_prefix) + return self._update_link_prefix(orig_url, CONF.api.compute_link_prefix) def get_instance(compute_api, context, instance_id, expected_attrs=None): """Fetch an instance from the compute API, handling error checking.""" try: return compute_api.get(context, instance_id, - want_objects=True, expected_attrs=expected_attrs) except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) diff --git a/nova/api/openstack/compute/__init__.py b/nova/api/openstack/compute/__init__.py index 13c5909e3e6..b8e155554c9 100644 --- a/nova/api/openstack/compute/__init__.py +++ b/nova/api/openstack/compute/__init__.py @@ -14,150 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. -""" -WSGI middleware for OpenStack Compute API. -""" - -from oslo_config import cfg -from oslo_log import log as logging - -import nova.api.openstack -from nova.api.openstack.compute import extension_info -from nova.api.openstack.compute.legacy_v2 import consoles as v2_consoles -from nova.api.openstack.compute.legacy_v2 import extensions as v2_extensions -from nova.api.openstack.compute.legacy_v2 import flavors as v2_flavors -from nova.api.openstack.compute.legacy_v2 import image_metadata \ - as v2_image_metadata -from nova.api.openstack.compute.legacy_v2 import images as v2_images -from nova.api.openstack.compute.legacy_v2 import ips as v2_ips -from nova.api.openstack.compute.legacy_v2 import limits as v2_limits -from nova.api.openstack.compute.legacy_v2 import server_metadata \ - as v2_server_metadata -from nova.api.openstack.compute.legacy_v2 import servers as v2_servers -from nova.api.openstack.compute.legacy_v2 import versions \ - as legacy_v2_versions -from nova.i18n import _LW - -allow_instance_snapshots_opt = cfg.BoolOpt('allow_instance_snapshots', - default=True, - help='Permit instance snapshot operations.') - -CONF = cfg.CONF -CONF.register_opt(allow_instance_snapshots_opt) - -LOG = logging.getLogger(__name__) - - -class APIRouter(nova.api.openstack.APIRouter): - """Routes requests on the OpenStack API to the appropriate controller - and method. - """ - ExtensionManager = v2_extensions.ExtensionManager - - def __init__(self, ext_mgr=None, init_only=None): - LOG.warning(_LW( - "Deprecated: Starting with the Liberty release, the v2 API was " - "already deprecated and the v2.1 API is set as the default. Nova " - "also supports v2.1 API legacy v2 compatible mode for switching " - "to v2.1 API smoothly. For more information on how to configure " - "v2.1 API and legacy v2 compatible mode, please refer Nova " - "api-paste.ini sample file.")) - super(APIRouter, self).__init__(ext_mgr=ext_mgr, - init_only=init_only) - - def _setup_routes(self, mapper, ext_mgr, init_only): - if init_only is None or 'versions' in init_only: - self.resources['versions'] = legacy_v2_versions.create_resource() - mapper.connect("versions", "/", - controller=self.resources['versions'], - action='show', - conditions={"method": ['GET']}) - - mapper.redirect("", "/") - - if init_only is None or 'consoles' in init_only: - self.resources['consoles'] = v2_consoles.create_resource() - mapper.resource("console", "consoles", - controller=self.resources['consoles'], - parent_resource=dict(member_name='server', - collection_name='servers')) - - if init_only is None or 'consoles' in init_only or \ - 'servers' in init_only or 'ips' in init_only: - self.resources['servers'] = v2_servers.create_resource(ext_mgr) - mapper.resource("server", "servers", - controller=self.resources['servers'], - collection={'detail': 'GET'}, - member={'action': 'POST'}) - - if init_only is None or 'ips' in init_only: - self.resources['ips'] = v2_ips.create_resource() - mapper.resource("ip", "ips", controller=self.resources['ips'], - parent_resource=dict(member_name='server', - collection_name='servers')) - - if init_only is None or 'images' in init_only: - self.resources['images'] = v2_images.create_resource() - mapper.resource("image", "images", - controller=self.resources['images'], - collection={'detail': 'GET'}) - - if init_only is None or 'limits' in init_only: - self.resources['limits'] = v2_limits.create_resource() - mapper.resource("limit", "limits", - controller=self.resources['limits']) - - if init_only is None or 'flavors' in init_only: - self.resources['flavors'] = v2_flavors.create_resource() - mapper.resource("flavor", "flavors", - controller=self.resources['flavors'], - collection={'detail': 'GET'}, - member={'action': 'POST'}) - - if init_only is None or 'image_metadata' in init_only: - v2immeta = v2_image_metadata - self.resources['image_metadata'] = v2immeta.create_resource() - image_metadata_controller = self.resources['image_metadata'] - - mapper.resource("image_meta", "metadata", - controller=image_metadata_controller, - parent_resource=dict(member_name='image', - collection_name='images')) - - mapper.connect("metadata", - "/{project_id}/images/{image_id}/metadata", - controller=image_metadata_controller, - action='update_all', - conditions={"method": ['PUT']}) - - if init_only is None or 'server_metadata' in init_only: - self.resources['server_metadata'] = \ - v2_server_metadata.create_resource() - server_metadata_controller = self.resources['server_metadata'] - - mapper.resource("server_meta", "metadata", - controller=server_metadata_controller, - parent_resource=dict(member_name='server', - collection_name='servers')) - - mapper.connect("metadata", - "/{project_id}/servers/{server_id}/metadata", - controller=server_metadata_controller, - action='update_all', - conditions={"method": ['PUT']}) - - -class APIRouterV21(nova.api.openstack.APIRouterV21): - """Routes requests on the OpenStack API to the appropriate controller - and method. - """ - def __init__(self, init_only=None): - self._loaded_extension_info = extension_info.LoadedExtensionInfo() - super(APIRouterV21, self).__init__(init_only) - - def _register_extension(self, ext): - return self.loaded_extension_info.register_extension(ext.obj) - - @property - def loaded_extension_info(self): - return self._loaded_extension_info +# The APIRouterV21 moves down to the 'nova.api.openstack.compute.routes' for +# circle reference problem. Import the APIRouterV21 is for the api-paste.ini +# works correctly without modification. We still looking for a chance to move +# the APIRouterV21 back to here after cleanups. +from nova.api.openstack.compute.routes import APIRouterV21 # noqa diff --git a/nova/api/openstack/compute/access_ips.py b/nova/api/openstack/compute/access_ips.py deleted file mode 100644 index f55978cd29c..00000000000 --- a/nova/api/openstack/compute/access_ips.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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 nova.api.openstack.compute.schemas import access_ips -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - -ALIAS = "os-access-ips" -authorize = extensions.os_compute_soft_authorizer(ALIAS) - - -class AccessIPsController(wsgi.Controller): - def _extend_server(self, req, server): - db_instance = req.get_db_instance(server['id']) - ip_v4 = db_instance.get('access_ip_v4') - ip_v6 = db_instance.get('access_ip_v6') - server['accessIPv4'] = str(ip_v4) if ip_v4 is not None else '' - server['accessIPv6'] = str(ip_v6) if ip_v6 is not None else '' - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - self._extend_server(req, server) - - @wsgi.extends - def update(self, req, resp_obj, id, body): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - self._extend_server(req, server) - - @wsgi.extends(action='rebuild') - def rebuild(self, req, resp_obj, id, body): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - self._extend_server(req, server) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if authorize(context): - servers = resp_obj.obj['servers'] - for server in servers: - self._extend_server(req, server) - - -class AccessIPs(extensions.V21APIExtensionBase): - """Access IPs support.""" - - name = "AccessIPs" - alias = ALIAS - version = 1 - v4_key = 'accessIPv4' - v6_key = 'accessIPv6' - - def get_controller_extensions(self): - controller = AccessIPsController() - extension = extensions.ControllerExtension(self, 'servers', - controller) - return [extension] - - def get_resources(self): - return [] - - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - # making 'body_deprecated_param' as optional to avoid changes for - # server_update & server_rebuild - def server_create(self, server_dict, create_kwargs, - body_deprecated_param=None): - if AccessIPs.v4_key in server_dict: - access_ip_v4 = server_dict.get(AccessIPs.v4_key) - if access_ip_v4: - create_kwargs['access_ip_v4'] = access_ip_v4 - else: - create_kwargs['access_ip_v4'] = None - if AccessIPs.v6_key in server_dict: - access_ip_v6 = server_dict.get(AccessIPs.v6_key) - if access_ip_v6: - create_kwargs['access_ip_v6'] = access_ip_v6 - else: - create_kwargs['access_ip_v6'] = None - - server_update = server_create - server_rebuild = server_create - - def get_server_create_schema(self, version): - return access_ips.server_create - - get_server_update_schema = get_server_create_schema - get_server_rebuild_schema = get_server_create_schema diff --git a/nova/api/openstack/compute/admin_actions.py b/nova/api/openstack/compute/admin_actions.py index 941678ee5ea..b258e868af9 100644 --- a/nova/api/openstack/compute/admin_actions.py +++ b/nova/api/openstack/compute/admin_actions.py @@ -22,21 +22,18 @@ from nova import compute from nova.compute import vm_states from nova import exception - -ALIAS = "os-admin-actions" +from nova.policies import admin_actions as aa_policies # States usable in resetState action # NOTE: It is necessary to update the schema of nova/api/openstack/compute/ # schemas/reset_server_state.py, when updating this state_map. state_map = dict(active=vm_states.ACTIVE, error=vm_states.ERROR) -authorize = extensions.os_compute_authorizer(ALIAS) - class AdminActionsController(wsgi.Controller): def __init__(self, *args, **kwargs): super(AdminActionsController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() @wsgi.response(202) @extensions.expected_errors((404, 409)) @@ -44,9 +41,9 @@ def __init__(self, *args, **kwargs): def _reset_network(self, req, id, body): """Permit admins to reset networking on a server.""" context = req.environ['nova.context'] - authorize(context, action='reset_network') + context.can(aa_policies.POLICY_ROOT % 'reset_network') + instance = common.get_instance(self.compute_api, context, id) try: - instance = common.get_instance(self.compute_api, context, id) self.compute_api.reset_network(context, instance) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) @@ -59,9 +56,9 @@ def _reset_network(self, req, id, body): def _inject_network_info(self, req, id, body): """Permit admins to inject network info into a server.""" context = req.environ['nova.context'] - authorize(context, action='inject_network_info') + context.can(aa_policies.POLICY_ROOT % 'inject_network_info') + instance = common.get_instance(self.compute_api, context, id) try: - instance = common.get_instance(self.compute_api, context, id) self.compute_api.inject_network_info(context, instance) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) @@ -75,7 +72,7 @@ def _inject_network_info(self, req, id, body): def _reset_state(self, req, id, body): """Permit admins to reset the state of a server.""" context = req.environ["nova.context"] - authorize(context, action='reset_state') + context.can(aa_policies.POLICY_ROOT % 'reset_state') # Identify the desired state from the body state = state_map[body["os-resetState"]["state"]] @@ -84,22 +81,3 @@ def _reset_state(self, req, id, body): instance.vm_state = state instance.task_state = None instance.save(admin_state_reset=True) - - -class AdminActions(extensions.V21APIExtensionBase): - """Enable admin-only server actions - - Actions include: resetNetwork, injectNetworkInfo, os-resetState - """ - - name = "AdminActions" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = AdminActionsController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/admin_password.py b/nova/api/openstack/compute/admin_password.py index ed1bd2050a1..09054e288b1 100644 --- a/nova/api/openstack/compute/admin_password.py +++ b/nova/api/openstack/compute/admin_password.py @@ -22,20 +22,17 @@ from nova import compute from nova import exception from nova.i18n import _ - - -ALIAS = "os-admin-password" -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import admin_password as ap_policies class AdminPasswordController(wsgi.Controller): def __init__(self, *args, **kwargs): super(AdminPasswordController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() - # TODO(eliqiao): Here should be 204(No content) instead of 202 by v2.1 - # +micorversions because the password has been changed when returning + # TODO(eliqiao): Here should be 204(No content) instead of 202 by v2.1+ + # microversions because the password has been changed when returning # a response. @wsgi.action('changePassword') @wsgi.response(202) @@ -43,15 +40,19 @@ def __init__(self, *args, **kwargs): @validation.schema(admin_password.change_password) def change_password(self, req, id, body): context = req.environ['nova.context'] - authorize(context) + instance = common.get_instance(self.compute_api, context, id) + context.can(ap_policies.BASE_POLICY_NAME, + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) password = body['changePassword']['adminPass'] - instance = common.get_instance(self.compute_api, context, id) try: self.compute_api.set_admin_password(context, instance, password) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.InstancePasswordSetFailed as e: + except (exception.InstancePasswordSetFailed, + exception.SetAdminPasswdNotSupported, + exception.InstanceAgentNotEnabled) as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as e: raise common.raise_http_conflict_for_instance_invalid_state( @@ -59,19 +60,3 @@ def change_password(self, req, id, body): except NotImplementedError: msg = _("Unable to set password on instance") common.raise_feature_not_supported(msg=msg) - - -class AdminPassword(extensions.V21APIExtensionBase): - """Admin password management support.""" - - name = "AdminPassword" - alias = ALIAS - version = 1 - - def get_resources(self): - return [] - - def get_controller_extensions(self): - controller = AdminPasswordController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/agents.py b/nova/api/openstack/compute/agents.py index ab01b29c619..489c85a920c 100644 --- a/nova/api/openstack/compute/agents.py +++ b/nova/api/openstack/compute/agents.py @@ -21,13 +21,10 @@ from nova.api import validation from nova import exception from nova import objects +from nova.policies import agents as agents_policies from nova import utils -ALIAS = "os-agents" -authorize = extensions.os_compute_authorizer(ALIAS) - - class AgentController(wsgi.Controller): """The agent is talking about guest agent.The host can use this for things like accessing files on the disk, configuring networking, @@ -54,7 +51,7 @@ class AgentController(wsgi.Controller): def index(self, req): """Return a list of all agent builds. Filter by hypervisor.""" context = req.environ['nova.context'] - authorize(context) + context.can(agents_policies.BASE_POLICY_NAME) hypervisor = None agents = [] if 'hypervisor' in req.GET: @@ -77,7 +74,7 @@ def index(self, req): def update(self, req, id, body): """Update an existing agent build.""" context = req.environ['nova.context'] - authorize(context) + context.can(agents_policies.BASE_POLICY_NAME) # TODO(oomichi): This parameter name "para" is different from the ones # of the other APIs. Most other names are resource names like "server" @@ -120,7 +117,7 @@ def update(self, req, id, body): def delete(self, req, id): """Deletes an existing agent build.""" context = req.environ['nova.context'] - authorize(context) + context.can(agents_policies.BASE_POLICY_NAME) try: utils.validate_integer(id, 'id') @@ -142,7 +139,7 @@ def delete(self, req, id): def create(self, req, body): """Creates a new agent build.""" context = req.environ['nova.context'] - authorize(context) + context.can(agents_policies.BASE_POLICY_NAME) agent = body['agent'] hypervisor = agent['hypervisor'] @@ -166,22 +163,3 @@ def create(self, req, body): except exception.AgentBuildExists as ex: raise webob.exc.HTTPConflict(explanation=ex.format_message()) return {'agent': agent} - - -class Agents(extensions.V21APIExtensionBase): - """Agents support.""" - - name = "Agents" - alias = ALIAS - version = 1 - - def get_resources(self): - resource = [extensions.ResourceExtension(ALIAS, - AgentController())] - return resource - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] diff --git a/nova/api/openstack/compute/aggregates.py b/nova/api/openstack/compute/aggregates.py index 34619a87ad1..3bcd759349e 100644 --- a/nova/api/openstack/compute/aggregates.py +++ b/nova/api/openstack/compute/aggregates.py @@ -19,6 +19,7 @@ from webob import exc +from nova.api.openstack import api_version_request from nova.api.openstack import common from nova.api.openstack.compute.schemas import aggregates from nova.api.openstack import extensions @@ -27,9 +28,7 @@ from nova.compute import api as compute_api from nova import exception from nova.i18n import _ - -ALIAS = "os-aggregates" -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import aggregates as aggr_policies def _get_context(req): @@ -45,9 +44,9 @@ def __init__(self): def index(self, req): """Returns a list a host aggregate's id, name, availability_zone.""" context = _get_context(req) - authorize(context, action='index') + context.can(aggr_policies.POLICY_ROOT % 'index') aggregates = self.api.get_aggregate_list(context) - return {'aggregates': [self._marshall_aggregate(a)['aggregate'] + return {'aggregates': [self._marshall_aggregate(req, a)['aggregate'] for a in aggregates]} # NOTE(gmann): Returns 200 for backwards compatibility but should be 201 @@ -60,7 +59,7 @@ def create(self, req, body): optional availability zone. """ context = _get_context(req) - authorize(context, action='create') + context.can(aggr_policies.POLICY_ROOT % 'create') host_aggregate = body["aggregate"] name = common.normalize_name(host_aggregate["name"]) avail_zone = host_aggregate.get("availability_zone") @@ -71,10 +70,13 @@ def create(self, req, body): aggregate = self.api.create_aggregate(context, name, avail_zone) except exception.AggregateNameExists as e: raise exc.HTTPConflict(explanation=e.format_message()) + except exception.ObjectActionError: + raise exc.HTTPConflict(explanation=_( + 'Not all aggregates have been migrated to the API database')) except exception.InvalidAggregateAction as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - agg = self._marshall_aggregate(aggregate) + agg = self._marshall_aggregate(req, aggregate) # To maintain the same API result as before the changes for returning # nova objects were made. @@ -87,12 +89,12 @@ def create(self, req, body): def show(self, req, id): """Shows the details of an aggregate, hosts and metadata included.""" context = _get_context(req) - authorize(context, action='show') + context.can(aggr_policies.POLICY_ROOT % 'show') try: aggregate = self.api.get_aggregate(context, id) except exception.AggregateNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) @extensions.expected_errors((400, 404, 409)) @validation.schema(aggregates.update_v20, '2.0', '2.0') @@ -100,7 +102,7 @@ def show(self, req, id): def update(self, req, id, body): """Updates the name and/or availability_zone of given aggregate.""" context = _get_context(req) - authorize(context, action='update') + context.can(aggr_policies.POLICY_ROOT % 'update') updates = body["aggregate"] if 'name' in updates: updates['name'] = common.normalize_name(updates['name']) @@ -114,7 +116,7 @@ def update(self, req, id, body): except exception.InvalidAggregateAction as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) # NOTE(gmann): Returns 200 for backwards compatibility but should be 204 # as this operation complete the deletion of aggregate resource and return @@ -123,7 +125,7 @@ def update(self, req, id, body): def delete(self, req, id): """Removes an aggregate by id.""" context = _get_context(req) - authorize(context, action='delete') + context.can(aggr_policies.POLICY_ROOT % 'delete') try: self.api.delete_aggregate(context, id) except exception.AggregateNotFound as e: @@ -134,7 +136,7 @@ def delete(self, req, id): # NOTE(gmann): Returns 200 for backwards compatibility but should be 202 # for representing async API as this API just accepts the request and # request hypervisor driver to complete the same in async mode. - @extensions.expected_errors((400, 404, 409)) + @extensions.expected_errors((404, 409)) @wsgi.action('add_host') @validation.schema(aggregates.add_host) def _add_host(self, req, id, body): @@ -142,21 +144,22 @@ def _add_host(self, req, id, body): host = body['add_host']['host'] context = _get_context(req) - authorize(context, action='add_host') + context.can(aggr_policies.POLICY_ROOT % 'add_host') try: aggregate = self.api.add_host_to_aggregate(context, id, host) except (exception.AggregateNotFound, + exception.HostMappingNotFound, exception.ComputeHostNotFound) as e: raise exc.HTTPNotFound(explanation=e.format_message()) except (exception.AggregateHostExists, exception.InvalidAggregateAction) as e: raise exc.HTTPConflict(explanation=e.format_message()) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) # NOTE(gmann): Returns 200 for backwards compatibility but should be 202 # for representing async API as this API just accepts the request and # request hypervisor driver to complete the same in async mode. - @extensions.expected_errors((400, 404, 409)) + @extensions.expected_errors((404, 409)) @wsgi.action('remove_host') @validation.schema(aggregates.remove_host) def _remove_host(self, req, id, body): @@ -164,11 +167,11 @@ def _remove_host(self, req, id, body): host = body['remove_host']['host'] context = _get_context(req) - authorize(context, action='remove_host') + context.can(aggr_policies.POLICY_ROOT % 'remove_host') try: aggregate = self.api.remove_host_from_aggregate(context, id, host) except (exception.AggregateNotFound, exception.AggregateHostNotFound, - exception.ComputeHostNotFound): + exception.HostMappingNotFound, exception.ComputeHostNotFound): msg = _('Cannot remove host %(host)s in aggregate %(id)s') % { 'host': host, 'id': id} raise exc.HTTPNotFound(explanation=msg) @@ -176,7 +179,7 @@ def _remove_host(self, req, id, body): msg = _('Cannot remove host %(host)s in aggregate %(id)s') % { 'host': host, 'id': id} raise exc.HTTPConflict(explanation=msg) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) @extensions.expected_errors((400, 404)) @wsgi.action('set_metadata') @@ -184,7 +187,7 @@ def _remove_host(self, req, id, body): def _set_metadata(self, req, id, body): """Replaces the aggregate's existing metadata with new metadata.""" context = _get_context(req) - authorize(context, action='set_metadata') + context.can(aggr_policies.POLICY_ROOT % 'set_metadata') metadata = body["set_metadata"]["metadata"] try: @@ -195,18 +198,19 @@ def _set_metadata(self, req, id, body): except exception.InvalidAggregateAction as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - return self._marshall_aggregate(aggregate) + return self._marshall_aggregate(req, aggregate) - def _marshall_aggregate(self, aggregate): + def _marshall_aggregate(self, req, aggregate): _aggregate = {} - for key, value in self._build_aggregate_items(aggregate): + for key, value in self._build_aggregate_items(req, aggregate): # NOTE(danms): The original API specified non-TZ-aware timestamps if isinstance(value, datetime.datetime): value = value.replace(tzinfo=None) _aggregate[key] = value return {"aggregate": _aggregate} - def _build_aggregate_items(self, aggregate): + def _build_aggregate_items(self, req, aggregate): + show_uuid = api_version_request.is_supported(req, min_version="2.41") keys = aggregate.obj_fields # NOTE(rlrossit): Within the compute API, metadata will always be # set on the aggregate object (at a minimum to {}). Because of this, @@ -214,27 +218,7 @@ def _build_aggregate_items(self, aggregate): # case it is only ['availability_zone']) without worrying about # lazy-loading an unset variable for key in keys: - # NOTE(danms): Skip the uuid field because we have no microversion - # to expose it if ((aggregate.obj_attr_is_set(key) or key in aggregate.obj_extra_fields) and - key != 'uuid'): + (show_uuid or key != 'uuid')): yield key, getattr(aggregate, key) - - -class Aggregates(extensions.V21APIExtensionBase): - """Admin-only aggregate administration.""" - - name = "Aggregates" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [extensions.ResourceExtension( - ALIAS, - AggregateController(), - member_actions={'action': 'POST'})] - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/assisted_volume_snapshots.py b/nova/api/openstack/compute/assisted_volume_snapshots.py index dcf08705d87..b2f60933a4a 100644 --- a/nova/api/openstack/compute/assisted_volume_snapshots.py +++ b/nova/api/openstack/compute/assisted_volume_snapshots.py @@ -16,7 +16,6 @@ """The Assisted volume snapshots extension.""" -from oslo_log import log as logging from oslo_serialization import jsonutils import six from webob import exc @@ -27,19 +26,14 @@ from nova.api import validation from nova import compute from nova import exception -from nova.i18n import _LI - - -LOG = logging.getLogger(__name__) -ALIAS = 'os-assisted-volume-snapshots' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import assisted_volume_snapshots as avs_policies class AssistedVolumeSnapshotsController(wsgi.Controller): """The Assisted volume snapshots API controller for the OpenStack API.""" def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() super(AssistedVolumeSnapshotsController, self).__init__() @extensions.expected_errors(400) @@ -47,29 +41,33 @@ def __init__(self): def create(self, req, body): """Creates a new snapshot.""" context = req.environ['nova.context'] - authorize(context, action='create') + context.can(avs_policies.POLICY_ROOT % 'create') snapshot = body['snapshot'] create_info = snapshot['create_info'] volume_id = snapshot['volume_id'] - LOG.info(_LI("Create assisted snapshot from volume %s"), volume_id, - context=context) try: return self.compute_api.volume_snapshot_create(context, volume_id, create_info) except (exception.VolumeBDMNotFound, + exception.VolumeBDMIsMultiAttach, exception.InvalidVolume) as error: raise exc.HTTPBadRequest(explanation=error.format_message()) + except (exception.InstanceInvalidState, + exception.InstanceNotReady) as e: + # TODO(mriedem) InstanceInvalidState and InstanceNotReady would + # normally result in a 409 but that would require bumping the + # microversion, which we should just do in a single microversion + # across all APIs when we fix status code wrinkles. + raise exc.HTTPBadRequest(explanation=e.format_message()) @wsgi.response(204) @extensions.expected_errors((400, 404)) def delete(self, req, id): """Delete a snapshot.""" context = req.environ['nova.context'] - authorize(context, action='delete') - - LOG.info(_LI("Delete snapshot with id: %s"), id, context=context) + context.can(avs_policies.POLICY_ROOT % 'delete') delete_metadata = {} delete_metadata.update(req.GET) @@ -88,22 +86,10 @@ def delete(self, req, id): raise exc.HTTPBadRequest(explanation=error.format_message()) except exception.NotFound as e: return exc.HTTPNotFound(explanation=e.format_message()) - - -class AssistedVolumeSnapshots(extensions.V21APIExtensionBase): - """Assisted volume snapshots.""" - - name = "AssistedVolumeSnapshots" - alias = ALIAS - version = 1 - - def get_resources(self): - res = [extensions.ResourceExtension(ALIAS, - AssistedVolumeSnapshotsController())] - return res - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] + except (exception.InstanceInvalidState, + exception.InstanceNotReady) as e: + # TODO(mriedem) InstanceInvalidState and InstanceNotReady would + # normally result in a 409 but that would require bumping the + # microversion, which we should just do in a single microversion + # across all APIs when we fix status code wrinkles. + raise exc.HTTPBadRequest(explanation=e.format_message()) diff --git a/nova/api/openstack/compute/attach_interfaces.py b/nova/api/openstack/compute/attach_interfaces.py index cbd5c6be582..c36c05759d0 100644 --- a/nova/api/openstack/compute/attach_interfaces.py +++ b/nova/api/openstack/compute/attach_interfaces.py @@ -27,10 +27,7 @@ from nova import exception from nova.i18n import _ from nova import network - - -ALIAS = 'os-attach-interfaces' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import attach_interfaces as ai_policies def _translate_interface_attachment_view(port_info): @@ -48,21 +45,37 @@ class InterfaceAttachmentController(wsgi.Controller): """The interface attachment API controller for the OpenStack API.""" def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) - self.network_api = network.API(skip_policy_check=True) + self.compute_api = compute.API() + self.network_api = network.API() super(InterfaceAttachmentController, self).__init__() @extensions.expected_errors((404, 501)) def index(self, req, server_id): """Returns the list of interface attachments for a given instance.""" - return self._items(req, server_id, - entity_maker=_translate_interface_attachment_view) + context = req.environ['nova.context'] + context.can(ai_policies.BASE_POLICY_NAME) + + instance = common.get_instance(self.compute_api, context, server_id) + search_opts = {'device_id': instance.uuid} + + try: + data = self.network_api.list_ports(context, **search_opts) + except exception.NotFound as e: + raise exc.HTTPNotFound(explanation=e.format_message()) + except NotImplementedError: + common.raise_feature_not_supported() + + ports = data.get('ports', []) + entity_maker = _translate_interface_attachment_view + results = [entity_maker(port) for port in ports] + + return {'interfaceAttachments': results} @extensions.expected_errors((403, 404)) def show(self, req, server_id, id): """Return data about the given interface attachment.""" context = req.environ['nova.context'] - authorize(context) + context.can(ai_policies.BASE_POLICY_NAME) port_id = id # NOTE(mriedem): We need to verify the instance actually exists from @@ -86,19 +99,23 @@ def show(self, req, server_id, id): port_info['port'])} @extensions.expected_errors((400, 404, 409, 500, 501)) - @validation.schema(attach_interfaces.create) + @validation.schema(attach_interfaces.create, '2.0', '2.48') + @validation.schema(attach_interfaces.create_v249, '2.49') def create(self, req, server_id, body): """Attach an interface to an instance.""" context = req.environ['nova.context'] - authorize(context) + context.can(ai_policies.BASE_POLICY_NAME) + context.can(ai_policies.POLICY_ROOT % 'create') network_id = None port_id = None req_ip = None + tag = None if body: attachment = body['interfaceAttachment'] network_id = attachment.get('net_id', None) port_id = attachment.get('port_id', None) + tag = attachment.get('tag', None) try: req_ip = attachment['fixed_ips'][0]['ip_address'] except Exception: @@ -114,12 +131,14 @@ def create(self, req, server_id, body): instance = common.get_instance(self.compute_api, context, server_id) try: vif = self.compute_api.attach_interface(context, - instance, network_id, port_id, req_ip) + instance, network_id, port_id, req_ip, tag=tag) except (exception.InterfaceAttachFailedNoNetwork, - exception.NetworkDuplicated, exception.NetworkAmbiguous, exception.NoMoreFixedIps, - exception.PortNotUsable) as e: + exception.PortNotUsable, + exception.AttachInterfaceNotSupported, + exception.SecurityGroupCannotBeApplied, + exception.TaggedAttachmentNotSupported) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except (exception.InstanceIsLocked, exception.FixedIpAlreadyInUse, @@ -128,8 +147,6 @@ def create(self, req, server_id, body): except (exception.PortNotFound, exception.NetworkNotFound) as e: raise exc.HTTPNotFound(explanation=e.format_message()) - except NotImplementedError: - common.raise_feature_not_supported() except exception.InterfaceAttachFailed as e: raise webob.exc.HTTPInternalServerError( explanation=e.format_message()) @@ -144,10 +161,12 @@ def create(self, req, server_id, body): def delete(self, req, server_id, id): """Detach an interface from an instance.""" context = req.environ['nova.context'] - authorize(context) + context.can(ai_policies.BASE_POLICY_NAME) + context.can(ai_policies.POLICY_ROOT % 'delete') port_id = id - instance = common.get_instance(self.compute_api, context, server_id) + instance = common.get_instance(self.compute_api, context, server_id, + expected_attrs=['device_metadata']) try: self.compute_api.detach_interface(context, instance, port_id=port_id) @@ -160,45 +179,3 @@ def delete(self, req, server_id, id): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'detach_interface', server_id) - - def _items(self, req, server_id, entity_maker): - """Returns a list of attachments, transformed through entity_maker.""" - context = req.environ['nova.context'] - authorize(context) - - instance = common.get_instance(self.compute_api, context, server_id) - search_opts = {'device_id': instance.uuid} - - try: - data = self.network_api.list_ports(context, **search_opts) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except NotImplementedError: - common.raise_feature_not_supported() - - ports = data.get('ports', []) - results = [entity_maker(port) for port in ports] - - return {'interfaceAttachments': results} - - -class AttachInterfaces(extensions.V21APIExtensionBase): - """Attach interface support.""" - - name = "AttachInterfaces" - alias = ALIAS - version = 1 - - def get_resources(self): - res = [extensions.ResourceExtension('os-interface', - InterfaceAttachmentController(), - parent=dict( - member_name='server', - collection_name='servers'))] - return res - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] diff --git a/nova/api/openstack/compute/availability_zone.py b/nova/api/openstack/compute/availability_zone.py index 97b2520ed8f..33e61d3fb11 100644 --- a/nova/api/openstack/compute/availability_zone.py +++ b/nova/api/openstack/compute/availability_zone.py @@ -16,14 +16,13 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import availability_zones +from nova import compute import nova.conf -from nova import objects +from nova.policies import availability_zone as az_policies from nova import servicegroup CONF = nova.conf.CONF -ALIAS = "os-availability-zone" ATTRIBUTE_NAME = "availability_zone" -authorize = extensions.os_compute_authorizer(ALIAS) class AvailabilityZoneController(wsgi.Controller): @@ -32,6 +31,7 @@ class AvailabilityZoneController(wsgi.Controller): def __init__(self): super(AvailabilityZoneController, self).__init__() self.servicegroup_api = servicegroup.API() + self.host_api = compute.HostAPI() def _get_filtered_availability_zones(self, zones, is_available): result = [] @@ -62,8 +62,9 @@ def _describe_availability_zones_verbose(self, context, **kwargs): availability_zones.get_availability_zones(ctxt) # Available services - enabled_services = objects.ServiceList.get_all(context, disabled=False, - set_zones=True) + enabled_services = self.host_api.service_get_all( + context, {'disabled': False}, set_zones=True, all_cells=True) + zone_hosts = {} host_services = {} api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata') @@ -106,7 +107,7 @@ def _describe_availability_zones_verbose(self, context, **kwargs): def index(self, req): """Returns a summary list of availability zone.""" context = req.environ['nova.context'] - authorize(context, action='list') + context.can(az_policies.POLICY_ROOT % 'list') return self._describe_availability_zones(context) @@ -114,42 +115,22 @@ def index(self, req): def detail(self, req): """Returns a detailed list of availability zone.""" context = req.environ['nova.context'] - authorize(context, action='detail') + context.can(az_policies.POLICY_ROOT % 'detail') return self._describe_availability_zones_verbose(context) -class AvailabilityZone(extensions.V21APIExtensionBase): - """1. Add availability_zone to the Create Server API. - 2. Add availability zones describing. - """ - - name = "AvailabilityZone" - alias = ALIAS - version = 1 - - def get_resources(self): - resource = [extensions.ResourceExtension(ALIAS, - AvailabilityZoneController(), - collection_actions={'detail': 'GET'})] - return resource - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] - - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - # NOTE(alex_xu): For v2.1 compat mode, we strip the spaces when create - # availability_zone. But we don't strip at here for backward-compatible - # with some users already created availability_zone with - # leading/trailing spaces with legacy v2 API. - create_kwargs['availability_zone'] = server_dict.get(ATTRIBUTE_NAME) - - def get_server_create_schema(self, version): - if version == "2.0": - return schema.server_create_v20 - return schema.server_create +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + # NOTE(alex_xu): For v2.1 compat mode, we strip the spaces when create + # availability_zone. But we don't strip at here for backward-compatible + # with some users already created availability_zone with + # leading/trailing spaces with legacy v2 API. + create_kwargs['availability_zone'] = server_dict.get(ATTRIBUTE_NAME) + + +def get_server_create_schema(version): + if version == "2.0": + return schema.server_create_v20 + return schema.server_create diff --git a/nova/api/openstack/compute/baremetal_nodes.py b/nova/api/openstack/compute/baremetal_nodes.py index faef487f1ad..8125a75b337 100644 --- a/nova/api/openstack/compute/baremetal_nodes.py +++ b/nova/api/openstack/compute/baremetal_nodes.py @@ -19,18 +19,18 @@ from oslo_utils import importutils import webob +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import wsgi import nova.conf from nova.i18n import _ +from nova.policies import baremetal_nodes as bn_policies ironic_client = importutils.try_import('ironicclient.client') ironic_exc = importutils.try_import('ironicclient.exc') -ALIAS = "os-baremetal-nodes" -authorize = extensions.os_compute_authorizer(ALIAS) - node_fields = ['id', 'cpus', 'local_gb', 'memory_mb', 'pm_address', 'pm_user', 'service_host', 'terminal_port', 'instance_uuid'] @@ -39,21 +39,6 @@ interface_fields = ['id', 'address', 'datapath_id', 'port_no'] CONF = nova.conf.CONF -CONF.import_opt('api_version', - 'nova.virt.ironic.driver', - group='ironic') -CONF.import_opt('api_endpoint', - 'nova.virt.ironic.driver', - group='ironic') -CONF.import_opt('admin_username', - 'nova.virt.ironic.driver', - group='ironic') -CONF.import_opt('admin_password', - 'nova.virt.ironic.driver', - group='ironic') -CONF.import_opt('admin_tenant_name', - 'nova.virt.ironic.driver', - group='ironic') def _check_ironic_client_enabled(): @@ -73,7 +58,11 @@ def _get_ironic_client(): 'os_endpoint_type': 'public', 'insecure': 'true', 'ironic_url': CONF.ironic.api_endpoint} - icli = ironic_client.get_client(CONF.ironic.api_version, **kwargs) + # NOTE(mriedem): The 1 api_version arg here is the only valid value for + # the client, but it's not even used so it doesn't really matter. The + # ironic client wrapper in the virt driver actually uses a hard-coded + # microversion via the os_ironic_api_version kwarg. + icli = ironic_client.get_client(1, **kwargs) return icli @@ -95,10 +84,11 @@ def _node_dict(self, node_ref): d[f] = node_ref.get(f) return d + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((404, 501)) def index(self, req): context = req.environ['nova.context'] - authorize(context) + context.can(bn_policies.BASE_POLICY_NAME) nodes = [] # proxy command to Ironic _check_ironic_client_enabled() @@ -115,10 +105,11 @@ def index(self, req): nodes.append(node) return {'nodes': nodes} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((404, 501)) def show(self, req, id): context = req.environ['nova.context'] - authorize(context) + context.can(bn_policies.BASE_POLICY_NAME) # proxy command to Ironic _check_ironic_client_enabled() icli = _get_ironic_client() @@ -140,40 +131,24 @@ def show(self, req, id): node['interfaces'].append({'address': port.address}) return {'node': node} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(400) def create(self, req, body): - _no_ironic_proxy("port-create") + _no_ironic_proxy("node-create") + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(400) def delete(self, req, id): - _no_ironic_proxy("port-create") + _no_ironic_proxy("node-delete") + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.action('add_interface') @extensions.expected_errors(400) def _add_interface(self, req, id, body): _no_ironic_proxy("port-create") + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.action('remove_interface') @extensions.expected_errors(400) def _remove_interface(self, req, id, body): _no_ironic_proxy("port-delete") - - -class BareMetalNodes(extensions.V21APIExtensionBase): - """Admin-only bare-metal node administration.""" - - name = "BareMetalNodes" - alias = ALIAS - version = 1 - - def get_resources(self): - resource = [extensions.ResourceExtension(ALIAS, - BareMetalNodeController(), - member_actions={"action": "POST"})] - return resource - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] diff --git a/nova/api/openstack/compute/block_device_mapping.py b/nova/api/openstack/compute/block_device_mapping.py index a449abe0614..4b88bc1d8f1 100644 --- a/nova/api/openstack/compute/block_device_mapping.py +++ b/nova/api/openstack/compute/block_device_mapping.py @@ -17,61 +17,58 @@ from webob import exc +from nova.api.openstack import api_version_request from nova.api.openstack.compute.schemas import block_device_mapping as \ schema_block_device_mapping -from nova.api.openstack import extensions from nova import block_device from nova import exception from nova.i18n import _ -ALIAS = "os-block-device-mapping" ATTRIBUTE_NAME = "block_device_mapping_v2" LEGACY_ATTRIBUTE_NAME = "block_device_mapping" -class BlockDeviceMapping(extensions.V21APIExtensionBase): - """Block device mapping boot support.""" - - name = "BlockDeviceMapping" - alias = ALIAS - version = 1 - - def get_resources(self): - return [] - - def get_controller_extensions(self): - return [] - - # use nova.api.extensions.server.extensions entry point to modify - # server create kwargs - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - - # Have to check whether --image is given, see bug 1433609 - image_href = server_dict.get('imageRef') - image_uuid_specified = image_href is not None - - bdm = server_dict.get(ATTRIBUTE_NAME, []) - legacy_bdm = server_dict.get(LEGACY_ATTRIBUTE_NAME, []) - - if bdm and legacy_bdm: - expl = _('Using different block_device_mapping syntaxes ' - 'is not allowed in the same request.') - raise exc.HTTPBadRequest(explanation=expl) - - try: - block_device_mapping = [ - block_device.BlockDeviceDict.from_api(bdm_dict, - image_uuid_specified) - for bdm_dict in bdm] - except exception.InvalidBDMFormat as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - if block_device_mapping: - create_kwargs['block_device_mapping'] = block_device_mapping - # Unset the legacy_bdm flag if we got a block device mapping. - create_kwargs['legacy_bdm'] = False - - def get_server_create_schema(self, version): +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + + # Have to check whether --image is given, see bug 1433609 + image_href = server_dict.get('imageRef') + image_uuid_specified = image_href is not None + + bdm = server_dict.get(ATTRIBUTE_NAME, []) + legacy_bdm = server_dict.get(LEGACY_ATTRIBUTE_NAME, []) + + if bdm and legacy_bdm: + expl = _('Using different block_device_mapping syntaxes ' + 'is not allowed in the same request.') + raise exc.HTTPBadRequest(explanation=expl) + + try: + block_device_mapping = [ + block_device.BlockDeviceDict.from_api(bdm_dict, + image_uuid_specified) + for bdm_dict in bdm] + except exception.InvalidBDMFormat as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) + + if block_device_mapping: + create_kwargs['block_device_mapping'] = block_device_mapping + # Unset the legacy_bdm flag if we got a block device mapping. + create_kwargs['legacy_bdm'] = False + + +def get_server_create_schema(version): + request_version = api_version_request.APIVersionRequest(version) + version_242 = api_version_request.APIVersionRequest('2.42') + + # NOTE(artom) the following conditional was merged as + # "if version == '2.32'" The intent all along was to check whether + # version was greater than or equal to 2.32. In other words, we wanted + # to support tags in versions 2.32 and up, but ended up supporting them + # in version 2.32 only. Since we need a new microversion to add request + # body attributes, tags have been re-added in version 2.42. + if version == '2.32' or request_version >= version_242: + return schema_block_device_mapping.server_create_with_tags + else: return schema_block_device_mapping.server_create diff --git a/nova/api/openstack/compute/block_device_mapping_v1.py b/nova/api/openstack/compute/block_device_mapping_v1.py index ee38bceae79..0e47231a801 100644 --- a/nova/api/openstack/compute/block_device_mapping_v1.py +++ b/nova/api/openstack/compute/block_device_mapping_v1.py @@ -20,49 +20,33 @@ from nova.api.openstack.compute.schemas import block_device_mapping_v1 as \ schema_block_device_mapping -from nova.api.openstack import extensions from nova.i18n import _ -ALIAS = "os-block-device-mapping-v1" ATTRIBUTE_NAME = "block_device_mapping" ATTRIBUTE_NAME_V2 = "block_device_mapping_v2" -class BlockDeviceMappingV1(extensions.V21APIExtensionBase): - """Block device mapping boot support.""" +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + block_device_mapping = server_dict.get(ATTRIBUTE_NAME, []) + block_device_mapping_v2 = server_dict.get(ATTRIBUTE_NAME_V2, []) - name = "BlockDeviceMappingV1" - alias = ALIAS - version = 1 + if block_device_mapping and block_device_mapping_v2: + expl = _('Using different block_device_mapping syntaxes ' + 'is not allowed in the same request.') + raise exc.HTTPBadRequest(explanation=expl) - def get_resources(self): - return [] + for bdm in block_device_mapping: + if 'delete_on_termination' in bdm: + bdm['delete_on_termination'] = strutils.bool_from_string( + bdm['delete_on_termination']) - def get_controller_extensions(self): - return [] + if block_device_mapping: + create_kwargs['block_device_mapping'] = block_device_mapping + # Sets the legacy_bdm flag if we got a legacy block device mapping. + create_kwargs['legacy_bdm'] = True - # use nova.api.extensions.server.extensions entry point to modify - # server create kwargs - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - block_device_mapping = server_dict.get(ATTRIBUTE_NAME, []) - block_device_mapping_v2 = server_dict.get(ATTRIBUTE_NAME_V2, []) - if block_device_mapping and block_device_mapping_v2: - expl = _('Using different block_device_mapping syntaxes ' - 'is not allowed in the same request.') - raise exc.HTTPBadRequest(explanation=expl) - - for bdm in block_device_mapping: - if 'delete_on_termination' in bdm: - bdm['delete_on_termination'] = strutils.bool_from_string( - bdm['delete_on_termination']) - - if block_device_mapping: - create_kwargs['block_device_mapping'] = block_device_mapping - # Sets the legacy_bdm flag if we got a legacy block device mapping. - create_kwargs['legacy_bdm'] = True - - def get_server_create_schema(self, version): - return schema_block_device_mapping.server_create +def get_server_create_schema(version): + return schema_block_device_mapping.server_create diff --git a/nova/api/openstack/compute/cells.py b/nova/api/openstack/compute/cells.py index 605a37ae19b..1e85e37140f 100644 --- a/nova/api/openstack/compute/cells.py +++ b/nova/api/openstack/compute/cells.py @@ -30,20 +30,18 @@ import nova.conf from nova import exception from nova.i18n import _ +from nova.policies import cells as cells_policies from nova import rpc CONF = nova.conf.CONF -ALIAS = "os-cells" -authorize = extensions.os_compute_authorizer(ALIAS) - def _filter_keys(item, keys): """Filters all model attributes except for keys item is a dict """ - return {k: v for k, v in six.iteritems(item) if k in keys} + return {k: v for k, v in item.items() if k in keys} def _fixup_cell_info(cell_info, keys): @@ -108,7 +106,7 @@ def _get_cells(self, ctxt, req, detail=False): def index(self, req): """Return all cells in brief.""" ctxt = req.environ['nova.context'] - authorize(ctxt) + ctxt.can(cells_policies.BASE_POLICY_NAME) return self._get_cells(ctxt, req) @extensions.expected_errors(501) @@ -116,7 +114,7 @@ def index(self, req): def detail(self, req): """Return all cells in detail.""" ctxt = req.environ['nova.context'] - authorize(ctxt) + ctxt.can(cells_policies.BASE_POLICY_NAME) return self._get_cells(ctxt, req, detail=True) @extensions.expected_errors(501) @@ -124,7 +122,7 @@ def detail(self, req): def info(self, req): """Return name and capabilities for this cell.""" context = req.environ['nova.context'] - authorize(context) + context.can(cells_policies.BASE_POLICY_NAME) cell_capabs = {} my_caps = CONF.cells.capabilities for cap in my_caps: @@ -145,7 +143,7 @@ def capacities(self, req, id=None): # TODO(kaushikc): return capacities as a part of cell info and # cells detail calls in v2.1, along with capabilities context = req.environ['nova.context'] - authorize(context) + context.can(cells_policies.BASE_POLICY_NAME) try: capacities = self.cells_rpcapi.get_capacities(context, cell_name=id) @@ -159,7 +157,7 @@ def capacities(self, req, id=None): def show(self, req, id): """Return data about the given cell name. 'id' is a cell name.""" context = req.environ['nova.context'] - authorize(context) + context.can(cells_policies.BASE_POLICY_NAME) try: cell = self.cells_rpcapi.cell_get(context, id) except exception.CellNotFound as e: @@ -175,7 +173,7 @@ def delete(self, req, id): """Delete a child or parent cell entry. 'id' is a cell name.""" context = req.environ['nova.context'] - authorize(context, action="delete") + context.can(cells_policies.POLICY_ROOT % "delete") try: num_deleted = self.cells_rpcapi.cell_delete(context, id) @@ -197,8 +195,7 @@ def _normalize_cell(self, cell, existing=None): # Start with the cell type conversion if 'type' in cell: - cell['is_parent'] = cell['type'] == 'parent' - del cell['type'] + cell['is_parent'] = cell.pop('type') == 'parent' # Avoid cell type being overwritten to 'child' elif existing: cell['is_parent'] = existing['is_parent'] @@ -243,7 +240,7 @@ def create(self, req, body): """Create a child cell entry.""" context = req.environ['nova.context'] - authorize(context, action="create") + context.can(cells_policies.POLICY_ROOT % "create") cell = body['cell'] self._normalize_cell(cell) @@ -261,7 +258,7 @@ def update(self, req, id, body): """Update a child cell entry. 'id' is the cell name to update.""" context = req.environ['nova.context'] - authorize(context, action="update") + context.can(cells_policies.POLICY_ROOT % "update") cell = body['cell'] cell.pop('id', None) @@ -295,7 +292,7 @@ def sync_instances(self, req, body): """Tell all cells to sync instance info.""" context = req.environ['nova.context'] - authorize(context, action="sync_instances") + context.can(cells_policies.POLICY_ROOT % "sync_instances") project_id = body.pop('project_id', None) deleted = body.pop('deleted', False) @@ -304,32 +301,3 @@ def sync_instances(self, req, body): deleted = strutils.bool_from_string(deleted, strict=True) self.cells_rpcapi.sync_instances(context, project_id=project_id, updated_since=updated_since, deleted=deleted) - - -class Cells(extensions.V21APIExtensionBase): - """Enables cells-related functionality such as adding neighbor cells, - listing neighbor cells, and getting the capabilities of the local cell. - """ - - name = "Cells" - alias = ALIAS - version = 1 - - def get_resources(self): - coll_actions = { - 'detail': 'GET', - 'info': 'GET', - 'sync_instances': 'POST', - 'capacities': 'GET', - } - memb_actions = { - 'capacities': 'GET', - } - - res = extensions.ResourceExtension(ALIAS, CellsController(), - collection_actions=coll_actions, - member_actions=memb_actions) - return [res] - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/certificates.py b/nova/api/openstack/compute/certificates.py index 420e32a97ab..3f588f46578 100644 --- a/nova/api/openstack/compute/certificates.py +++ b/nova/api/openstack/compute/certificates.py @@ -14,75 +14,19 @@ import webob.exc -from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import wsgi -import nova.cert.rpcapi -from nova import exception -from nova.i18n import _ - -ALIAS = "os-certificates" -authorize = extensions.os_compute_authorizer(ALIAS) - - -def _translate_certificate_view(certificate, private_key=None): - return { - 'data': certificate, - 'private_key': private_key, - } class CertificatesController(wsgi.Controller): """The x509 Certificates API controller for the OpenStack API.""" - def __init__(self): - self.cert_rpcapi = nova.cert.rpcapi.CertAPI() - super(CertificatesController, self).__init__() - - @extensions.expected_errors((404, 501)) + @extensions.expected_errors(410) def show(self, req, id): """Return certificate information.""" - context = req.environ['nova.context'] - authorize(context, action='show') - if id != 'root': - msg = _("Only root certificate can be retrieved.") - # TODO(oomichi): This seems a HTTPBadRequest case because of the - # above message. This will be changed with a microversion in the - # future. - common.raise_feature_not_supported(msg=msg) - try: - cert = self.cert_rpcapi.fetch_ca(context, - project_id=context.project_id) - except exception.CryptoCAFileNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - return {'certificate': _translate_certificate_view(cert)} + raise webob.exc.HTTPGone() - # NOTE(gmann): Here should be 201 instead of 200 by v2.1 - # +microversions because the resource certificate has been created - # completely when returning a response. - @extensions.expected_errors(()) + @extensions.expected_errors((410)) def create(self, req, body=None): """Create a certificate.""" - context = req.environ['nova.context'] - authorize(context, action='create') - pk, cert = self.cert_rpcapi.generate_x509_cert(context, - user_id=context.user_id, project_id=context.project_id) - return {'certificate': _translate_certificate_view(cert, pk)} - - -class Certificates(extensions.V21APIExtensionBase): - """Certificates support.""" - - name = "Certificates" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [ - extensions.ResourceExtension(ALIAS, - CertificatesController(), - member_actions={})] - return resources - - def get_controller_extensions(self): - return [] + raise webob.exc.HTTPGone() diff --git a/nova/api/openstack/compute/cloudpipe.py b/nova/api/openstack/compute/cloudpipe.py index 2529b2b1b1d..ddc6e341ed0 100644 --- a/nova/api/openstack/compute/cloudpipe.py +++ b/nova/api/openstack/compute/cloudpipe.py @@ -14,183 +14,29 @@ """Connect your vlan to the world.""" -from oslo_config import cfg -from oslo_utils import fileutils from webob import exc -from nova.api.openstack.compute.schemas import cloudpipe from nova.api.openstack import extensions from nova.api.openstack import wsgi -from nova.api import validation -from nova.cloudpipe import pipelib -from nova import compute -from nova.compute import utils as compute_utils -from nova.compute import vm_states -from nova import exception -from nova.i18n import _ -from nova import network -from nova import objects -from nova import utils - -CONF = cfg.CONF -CONF.import_opt('keys_path', 'nova.crypto') - -ALIAS = 'os-cloudpipe' -authorize = extensions.os_compute_authorizer(ALIAS) class CloudpipeController(wsgi.Controller): """Handle creating and listing cloudpipe instances.""" - def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) - self.network_api = network.API(skip_policy_check=True) - self.cloudpipe = pipelib.CloudPipe(skip_policy_check=True) - self.setup() - - def setup(self): - """Ensure the keychains and folders exist.""" - # NOTE(vish): One of the drawbacks of doing this in the api is - # the keys will only be on the api node that launched - # the cloudpipe. - fileutils.ensure_tree(CONF.keys_path) - - def _get_all_cloudpipes(self, context): - """Get all cloudpipes.""" - instances = self.compute_api.get_all(context, - search_opts={'deleted': False}, - want_objects=True) - return [instance for instance in instances - if pipelib.is_vpn_image(instance.image_ref) - and instance.vm_state != vm_states.DELETED] - - def _get_cloudpipe_for_project(self, context): - """Get the cloudpipe instance for a project from context.""" - cloudpipes = self._get_all_cloudpipes(context) or [None] - return cloudpipes[0] - - def _vpn_dict(self, context, project_id, instance): - elevated = context.elevated() - rv = {'project_id': project_id} - if not instance: - rv['state'] = 'pending' - return rv - rv['instance_id'] = instance.uuid - rv['created_at'] = utils.isotime(instance.created_at) - nw_info = compute_utils.get_nw_info_for_instance(instance) - if not nw_info: - return rv - vif = nw_info[0] - ips = [ip for ip in vif.fixed_ips() if ip['version'] == 4] - if ips: - rv['internal_ip'] = ips[0]['address'] - # NOTE(vish): Currently network_api.get does an owner check on - # project_id. This is probably no longer necessary - # but rather than risk changes in the db layer, - # we are working around it here by changing the - # project_id in the context. This can be removed - # if we remove the project_id check in the db. - elevated.project_id = project_id - network = self.network_api.get(elevated, vif['network']['id']) - if network: - vpn_ip = network['vpn_public_address'] - vpn_port = network['vpn_public_port'] - rv['public_ip'] = vpn_ip - rv['public_port'] = vpn_port - if vpn_ip and vpn_port: - if utils.vpn_ping(vpn_ip, vpn_port): - rv['state'] = 'running' - else: - rv['state'] = 'down' - else: - rv['state'] = 'invalid' - return rv - - @extensions.expected_errors((400, 403)) - @validation.schema(cloudpipe.create) + @extensions.expected_errors((410)) def create(self, req, body): """Create a new cloudpipe instance, if none exists. Parameters: {cloudpipe: {'project_id': ''}} """ + raise exc.HTTPGone() - context = req.environ['nova.context'] - authorize(context) - params = body.get('cloudpipe', {}) - project_id = params.get('project_id', context.project_id) - # NOTE(vish): downgrade to project context. Note that we keep - # the same token so we can still talk to glance - context.project_id = project_id - context.user_id = 'project-vpn' - context.is_admin = False - context.roles = [] - instance = self._get_cloudpipe_for_project(context) - if not instance: - try: - result = self.cloudpipe.launch_vpn_instance(context) - instance = result[0][0] - except exception.NoMoreNetworks: - msg = _("Unable to claim IP for VPN instances, ensure it " - "isn't running, and try again in a few minutes") - raise exc.HTTPBadRequest(explanation=msg) - return {'instance_id': instance.uuid} - - @extensions.expected_errors((400, 403, 404)) + @extensions.expected_errors((410)) def index(self, req): """List running cloudpipe instances.""" - context = req.environ['nova.context'] - authorize(context) - vpns = [self._vpn_dict(context, x['project_id'], x) - for x in self._get_all_cloudpipes(context)] - return {'cloudpipes': vpns} + raise exc.HTTPGone() - @wsgi.response(202) - @extensions.expected_errors(400) - @validation.schema(cloudpipe.update) + @extensions.expected_errors(410) def update(self, req, id, body): """Configure cloudpipe parameters for the project.""" - - context = req.environ['nova.context'] - authorize(context) - - if id != "configure-project": - msg = _("Unknown action %s") % id - raise exc.HTTPBadRequest(explanation=msg) - - project_id = context.project_id - networks = objects.NetworkList.get_by_project(context, project_id) - - params = body['configure_project'] - vpn_ip = params['vpn_ip'] - vpn_port = params['vpn_port'] - for nw in networks: - nw.vpn_public_address = vpn_ip - nw.vpn_public_port = vpn_port - nw.save() - - -class Cloudpipe(extensions.V21APIExtensionBase): - """Adds actions to create cloudpipe instances. - - When running with the Vlan network mode, you need a mechanism to route - from the public Internet to your vlans. This mechanism is known as a - cloudpipe. - - At the time of creating this class, only OpenVPN is supported. Support for - a SSH Bastion host is forthcoming. - """ - - name = "Cloudpipe" - alias = ALIAS - version = 1 - - def get_resources(self): - resource = [extensions.ResourceExtension(ALIAS, - CloudpipeController())] - return resource - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] + raise exc.HTTPGone() diff --git a/nova/api/openstack/compute/config_drive.py b/nova/api/openstack/compute/config_drive.py index f4aa4b542cd..af4f0593e2c 100644 --- a/nova/api/openstack/compute/config_drive.py +++ b/nova/api/openstack/compute/config_drive.py @@ -17,12 +17,10 @@ from nova.api.openstack.compute.schemas import config_drive as \ schema_config_drive -from nova.api.openstack import extensions from nova.api.openstack import wsgi +from nova.policies import config_drive as cd_policies -ALIAS = "os-config-drive" ATTRIBUTE_NAME = "config_drive" -authorize = extensions.os_compute_soft_authorizer(ALIAS) class ConfigDriveController(wsgi.Controller): @@ -42,36 +40,23 @@ def _show(self, req, resp_obj): @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['nova.context'] - if authorize(context): + if context.can(cd_policies.BASE_POLICY_NAME, fatal=False): self._show(req, resp_obj) @wsgi.extends def detail(self, req, resp_obj): context = req.environ['nova.context'] - if 'servers' in resp_obj.obj and authorize(context): + if 'servers' in resp_obj.obj and context.can( + cd_policies.BASE_POLICY_NAME, fatal=False): servers = resp_obj.obj['servers'] self._add_config_drive(req, servers) -class ConfigDrive(extensions.V21APIExtensionBase): - """Config Drive Extension.""" +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + create_kwargs['config_drive'] = server_dict.get(ATTRIBUTE_NAME) - name = "ConfigDrive" - alias = ALIAS - version = 1 - def get_controller_extensions(self): - controller = ConfigDriveController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] - - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - create_kwargs['config_drive'] = server_dict.get(ATTRIBUTE_NAME) - - def get_server_create_schema(self, version): - return schema_config_drive.server_create +def get_server_create_schema(version): + return schema_config_drive.server_create diff --git a/nova/api/openstack/compute/console_auth_tokens.py b/nova/api/openstack/compute/console_auth_tokens.py index f7e4b514bb1..ff0a2dda307 100644 --- a/nova/api/openstack/compute/console_auth_tokens.py +++ b/nova/api/openstack/compute/console_auth_tokens.py @@ -19,9 +19,7 @@ from nova.api.openstack import wsgi from nova.consoleauth import rpcapi as consoleauth_rpcapi from nova.i18n import _ - -ALIAS = "os-console-auth-tokens" -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import console_auth_tokens as cat_policies class ConsoleAuthTokensController(wsgi.Controller): @@ -29,11 +27,10 @@ def __init__(self, *args, **kwargs): self._consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI() super(ConsoleAuthTokensController, self).__init__(*args, **kwargs) - @extensions.expected_errors((400, 401, 404)) - def show(self, req, id): + def _show(self, req, id, rdp_only): """Checks a console auth token and returns the related connect info.""" context = req.environ['nova.context'] - authorize(context) + context.can(cat_policies.BASE_POLICY_NAME) token = id if not token: @@ -45,8 +42,8 @@ def show(self, req, id): raise webob.exc.HTTPNotFound(explanation=_("Token not found")) console_type = connect_info.get('console_type') - # This is currently required only for RDP consoles - if console_type != "rdp-html5": + + if rdp_only and console_type != "rdp-html5": raise webob.exc.HTTPUnauthorized( explanation=_("The requested console type details are not " "accessible")) @@ -57,18 +54,12 @@ def show(self, req, id): 'internal_access_path'] if i in connect_info}} + @wsgi.Controller.api_version("2.1", "2.30") + @extensions.expected_errors((400, 401, 404)) + def show(self, req, id): + return self._show(req, id, True) -class ConsoleAuthTokens(extensions.V21APIExtensionBase): - """Console token authentication support.""" - name = "ConsoleAuthTokens" - alias = ALIAS - version = 1 - - def get_resources(self): - controller = ConsoleAuthTokensController() - ext = extensions.ResourceExtension(ALIAS, - controller) - return [ext] - - def get_controller_extensions(self): - return [] + @wsgi.Controller.api_version("2.31") # noqa + @extensions.expected_errors((400, 404)) + def show(self, req, id): + return self._show(req, id, False) diff --git a/nova/api/openstack/compute/console_output.py b/nova/api/openstack/compute/console_output.py index 9dd17acd9a0..06129f935c2 100644 --- a/nova/api/openstack/compute/console_output.py +++ b/nova/api/openstack/compute/console_output.py @@ -25,15 +25,13 @@ from nova.api import validation from nova import compute from nova import exception - -ALIAS = "os-console-output" -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import console_output as co_policies class ConsoleOutputController(wsgi.Controller): def __init__(self, *args, **kwargs): super(ConsoleOutputController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() @extensions.expected_errors((404, 409, 501)) @wsgi.action('os-getConsoleOutput') @@ -41,7 +39,7 @@ def __init__(self, *args, **kwargs): def get_console_output(self, req, id, body): """Get text console output.""" context = req.environ['nova.context'] - authorize(context) + context.can(co_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, id) length = body['os-getConsoleOutput'].get('length') @@ -71,19 +69,3 @@ def get_console_output(self, req, id, body): output = remove_re.sub('', output) return {'output': output} - - -class ConsoleOutput(extensions.V21APIExtensionBase): - """Console log output support, with tailing ability.""" - - name = "ConsoleOutput" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = ConsoleOutputController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/consoles.py b/nova/api/openstack/compute/consoles.py index a79d886c078..0c59278d3ce 100644 --- a/nova/api/openstack/compute/consoles.py +++ b/nova/api/openstack/compute/consoles.py @@ -19,10 +19,7 @@ from nova.api.openstack import wsgi from nova.console import api as console_api from nova import exception - - -ALIAS = 'os-consoles' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import consoles as consoles_policies def _translate_keys(cons): @@ -55,7 +52,7 @@ def __init__(self): def index(self, req, server_id): """Returns a list of consoles for this instance.""" context = req.environ['nova.context'] - authorize(context, action='index') + context.can(consoles_policies.POLICY_ROOT % 'index') consoles = self.console_api.get_consoles( req.environ['nova.context'], server_id) @@ -69,7 +66,7 @@ def index(self, req, server_id): def create(self, req, server_id, body): """Creates a new console.""" context = req.environ['nova.context'] - authorize(context, action='create') + context.can(consoles_policies.POLICY_ROOT % 'create') try: self.console_api.create_console( @@ -81,7 +78,7 @@ def create(self, req, server_id, body): def show(self, req, server_id, id): """Shows in-depth information on a specific console.""" context = req.environ['nova.context'] - authorize(context, action='show') + context.can(consoles_policies.POLICY_ROOT % 'show') try: console = self.console_api.get_console( @@ -97,7 +94,7 @@ def show(self, req, server_id, id): def delete(self, req, server_id, id): """Deletes a console.""" context = req.environ['nova.context'] - authorize(context, action='delete') + context.can(consoles_policies.POLICY_ROOT % 'delete') try: self.console_api.delete_console(req.environ['nova.context'], @@ -105,24 +102,3 @@ def delete(self, req, server_id, id): int(id)) except exception.ConsoleNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) - - -class Consoles(extensions.V21APIExtensionBase): - """Consoles.""" - - name = "Consoles" - alias = ALIAS - version = 1 - - def get_resources(self): - parent = {'member_name': 'server', - 'collection_name': 'servers'} - resources = [ - extensions.ResourceExtension( - 'consoles', ConsolesController(), parent=parent, - member_name='console')] - - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/create_backup.py b/nova/api/openstack/compute/create_backup.py index b78f2a584cd..4f59dd3e9b3 100644 --- a/nova/api/openstack/compute/create_backup.py +++ b/nova/api/openstack/compute/create_backup.py @@ -15,6 +15,7 @@ import webob +from nova.api.openstack import api_version_request from nova.api.openstack import common from nova.api.openstack.compute.schemas import create_backup from nova.api.openstack import extensions @@ -22,16 +23,15 @@ from nova.api import validation from nova import compute from nova import exception - -ALIAS = "os-create-backup" -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import create_backup as cb_policies class CreateBackupController(wsgi.Controller): def __init__(self, *args, **kwargs): super(CreateBackupController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() + @wsgi.response(202) @extensions.expected_errors((400, 403, 404, 409)) @wsgi.action('createBackup') @validation.schema(create_backup.create_backup_v20, '2.0', '2.0') @@ -48,7 +48,7 @@ def _create_backup(self, req, id, body): """ context = req.environ["nova.context"] - authorize(context) + context.can(cb_policies.BASE_POLICY_NAME) entity = body["createBackup"] image_name = common.normalize_name(entity["name"]) @@ -57,7 +57,11 @@ def _create_backup(self, req, id, body): props = {} metadata = entity.get('metadata', {}) - common.check_img_metadata_properties_quota(context, metadata) + # Starting from microversion 2.39 we don't check quotas on createBackup + if api_version_request.is_supported( + req, max_version= + api_version_request.MAX_IMAGE_META_PROXY_API_VERSION): + common.check_img_metadata_properties_quota(context, metadata) props.update(metadata) instance = common.get_instance(self.compute_api, context, id) @@ -73,6 +77,11 @@ def _create_backup(self, req, id, body): except exception.InvalidRequest as e: raise webob.exc.HTTPBadRequest(explanation=e.format_message()) + # Starting with microversion 2.45 we return a response body containing + # the snapshot image id without the Location header. + if api_version_request.is_supported(req, '2.45'): + return {'image_id': image['id']} + resp = webob.Response(status_int=202) # build location of newly-created image entity if rotation is not zero @@ -83,19 +92,3 @@ def _create_backup(self, req, id, body): resp.headers['Location'] = image_ref return resp - - -class CreateBackup(extensions.V21APIExtensionBase): - """Create a backup of a server.""" - - name = "CreateBackup" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = CreateBackupController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/deferred_delete.py b/nova/api/openstack/compute/deferred_delete.py index 13b5f5954f5..cf14e9b02ac 100644 --- a/nova/api/openstack/compute/deferred_delete.py +++ b/nova/api/openstack/compute/deferred_delete.py @@ -22,23 +22,21 @@ from nova.api.openstack import wsgi from nova import compute from nova import exception - -ALIAS = 'os-deferred-delete' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import deferred_delete as dd_policies class DeferredDeleteController(wsgi.Controller): def __init__(self, *args, **kwargs): super(DeferredDeleteController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() @wsgi.response(202) - @extensions.expected_errors((404, 409, 403)) + @extensions.expected_errors((403, 404, 409)) @wsgi.action('restore') def _restore(self, req, id, body): """Restore a previously deleted instance.""" context = req.environ["nova.context"] - authorize(context) + context.can(dd_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, id) try: self.compute_api.restore(context, instance) @@ -56,25 +54,14 @@ def _restore(self, req, id, body): def _force_delete(self, req, id, body): """Force delete of instance before deferred cleanup.""" context = req.environ["nova.context"] - authorize(context) instance = common.get_instance(self.compute_api, context, id) + context.can(dd_policies.BASE_POLICY_NAME, + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) try: self.compute_api.force_delete(context, instance) + except (exception.InstanceNotFound, + exception.InstanceUnknownCell) as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise webob.exc.HTTPConflict(explanation=e.format_message()) - - -class DeferredDelete(extensions.V21APIExtensionBase): - """Instance deferred delete.""" - - name = "DeferredDelete" - alias = "os-deferred-delete" - version = 1 - - def get_controller_extensions(self): - controller = DeferredDeleteController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/disk_config.py b/nova/api/openstack/compute/disk_config.py deleted file mode 100644 index 171a0952af0..00000000000 --- a/nova/api/openstack/compute/disk_config.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# 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. - -"""Disk Config extension.""" - -from oslo_utils import strutils -from webob import exc - -from nova.api.openstack.compute.schemas import disk_config -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova.i18n import _ - -ALIAS = 'os-disk-config' -API_DISK_CONFIG = "OS-DCF:diskConfig" -INTERNAL_DISK_CONFIG = "auto_disk_config" -authorize = extensions.os_compute_soft_authorizer(ALIAS) - - -def disk_config_to_api(value): - return 'AUTO' if value else 'MANUAL' - - -def disk_config_from_api(value): - if value == 'AUTO': - return True - elif value == 'MANUAL': - return False - else: - msg = _("%s must be either 'MANUAL' or 'AUTO'.") % API_DISK_CONFIG - raise exc.HTTPBadRequest(explanation=msg) - - -class ImageDiskConfigController(wsgi.Controller): - def _add_disk_config(self, context, images): - for image in images: - metadata = image['metadata'] - if INTERNAL_DISK_CONFIG in metadata: - raw_value = metadata[INTERNAL_DISK_CONFIG] - value = strutils.bool_from_string(raw_value) - image[API_DISK_CONFIG] = disk_config_to_api(value) - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if 'image' in resp_obj.obj and authorize(context): - image = resp_obj.obj['image'] - self._add_disk_config(context, [image]) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if 'images' in resp_obj.obj and authorize(context): - images = resp_obj.obj['images'] - self._add_disk_config(context, images) - - -class ServerDiskConfigController(wsgi.Controller): - def _add_disk_config(self, req, servers): - for server in servers: - db_server = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show'/'detail' methods. - value = db_server.get(INTERNAL_DISK_CONFIG) - server[API_DISK_CONFIG] = disk_config_to_api(value) - - def _show(self, req, resp_obj): - if 'server' in resp_obj.obj: - server = resp_obj.obj['server'] - self._add_disk_config(req, [server]) - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - self._show(req, resp_obj) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if 'servers' in resp_obj.obj and authorize(context): - servers = resp_obj.obj['servers'] - self._add_disk_config(req, servers) - - @wsgi.extends - def create(self, req, resp_obj, body): - context = req.environ['nova.context'] - if authorize(context): - self._show(req, resp_obj) - - @wsgi.extends - def update(self, req, resp_obj, id, body): - context = req.environ['nova.context'] - if authorize(context): - self._show(req, resp_obj) - - @wsgi.extends(action='rebuild') - def _action_rebuild(self, req, resp_obj, id, body): - context = req.environ['nova.context'] - if authorize(context): - self._show(req, resp_obj) - - -class DiskConfig(extensions.V21APIExtensionBase): - """Disk Management Extension.""" - - name = "DiskConfig" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - servers_extension = extensions.ControllerExtension( - self, 'servers', ServerDiskConfigController()) - - images_extension = extensions.ControllerExtension( - self, 'images', ImageDiskConfigController()) - - return [servers_extension, images_extension] - - def get_resources(self): - return [] - - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - # making 'body_deprecated_param' as optional to avoid changes for - # server_update & server_rebuild - def server_create(self, server_dict, create_kwargs, - body_deprecated_param=None): - if API_DISK_CONFIG in server_dict: - api_value = server_dict[API_DISK_CONFIG] - internal_value = disk_config_from_api(api_value) - create_kwargs[INTERNAL_DISK_CONFIG] = internal_value - - server_update = server_create - server_rebuild = server_create - server_resize = server_create - - def get_server_create_schema(self, version): - return disk_config.server_create - - get_server_update_schema = get_server_create_schema - get_server_rebuild_schema = get_server_create_schema - get_server_resize_schema = get_server_create_schema diff --git a/nova/api/openstack/compute/evacuate.py b/nova/api/openstack/compute/evacuate.py index 322032e40e1..e2f7a3c6797 100644 --- a/nova/api/openstack/compute/evacuate.py +++ b/nova/api/openstack/compute/evacuate.py @@ -13,7 +13,6 @@ # under the License. -from oslo_config import cfg from oslo_utils import strutils from webob import exc @@ -24,22 +23,19 @@ from nova.api.openstack import wsgi from nova.api import validation from nova import compute +import nova.conf from nova import exception from nova.i18n import _ +from nova.policies import evacuate as evac_policies from nova import utils -CONF = cfg.CONF -CONF.import_opt('enable_instance_password', - 'nova.api.openstack.compute.legacy_v2.servers') - -ALIAS = "os-evacuate" -authorize = extensions.os_compute_authorizer(ALIAS) +CONF = nova.conf.CONF class EvacuateController(wsgi.Controller): def __init__(self, *args, **kwargs): super(EvacuateController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() self.host_api = compute.HostAPI() def _get_on_shared_storage(self, req, evacuate_body): @@ -76,20 +72,31 @@ def _get_password_v214(self, req, evacuate_body): # backwards compatibility reasons. @extensions.expected_errors((400, 404, 409)) @wsgi.action('evacuate') - @validation.schema(evacuate.evacuate, "2.1", "2.12") - @validation.schema(evacuate.evacuate_v214, "2.14") + @validation.schema(evacuate.evacuate, "2.0", "2.13") + @validation.schema(evacuate.evacuate_v214, "2.14", "2.28") + @validation.schema(evacuate.evacuate_v2_29, "2.29") def _evacuate(self, req, id, body): """Permit admins to evacuate a server from a failed host to a new one. """ context = req.environ["nova.context"] - authorize(context) + instance = common.get_instance(self.compute_api, context, id) + context.can(evac_policies.BASE_POLICY_NAME, + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) evacuate_body = body["evacuate"] host = evacuate_body.get("host") + force = None on_shared_storage = self._get_on_shared_storage(req, evacuate_body) + if api_version_request.is_supported(req, min_version='2.29'): + force = body["evacuate"].get("force", False) + force = strutils.bool_from_string(force, strict=True) + if force is True and not host: + message = _("Can't force to a non-provided destination") + raise exc.HTTPBadRequest(explanation=message) if api_version_request.is_supported(req, min_version='2.14'): password = self._get_password_v214(req, evacuate_body) else: @@ -99,18 +106,18 @@ def _evacuate(self, req, id, body): if host is not None: try: self.host_api.service_get_by_compute_host(context, host) - except exception.ComputeHostNotFound: + except (exception.ComputeHostNotFound, + exception.HostMappingNotFound): msg = _("Compute host %s not found.") % host raise exc.HTTPNotFound(explanation=msg) - instance = common.get_instance(self.compute_api, context, id) if instance.host == host: msg = _("The target host can't be the same one.") raise exc.HTTPBadRequest(explanation=msg) try: self.compute_api.evacuate(context, instance, host, - on_shared_storage, password) + on_shared_storage, password, force) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: @@ -120,23 +127,7 @@ def _evacuate(self, req, id, body): raise exc.HTTPBadRequest(explanation=e.format_message()) if (not api_version_request.is_supported(req, min_version='2.14') and - CONF.enable_instance_password): + CONF.api.enable_instance_password): return {'adminPass': password} else: return None - - -class Evacuate(extensions.V21APIExtensionBase): - """Enables server evacuation.""" - - name = "Evacuate" - alias = ALIAS - version = 1 - - def get_resources(self): - return [] - - def get_controller_extensions(self): - controller = EvacuateController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/extended_availability_zone.py b/nova/api/openstack/compute/extended_availability_zone.py index f4337ac3fbc..ac562c588a0 100644 --- a/nova/api/openstack/compute/extended_availability_zone.py +++ b/nova/api/openstack/compute/extended_availability_zone.py @@ -15,12 +15,10 @@ """The Extended Availability Zone Status API extension.""" -from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import availability_zones as avail_zone +from nova.policies import extended_availability_zone as eaz_policies -ALIAS = "os-extended-availability-zone" -authorize = extensions.os_compute_soft_authorizer(ALIAS) PREFIX = "OS-EXT-AZ" @@ -36,7 +34,7 @@ def _extend_server(self, context, server, instance): @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['nova.context'] - if authorize(context): + if context.can(eaz_policies.BASE_POLICY_NAME, fatal=False): server = resp_obj.obj['server'] db_instance = req.get_db_instance(server['id']) self._extend_server(context, server, db_instance) @@ -44,24 +42,8 @@ def show(self, req, resp_obj, id): @wsgi.extends def detail(self, req, resp_obj): context = req.environ['nova.context'] - if authorize(context): + if context.can(eaz_policies.BASE_POLICY_NAME, fatal=False): servers = list(resp_obj.obj['servers']) for server in servers: db_instance = req.get_db_instance(server['id']) self._extend_server(context, server, db_instance) - - -class ExtendedAvailabilityZone(extensions.V21APIExtensionBase): - """Extended Availability Zone support.""" - - name = "ExtendedAvailabilityZone" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = ExtendedAZController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/extended_server_attributes.py b/nova/api/openstack/compute/extended_server_attributes.py index 66061f2b9f3..6f2c7f92d7c 100644 --- a/nova/api/openstack/compute/extended_server_attributes.py +++ b/nova/api/openstack/compute/extended_server_attributes.py @@ -15,20 +15,17 @@ """The Extended Server Attributes API extension.""" from nova.api.openstack import api_version_request -from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute - -ALIAS = "os-extended-server-attributes" -authorize = extensions.os_compute_soft_authorizer(ALIAS) -soft_authorize = extensions.os_compute_soft_authorizer('servers') +from nova.policies import extended_server_attributes as esa_policies +from nova.policies import servers as servers_policies class ExtendedServerAttributesController(wsgi.Controller): def __init__(self, *args, **kwargs): super(ExtendedServerAttributesController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() def _extend_server(self, context, server, instance, req): key = "OS-EXT-SRV-ATTR:hypervisor_hostname" @@ -39,7 +36,7 @@ def _extend_server(self, context, server, instance, req): # NOTE(mriedem): These will use the OS-EXT-SRV-ATTR prefix below # and that's OK for microversion 2.3 which is being compatible # with v2.0 for the ec2 API split out from Nova. After this, - # however, new microversoins should not be using the + # however, new microversions should not be using the # OS-EXT-SRV-ATTR prefix. properties += ['reservation_id', 'launch_index', 'hostname', 'kernel_id', 'ramdisk_id', @@ -62,10 +59,11 @@ def show(self, req, resp_obj, id): context = req.environ['nova.context'] authorize_extend = False authorize_host_status = False - if authorize(context): + if context.can(esa_policies.BASE_POLICY_NAME, fatal=False): authorize_extend = True if (api_version_request.is_supported(req, min_version='2.16') and - soft_authorize(context, action='show:host_status')): + context.can(servers_policies.SERVERS % 'show:host_status', + fatal=False)): authorize_host_status = True if authorize_extend or authorize_host_status: server = resp_obj.obj['server'] @@ -82,38 +80,26 @@ def detail(self, req, resp_obj): context = req.environ['nova.context'] authorize_extend = False authorize_host_status = False - if authorize(context): + if context.can(esa_policies.BASE_POLICY_NAME, fatal=False): authorize_extend = True if (api_version_request.is_supported(req, min_version='2.16') and - soft_authorize(context, action='show:host_status')): + context.can(servers_policies.SERVERS % 'show:host_status', + fatal=False)): authorize_host_status = True if authorize_extend or authorize_host_status: servers = list(resp_obj.obj['servers']) - instances = req.get_db_instances() - # Instances is guaranteed to be in the cache due to - # the core API adding it in its 'detail' method. - if authorize_host_status: - host_statuses = self.compute_api.get_instances_host_statuses( - instances.values()) - for server in servers: - if authorize_extend: - instance = instances[server['id']] - self._extend_server(context, server, instance, req) + # NOTE(dinesh-bhor): Skipping fetching of instances from cache as + # servers list can be empty if invalid status is provided to the + # core API 'detail' method. + if servers: + instances = req.get_db_instances() if authorize_host_status: - server['host_status'] = host_statuses[server['id']] - - -class ExtendedServerAttributes(extensions.V21APIExtensionBase): - """Extended Server Attributes support.""" - - name = "ExtendedServerAttributes" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = ExtendedServerAttributesController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] + host_statuses = ( + self.compute_api.get_instances_host_statuses( + instances.values())) + for server in servers: + if authorize_extend: + instance = instances[server['id']] + self._extend_server(context, server, instance, req) + if authorize_host_status: + server['host_status'] = host_statuses[server['id']] diff --git a/nova/api/openstack/compute/extended_status.py b/nova/api/openstack/compute/extended_status.py index 13ec50f944e..e1bb52b2890 100644 --- a/nova/api/openstack/compute/extended_status.py +++ b/nova/api/openstack/compute/extended_status.py @@ -14,17 +14,11 @@ """The Extended Status Admin API extension.""" -from nova.api.openstack import extensions from nova.api.openstack import wsgi - -ALIAS = "os-extended-status" -authorize = extensions.os_compute_soft_authorizer(ALIAS) +from nova.policies import extended_status as es_policies class ExtendedStatusController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ExtendedStatusController, self).__init__(*args, **kwargs) - def _extend_server(self, server, instance): # Note(gmann): Removed 'locked_by' from extended status # to make it same as V2. If needed it can be added with @@ -39,7 +33,7 @@ def _extend_server(self, server, instance): @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['nova.context'] - if authorize(context): + if context.can(es_policies.BASE_POLICY_NAME, fatal=False): server = resp_obj.obj['server'] db_instance = req.get_db_instance(server['id']) # server['id'] is guaranteed to be in the cache due to @@ -49,26 +43,10 @@ def show(self, req, resp_obj, id): @wsgi.extends def detail(self, req, resp_obj): context = req.environ['nova.context'] - if authorize(context): + if context.can(es_policies.BASE_POLICY_NAME, fatal=False): servers = list(resp_obj.obj['servers']) for server in servers: db_instance = req.get_db_instance(server['id']) # server['id'] is guaranteed to be in the cache due to # the core API adding it in its 'detail' method. self._extend_server(server, db_instance) - - -class ExtendedStatus(extensions.V21APIExtensionBase): - """Extended Status support.""" - - name = "ExtendedStatus" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = ExtendedStatusController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/extended_volumes.py b/nova/api/openstack/compute/extended_volumes.py index 49037795d90..13416b39dad 100644 --- a/nova/api/openstack/compute/extended_volumes.py +++ b/nova/api/openstack/compute/extended_volumes.py @@ -14,18 +14,13 @@ """The Extended Volumes API extension.""" from nova.api.openstack import api_version_request -from nova.api.openstack import extensions from nova.api.openstack import wsgi +from nova import context from nova import objects - -ALIAS = "os-extended-volumes" -soft_authorize = extensions.os_compute_soft_authorizer(ALIAS) +from nova.policies import extended_volumes as ev_policies class ExtendedVolumesController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ExtendedVolumesController, self).__init__(*args, **kwargs) - def _extend_server(self, context, server, req, bdms): volumes_attached = [] for bdm in bdms: @@ -38,27 +33,47 @@ def _extend_server(self, context, server, req, bdms): # NOTE(mriedem): The os-extended-volumes prefix should not be used for # new attributes after v2.1. They are only in v2.1 for backward compat # with v2.0. - key = "%s:volumes_attached" % ExtendedVolumes.alias + key = "os-extended-volumes:volumes_attached" server[key] = volumes_attached @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['nova.context'] - if soft_authorize(context): + if context.can(ev_policies.BASE_POLICY_NAME, fatal=False): server = resp_obj.obj['server'] bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid( context, [server['id']]) instance_bdms = self._get_instance_bdms(bdms, server) self._extend_server(context, server, req, instance_bdms) + @staticmethod + def _get_instance_bdms_in_multiple_cells(ctxt, servers): + instance_uuids = [server['id'] for server in servers] + inst_maps = objects.InstanceMappingList.get_by_instance_uuids( + ctxt, instance_uuids) + + cell_mappings = {} + for inst_map in inst_maps: + if (inst_map.cell_mapping is not None and + inst_map.cell_mapping.uuid not in cell_mappings): + cell_mappings.update( + {inst_map.cell_mapping.uuid: inst_map.cell_mapping}) + + bdms = {} + for cell_mapping in cell_mappings.values(): + with context.target_cell(ctxt, cell_mapping) as cctxt: + bdms.update( + objects.BlockDeviceMappingList.bdms_by_instance_uuid( + cctxt, instance_uuids)) + + return bdms + @wsgi.extends def detail(self, req, resp_obj): context = req.environ['nova.context'] - if soft_authorize(context): + if context.can(ev_policies.BASE_POLICY_NAME, fatal=False): servers = list(resp_obj.obj['servers']) - instance_uuids = [server['id'] for server in servers] - bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid( - context, instance_uuids) + bdms = self._get_instance_bdms_in_multiple_cells(context, servers) for server in servers: instance_bdms = self._get_instance_bdms(bdms, server) self._extend_server(context, server, req, instance_bdms) @@ -69,19 +84,3 @@ def _get_instance_bdms(self, bdms, server): # If that instance has since been deleted, it won't be in the # 'bdms' dictionary though, so use 'get' to avoid KeyErrors. return bdms.get(server['id'], []) - - -class ExtendedVolumes(extensions.V21APIExtensionBase): - """Extended Volumes support.""" - - name = "ExtendedVolumes" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = ExtendedVolumesController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/extension_info.py b/nova/api/openstack/compute/extension_info.py index 45e8195ea84..d3eb9be9485 100644 --- a/nova/api/openstack/compute/extension_info.py +++ b/nova/api/openstack/compute/extension_info.py @@ -12,220 +12,887 @@ # License for the specific language governing permissions and limitations # under the License. -import copy - from oslo_log import log as logging -import six + import webob.exc from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import exception -from nova.i18n import _LE +from nova.policies import extensions as ext_policies -ALIAS = 'extensions' LOG = logging.getLogger(__name__) -authorize = extensions.os_compute_authorizer(ALIAS) - -# NOTE(cyeoh): The following mappings are currently incomplete -# Having a v2.1 extension loaded can imply that several v2 extensions -# should also appear to be loaded (although they no longer do in v2.1) -v21_to_v2_extension_list_mapping = { - 'os-quota-sets': [{'name': 'UserQuotas', 'alias': 'os-user-quotas'}, - {'name': 'ExtendedQuotas', - 'alias': 'os-extended-quotas'}], - 'os-cells': [{'name': 'CellCapacities', 'alias': 'os-cell-capacities'}], - 'os-baremetal-nodes': [{'name': 'BareMetalExtStatus', - 'alias': 'os-baremetal-ext-status'}], - 'os-block-device-mapping': [{'name': 'BlockDeviceMappingV2Boot', - 'alias': 'os-block-device-mapping-v2-boot'}], - 'os-cloudpipe': [{'name': 'CloudpipeUpdate', - 'alias': 'os-cloudpipe-update'}], - 'servers': [{'name': 'Createserverext', 'alias': 'os-create-server-ext'}, - {'name': 'ExtendedIpsMac', 'alias': 'OS-EXT-IPS-MAC'}, - {'name': 'ExtendedIps', 'alias': 'OS-EXT-IPS'}, - {'name': 'ServerListMultiStatus', - 'alias': 'os-server-list-multi-status'}, - {'name': 'ServerSortKeys', 'alias': 'os-server-sort-keys'}, - {'name': 'ServerStartStop', 'alias': 'os-server-start-stop'}], - 'flavors': [{'name': 'FlavorDisabled', 'alias': 'OS-FLV-DISABLED'}, - {'name': 'FlavorExtraData', 'alias': 'OS-FLV-EXT-DATA'}, - {'name': 'FlavorSwap', 'alias': 'os-flavor-swap'}], - 'os-services': [{'name': 'ExtendedServicesDelete', - 'alias': 'os-extended-services-delete'}, - {'name': 'ExtendedServices', 'alias': - 'os-extended-services'}], - 'os-evacuate': [{'name': 'ExtendedEvacuateFindHost', - 'alias': 'os-extended-evacuate-find-host'}], - 'os-floating-ips': [{'name': 'ExtendedFloatingIps', - 'alias': 'os-extended-floating-ips'}], - 'os-hypervisors': [{'name': 'ExtendedHypervisors', - 'alias': 'os-extended-hypervisors'}, - {'name': 'HypervisorStatus', - 'alias': 'os-hypervisor-status'}], - 'os-networks': [{'name': 'ExtendedNetworks', - 'alias': 'os-extended-networks'}], - 'os-rescue': [{'name': 'ExtendedRescueWithImage', - 'alias': 'os-extended-rescue-with-image'}], - 'os-extended-status': [{'name': 'ExtendedStatus', - 'alias': 'OS-EXT-STS'}], - 'os-used-limits': [{'name': 'UsedLimitsForAdmin', - 'alias': 'os-used-limits-for-admin'}], - 'os-volumes': [{'name': 'VolumeAttachmentUpdate', - 'alias': 'os-volume-attachment-update'}], - 'os-server-groups': [{'name': 'ServerGroupQuotas', - 'alias': 'os-server-group-quotas'}], -} - -# v2.1 plugins which should never appear in the v2 extension list -# This should be the v2.1 alias, not the V2.0 alias -v2_extension_suppress_list = ['servers', 'images', 'versions', 'flavors', - 'os-block-device-mapping-v1', 'os-consoles', - 'extensions', 'image-metadata', 'ips', 'limits', - 'server-metadata', 'server-migrations' - ] -# v2.1 plugins which should appear under a different name in v2 -v21_to_v2_alias_mapping = { - 'image-size': 'OS-EXT-IMG-SIZE', - 'os-remote-consoles': 'os-consoles', - 'os-disk-config': 'OS-DCF', - 'os-extended-availability-zone': 'OS-EXT-AZ', - 'os-extended-server-attributes': 'OS-EXT-SRV-ATTR', - 'os-multinic': 'NMN', - 'os-scheduler-hints': 'OS-SCH-HNT', - 'os-server-usage': 'OS-SRV-USG', - 'os-instance-usage-audit-log': 'os-instance_usage_audit_log', -} -# V2.1 does not support XML but we need to keep an entry in the -# /extensions information returned to the user for backwards -# compatibility -FAKE_XML_URL = "http://docs.openstack.org/compute/ext/fake_xml" -FAKE_UPDATED_DATE = "2014-12-03T00:00:00Z" - - -class FakeExtension(object): - def __init__(self, name, alias): - self.name = name - self.alias = alias - self.__doc__ = "" - self.version = -1 +EXTENSION_LIST = [ + { + "alias": "NMN", + "description": "Multiple network support.", + "links": [], + "name": "Multinic", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-DCF", + "description": "Disk Management Extension.", + "links": [], + "name": "DiskConfig", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-EXT-AZ", + "description": "Extended Availability Zone support.", + "links": [], + "name": "ExtendedAvailabilityZone", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-EXT-IMG-SIZE", + "description": "Adds image size to image listings.", + "links": [], + "name": "ImageSize", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-EXT-IPS", + "description": "Adds type parameter to the ip list.", + "links": [], + "name": "ExtendedIps", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-EXT-IPS-MAC", + "description": "Adds mac address parameter to the ip list.", + "links": [], + "name": "ExtendedIpsMac", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-EXT-SRV-ATTR", + "description": "Extended Server Attributes support.", + "links": [], + "name": "ExtendedServerAttributes", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-EXT-STS", + "description": "Extended Status support.", + "links": [], + "name": "ExtendedStatus", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-FLV-DISABLED", + "description": "Support to show the disabled status of a flavor.", + "links": [], + "name": "FlavorDisabled", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-FLV-EXT-DATA", + "description": "Provide additional data for flavors.", + "links": [], + "name": "FlavorExtraData", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-SCH-HNT", + "description": "Pass arbitrary key/value pairs to the scheduler.", + "links": [], + "name": "SchedulerHints", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "OS-SRV-USG", + "description": "Adds launched_at and terminated_at on Servers.", + "links": [], + "name": "ServerUsage", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-access-ips", + "description": "Access IPs support.", + "links": [], + "name": "AccessIPs", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-admin-actions", + "description": "Enable admin-only server actions\n\n " + "Actions include: resetNetwork, injectNetworkInfo, " + "os-resetState\n ", + "links": [], + "name": "AdminActions", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-admin-password", + "description": "Admin password management support.", + "links": [], + "name": "AdminPassword", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-agents", + "description": "Agents support.", + "links": [], + "name": "Agents", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-aggregates", + "description": "Admin-only aggregate administration.", + "links": [], + "name": "Aggregates", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-assisted-volume-snapshots", + "description": "Assisted volume snapshots.", + "links": [], + "name": "AssistedVolumeSnapshots", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-attach-interfaces", + "description": "Attach interface support.", + "links": [], + "name": "AttachInterfaces", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-availability-zone", + "description": "1. Add availability_zone to the Create Server " + "API.\n 2. Add availability zones " + "describing.\n ", + "links": [], + "name": "AvailabilityZone", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-baremetal-ext-status", + "description": "Add extended status in Baremetal Nodes v2 API.", + "links": [], + "name": "BareMetalExtStatus", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-baremetal-nodes", + "description": "Admin-only bare-metal node administration.", + "links": [], + "name": "BareMetalNodes", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-block-device-mapping", + "description": "Block device mapping boot support.", + "links": [], + "name": "BlockDeviceMapping", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-block-device-mapping-v2-boot", + "description": "Allow boot with the new BDM data format.", + "links": [], + "name": "BlockDeviceMappingV2Boot", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-cell-capacities", + "description": "Adding functionality to get cell capacities.", + "links": [], + "name": "CellCapacities", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-cells", + "description": "Enables cells-related functionality such as adding " + "neighbor cells,\n listing neighbor cells, " + "and getting the capabilities of the local cell.\n ", + "links": [], + "name": "Cells", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-certificates", + "description": "Certificates support.", + "links": [], + "name": "Certificates", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-cloudpipe", + "description": "Adds actions to create cloudpipe instances.\n\n " + "When running with the Vlan network mode, you need a " + "mechanism to route\n from the public Internet to " + "your vlans. This mechanism is known as a\n " + "cloudpipe.\n\n At the time of creating this class, " + "only OpenVPN is supported. Support for\n a SSH " + "Bastion host is forthcoming.\n ", + "links": [], + "name": "Cloudpipe", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-cloudpipe-update", + "description": "Adds the ability to set the vpn ip/port for cloudpipe " + "instances.", + "links": [], + "name": "CloudpipeUpdate", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-config-drive", + "description": "Config Drive Extension.", + "links": [], + "name": "ConfigDrive", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-console-auth-tokens", + "description": "Console token authentication support.", + "links": [], + "name": "ConsoleAuthTokens", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-console-output", + "description": "Console log output support, with tailing ability.", + "links": [], + "name": "ConsoleOutput", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-consoles", + "description": "Interactive Console support.", + "links": [], + "name": "Consoles", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-create-backup", + "description": "Create a backup of a server.", + "links": [], + "name": "CreateBackup", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-create-server-ext", + "description": "Extended support to the Create Server v1.1 API.", + "links": [], + "name": "Createserverext", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-deferred-delete", + "description": "Instance deferred delete.", + "links": [], + "name": "DeferredDelete", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-evacuate", + "description": "Enables server evacuation.", + "links": [], + "name": "Evacuate", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-evacuate-find-host", + "description": "Enables server evacuation without target host. " + "Scheduler will select one to target.", + "links": [], + "name": "ExtendedEvacuateFindHost", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-floating-ips", + "description": "Adds optional fixed_address to the add floating IP " + "command.", + "links": [], + "name": "ExtendedFloatingIps", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-hypervisors", + "description": "Extended hypervisors support.", + "links": [], + "name": "ExtendedHypervisors", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-networks", + "description": "Adds additional fields to networks.", + "links": [], + "name": "ExtendedNetworks", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-quotas", + "description": "Adds ability for admins to delete quota and " + "optionally force the update Quota command.", + "links": [], + "name": "ExtendedQuotas", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-rescue-with-image", + "description": "Allow the user to specify the image to use for " + "rescue.", + "links": [], + "name": "ExtendedRescueWithImage", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-services", + "description": "Extended services support.", + "links": [], + "name": "ExtendedServices", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-services-delete", + "description": "Extended services deletion support.", + "links": [], + "name": "ExtendedServicesDelete", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-status", + "description": "Extended Status support.", + "links": [], + "name": "ExtendedStatus", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-extended-volumes", + "description": "Extended Volumes support.", + "links": [], + "name": "ExtendedVolumes", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-fixed-ips", + "description": "Fixed IPs support.", + "links": [], + "name": "FixedIPs", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-flavor-access", + "description": "Flavor access support.", + "links": [], + "name": "FlavorAccess", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-flavor-extra-specs", + "description": "Flavors extra specs support.", + "links": [], + "name": "FlavorExtraSpecs", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-flavor-manage", + "description": "Flavor create/delete API support.", + "links": [], + "name": "FlavorManage", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-flavor-rxtx", + "description": "Support to show the rxtx status of a flavor.", + "links": [], + "name": "FlavorRxtx", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-flavor-swap", + "description": "Support to show the swap status of a flavor.", + "links": [], + "name": "FlavorSwap", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-floating-ip-dns", + "description": "Floating IP DNS support.", + "links": [], + "name": "FloatingIpDns", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-floating-ip-pools", + "description": "Floating IPs support.", + "links": [], + "name": "FloatingIpPools", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-floating-ips", + "description": "Floating IPs support.", + "links": [], + "name": "FloatingIps", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-floating-ips-bulk", + "description": "Bulk handling of Floating IPs.", + "links": [], + "name": "FloatingIpsBulk", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-fping", + "description": "Fping Management Extension.", + "links": [], + "name": "Fping", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-hide-server-addresses", + "description": "Support hiding server addresses in certain states.", + "links": [], + "name": "HideServerAddresses", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-hosts", + "description": "Admin-only host administration.", + "links": [], + "name": "Hosts", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-hypervisor-status", + "description": "Show hypervisor status.", + "links": [], + "name": "HypervisorStatus", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-hypervisors", + "description": "Admin-only hypervisor administration.", + "links": [], + "name": "Hypervisors", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-instance-actions", + "description": "View a log of actions and events taken on an " + "instance.", + "links": [], + "name": "InstanceActions", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-instance_usage_audit_log", + "description": "Admin-only Task Log Monitoring.", + "links": [], + "name": "OSInstanceUsageAuditLog", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-keypairs", + "description": "Keypair Support.", + "links": [], + "name": "Keypairs", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-lock-server", + "description": "Enable lock/unlock server actions.", + "links": [], + "name": "LockServer", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-migrate-server", + "description": "Enable migrate and live-migrate server actions.", + "links": [], + "name": "MigrateServer", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-migrations", + "description": "Provide data on migrations.", + "links": [], + "name": "Migrations", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-multiple-create", + "description": "Allow multiple create in the Create Server v2.1 API.", + "links": [], + "name": "MultipleCreate", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-networks", + "description": "Admin-only Network Management Extension.", + "links": [], + "name": "Networks", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-networks-associate", + "description": "Network association support.", + "links": [], + "name": "NetworkAssociationSupport", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-pause-server", + "description": "Enable pause/unpause server actions.", + "links": [], + "name": "PauseServer", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-personality", + "description": "Personality support.", + "links": [], + "name": "Personality", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-preserve-ephemeral-rebuild", + "description": "Allow preservation of the ephemeral partition on " + "rebuild.", + "links": [], + "name": "PreserveEphemeralOnRebuild", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-quota-class-sets", + "description": "Quota classes management support.", + "links": [], + "name": "QuotaClasses", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-quota-sets", + "description": "Quotas management support.", + "links": [], + "name": "Quotas", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-rescue", + "description": "Instance rescue mode.", + "links": [], + "name": "Rescue", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-security-group-default-rules", + "description": "Default rules for security group support.", + "links": [], + "name": "SecurityGroupDefaultRules", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-security-groups", + "description": "Security group support.", + "links": [], + "name": "SecurityGroups", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-server-diagnostics", + "description": "Allow Admins to view server diagnostics through " + "server action.", + "links": [], + "name": "ServerDiagnostics", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-server-external-events", + "description": "Server External Event Triggers.", + "links": [], + "name": "ServerExternalEvents", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-server-group-quotas", + "description": "Adds quota support to server groups.", + "links": [], + "name": "ServerGroupQuotas", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-server-groups", + "description": "Server group support.", + "links": [], + "name": "ServerGroups", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-server-list-multi-status", + "description": "Allow to filter the servers by a set of status " + "values.", + "links": [], + "name": "ServerListMultiStatus", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-server-password", + "description": "Server password support.", + "links": [], + "name": "ServerPassword", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-server-sort-keys", + "description": "Add sorting support in get Server v2 API.", + "links": [], + "name": "ServerSortKeys", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-server-start-stop", + "description": "Start/Stop instance compute API support.", + "links": [], + "name": "ServerStartStop", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-services", + "description": "Services support.", + "links": [], + "name": "Services", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-shelve", + "description": "Instance shelve mode.", + "links": [], + "name": "Shelve", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-simple-tenant-usage", + "description": "Simple tenant usage extension.", + "links": [], + "name": "SimpleTenantUsage", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-suspend-server", + "description": "Enable suspend/resume server actions.", + "links": [], + "name": "SuspendServer", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-tenant-networks", + "description": "Tenant-based Network Management Extension.", + "links": [], + "name": "OSTenantNetworks", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-used-limits", + "description": "Provide data on limited resources that are being " + "used.", + "links": [], + "name": "UsedLimits", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-used-limits-for-admin", + "description": "Provide data to admin on limited resources used by " + "other tenants.", + "links": [], + "name": "UsedLimitsForAdmin", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-user-data", + "description": "Add user_data to the Create Server API.", + "links": [], + "name": "UserData", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-user-quotas", + "description": "Project user quota support.", + "links": [], + "name": "UserQuotas", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-virtual-interfaces", + "description": "Virtual interface support.", + "links": [], + "name": "VirtualInterfaces", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-volume-attachment-update", + "description": "Support for updating a volume attachment.", + "links": [], + "name": "VolumeAttachmentUpdate", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + }, + { + "alias": "os-volumes", + "description": "Volumes support.", + "links": [], + "name": "Volumes", + "namespace": "http://docs.openstack.org/compute/ext/fake_xml", + "updated": "2014-12-03T00:00:00Z" + } +] + + +EXTENSION_LIST_LEGACY_V2_COMPATIBLE = EXTENSION_LIST[:] +EXTENSION_LIST_LEGACY_V2_COMPATIBLE.append({ + 'alias': 'OS-EXT-VIF-NET', + 'description': 'Adds network id parameter to the virtual interface list.', + 'links': [], + 'name': 'ExtendedVIFNet', + 'namespace': 'http://docs.openstack.org/compute/ext/fake_xml', + "updated": "2014-12-03T00:00:00Z" +}) +EXTENSION_LIST_LEGACY_V2_COMPATIBLE = sorted( + EXTENSION_LIST_LEGACY_V2_COMPATIBLE, key=lambda x: x['alias']) class ExtensionInfoController(wsgi.Controller): - def __init__(self, extension_info): - self.extension_info = extension_info - - def _translate(self, ext): - ext_data = {} - ext_data["name"] = ext.name - ext_data["alias"] = ext.alias - ext_data["description"] = ext.__doc__ - ext_data["namespace"] = FAKE_XML_URL - ext_data["updated"] = FAKE_UPDATED_DATE - ext_data["links"] = [] - return ext_data - - def _create_fake_ext(self, alias, name): - return FakeExtension(alias, name) - - def _add_vif_extension(self, discoverable_extensions): - vif_extension = {} + def _add_vif_extension(self, all_extensions): vif_extension_info = {'name': 'ExtendedVIFNet', - 'alias': 'OS-EXT-VIF-NET'} - vif_extension[vif_extension_info["alias"]] = self._create_fake_ext( - vif_extension_info["name"], vif_extension_info["alias"]) - discoverable_extensions.update(vif_extension) - - def _get_extensions(self, context): - """Filter extensions list based on policy.""" - - discoverable_extensions = dict() - for alias, ext in six.iteritems(self.extension_info.get_extensions()): - authorize = extensions.os_compute_soft_authorizer(alias) - if authorize(context, action='discoverable'): - discoverable_extensions[alias] = ext - else: - LOG.debug("Filter out extension %s from discover list", - alias) - - # Add fake v2 extensions to list - extra_exts = {} - for alias in discoverable_extensions: - if alias in v21_to_v2_extension_list_mapping: - for extra_ext in v21_to_v2_extension_list_mapping[alias]: - extra_exts[extra_ext["alias"]] = self._create_fake_ext( - extra_ext["name"], extra_ext["alias"]) - discoverable_extensions.update(extra_exts) - - # Suppress extensions which we don't want to see in v2 - for suppress_ext in v2_extension_suppress_list: - try: - del discoverable_extensions[suppress_ext] - except KeyError: - pass - - # v2.1 to v2 extension name mapping - for rename_ext in v21_to_v2_alias_mapping: - if rename_ext in discoverable_extensions: - new_name = v21_to_v2_alias_mapping[rename_ext] - mod_ext = copy.deepcopy( - discoverable_extensions.pop(rename_ext)) - mod_ext.alias = new_name - discoverable_extensions[new_name] = mod_ext - - return discoverable_extensions + 'alias': 'OS-EXT-VIF-NET', + 'description': 'Adds network id parameter' + ' to the virtual interface list.'} + all_extensions.append(vif_extension_info) @extensions.expected_errors(()) def index(self, req): context = req.environ['nova.context'] - authorize(context) - discoverable_extensions = self._get_extensions(context) + context.can(ext_policies.BASE_POLICY_NAME) + # NOTE(gmann): This is for v2.1 compatible mode where # extension list should show all extensions as shown by v2. - # Here we add VIF extension which has been removed from v2.1 list. if req.is_legacy_v2(): - self._add_vif_extension(discoverable_extensions) - sorted_ext_list = sorted( - six.iteritems(discoverable_extensions)) + return dict(extensions=EXTENSION_LIST_LEGACY_V2_COMPATIBLE) - extensions = [] - for _alias, ext in sorted_ext_list: - extensions.append(self._translate(ext)) - - return dict(extensions=extensions) + return dict(extensions=EXTENSION_LIST) @extensions.expected_errors(404) def show(self, req, id): context = req.environ['nova.context'] - authorize(context) - try: - # NOTE(dprince): the extensions alias is used as the 'id' for show - ext = self._get_extensions(context)[id] - except KeyError: - raise webob.exc.HTTPNotFound() - - return dict(extension=self._translate(ext)) - - -class ExtensionInfo(extensions.V21APIExtensionBase): - """Extension information.""" - - name = "Extensions" - alias = ALIAS - version = 1 + context.can(ext_policies.BASE_POLICY_NAME) + all_exts = EXTENSION_LIST + # NOTE(gmann): This is for v2.1 compatible mode where + # extension list should show all extensions as shown by v2. + if req.is_legacy_v2(): + all_exts = EXTENSION_LIST_LEGACY_V2_COMPATIBLE - def get_resources(self): - resources = [ - extensions.ResourceExtension( - ALIAS, ExtensionInfoController(self.extension_info), - member_name='extension')] - return resources + # NOTE(dprince): the extensions alias is used as the 'id' for show + for ext in all_exts: + if ext['alias'] == id: + return dict(extension=ext) - def get_controller_extensions(self): - return [] + raise webob.exc.HTTPNotFound() class LoadedExtensionInfo(object): @@ -251,7 +918,7 @@ def _check_extension(self, extension): try: extension.is_valid() except AttributeError: - LOG.exception(_LE("Exception loading extension")) + LOG.exception("Exception loading extension") return False return True diff --git a/nova/api/openstack/compute/fixed_ips.py b/nova/api/openstack/compute/fixed_ips.py index d432fa586bd..1814f042a8a 100644 --- a/nova/api/openstack/compute/fixed_ips.py +++ b/nova/api/openstack/compute/fixed_ips.py @@ -15,6 +15,8 @@ import webob import webob.exc +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack.compute.schemas import fixed_ips from nova.api.openstack import extensions from nova.api.openstack import wsgi @@ -22,9 +24,7 @@ from nova import exception from nova.i18n import _ from nova import objects - -ALIAS = 'os-fixed-ips' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import fixed_ips as fi_policies class FixedIPController(wsgi.Controller): @@ -39,11 +39,12 @@ def _fill_reserved_status(self, req, fixed_ip, fixed_ip_info): def _fill_reserved_status(self, req, fixed_ip, fixed_ip_info): fixed_ip_info['fixed_ip']['reserved'] = fixed_ip.reserved + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 404)) def show(self, req, id): """Return data about the given fixed IP.""" context = req.environ['nova.context'] - authorize(context) + context.can(fi_policies.BASE_POLICY_NAME) attrs = ['network', 'instance'] try: @@ -73,23 +74,25 @@ def show(self, req, id): return fixed_ip_info + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.response(202) @extensions.expected_errors((400, 404)) @validation.schema(fixed_ips.reserve) @wsgi.action('reserve') def reserve(self, req, id, body): context = req.environ['nova.context'] - authorize(context) + context.can(fi_policies.BASE_POLICY_NAME) return self._set_reserved(context, id, True) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.response(202) @extensions.expected_errors((400, 404)) @validation.schema(fixed_ips.unreserve) @wsgi.action('unreserve') def unreserve(self, req, id, body): context = req.environ['nova.context'] - authorize(context) + context.can(fi_policies.BASE_POLICY_NAME) return self._set_reserved(context, id, False) def _set_reserved(self, context, address, reserved): @@ -103,21 +106,3 @@ def _set_reserved(self, context, address, reserved): except exception.FixedIpInvalid: msg = _("Fixed IP %s not valid") % address raise webob.exc.HTTPBadRequest(explanation=msg) - - -class FixedIps(extensions.V21APIExtensionBase): - """Fixed IPs support.""" - - name = "FixedIPs" - alias = ALIAS - version = 1 - - def get_resources(self): - member_actions = {'action': 'POST'} - resources = extensions.ResourceExtension(ALIAS, - FixedIPController(), - member_actions=member_actions) - return [resources] - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/flavor_access.py b/nova/api/openstack/compute/flavor_access.py index 50439d22846..b591212d691 100644 --- a/nova/api/openstack/compute/flavor_access.py +++ b/nova/api/openstack/compute/flavor_access.py @@ -21,15 +21,12 @@ from nova.api.openstack import common from nova.api.openstack.compute.schemas import flavor_access from nova.api.openstack import extensions +from nova.api.openstack import identity from nova.api.openstack import wsgi from nova.api import validation from nova import exception from nova.i18n import _ -from nova import objects - -ALIAS = 'os-flavor-access' -soft_authorize = extensions.os_compute_soft_authorizer(ALIAS) -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import flavor_access as fa_policies def _marshall_flavor_access(flavor): @@ -43,14 +40,10 @@ def _marshall_flavor_access(flavor): class FlavorAccessController(wsgi.Controller): """The flavor access API controller for the OpenStack API.""" - - def __init__(self): - super(FlavorAccessController, self).__init__() - @extensions.expected_errors(404) def index(self, req, flavor_id): context = req.environ['nova.context'] - authorize(context) + context.can(fa_policies.BASE_POLICY_NAME) flavor = common.get_flavor(context, flavor_id) @@ -66,13 +59,13 @@ def index(self, req, flavor_id): class FlavorActionController(wsgi.Controller): """The flavor access API controller for the OpenStack API.""" def _extend_flavor(self, flavor_rval, flavor_ref): - key = "%s:is_public" % (FlavorAccess.alias) + key = "os-flavor-access:is_public" flavor_rval[key] = flavor_ref['is_public'] @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['nova.context'] - if soft_authorize(context): + if context.can(fa_policies.BASE_POLICY_NAME, fatal=False): db_flavor = req.get_db_flavor(id) self._extend_flavor(resp_obj.obj['flavor'], db_flavor) @@ -80,7 +73,7 @@ def show(self, req, resp_obj, id): @wsgi.extends def detail(self, req, resp_obj): context = req.environ['nova.context'] - if soft_authorize(context): + if context.can(fa_policies.BASE_POLICY_NAME, fatal=False): flavors = list(resp_obj.obj['flavors']) for flavor_rval in flavors: db_flavor = req.get_db_flavor(flavor_rval['id']) @@ -89,7 +82,7 @@ def detail(self, req, resp_obj): @wsgi.extends(action='create') def create(self, req, body, resp_obj): context = req.environ['nova.context'] - if soft_authorize(context): + if context.can(fa_policies.BASE_POLICY_NAME, fatal=False): db_flavor = req.get_db_flavor(resp_obj.obj['flavor']['id']) self._extend_flavor(resp_obj.obj['flavor'], db_flavor) @@ -99,10 +92,11 @@ def create(self, req, body, resp_obj): @validation.schema(flavor_access.add_tenant_access) def _add_tenant_access(self, req, id, body): context = req.environ['nova.context'] - authorize(context, action="add_tenant_access") + context.can(fa_policies.POLICY_ROOT % "add_tenant_access") vals = body['addTenantAccess'] tenant = vals['tenant'] + identity.verify_project_id(context, tenant) flavor = common.get_flavor(context, id) @@ -116,8 +110,6 @@ def _add_tenant_access(self, req, id, body): raise webob.exc.HTTPNotFound(explanation=e.format_message()) except exception.FlavorAccessExists as err: raise webob.exc.HTTPConflict(explanation=err.format_message()) - except exception.AdminRequired as e: - raise webob.exc.HTTPForbidden(explanation=e.format_message()) return _marshall_flavor_access(flavor) @extensions.expected_errors((400, 403, 404)) @@ -125,39 +117,21 @@ def _add_tenant_access(self, req, id, body): @validation.schema(flavor_access.remove_tenant_access) def _remove_tenant_access(self, req, id, body): context = req.environ['nova.context'] - authorize(context, action="remove_tenant_access") + context.can( + fa_policies.POLICY_ROOT % "remove_tenant_access") vals = body['removeTenantAccess'] tenant = vals['tenant'] + identity.verify_project_id(context, tenant) + + # NOTE(gibi): We have to load a flavor from the db here as + # flavor.remove_access() will try to emit a notification and that needs + # a fully loaded flavor. + flavor = common.get_flavor(context, id) - flavor = objects.Flavor(context=context, flavorid=id) try: flavor.remove_access(tenant) except (exception.FlavorAccessNotFound, exception.FlavorNotFound) as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except exception.AdminRequired as e: - raise webob.exc.HTTPForbidden(explanation=e.format_message()) return _marshall_flavor_access(flavor) - - -class FlavorAccess(extensions.V21APIExtensionBase): - """Flavor access support.""" - - name = "FlavorAccess" - alias = ALIAS - version = 1 - - def get_resources(self): - res = extensions.ResourceExtension( - ALIAS, - controller=FlavorAccessController(), - parent=dict(member_name='flavor', collection_name='flavors')) - - return [res] - - def get_controller_extensions(self): - extension = extensions.ControllerExtension( - self, 'flavors', FlavorActionController()) - - return [extension] diff --git a/nova/api/openstack/compute/flavor_manage.py b/nova/api/openstack/compute/flavor_manage.py index 8f239f623ae..d03071fc657 100644 --- a/nova/api/openstack/compute/flavor_manage.py +++ b/nova/api/openstack/compute/flavor_manage.py @@ -12,6 +12,8 @@ import webob +from oslo_log import log as logging + from nova.api.openstack.compute.schemas import flavor_manage from nova.api.openstack.compute.views import flavors as flavors_view from nova.api.openstack import extensions @@ -19,15 +21,20 @@ from nova.api import validation from nova.compute import flavors from nova import exception +from nova.i18n import _ +from nova import objects +from nova.policies import base +from nova.policies import flavor_manage as fm_policies +from nova import policy -ALIAS = "os-flavor-manage" -authorize = extensions.os_compute_authorizer(ALIAS) +LOG = logging.getLogger(__name__) +ALIAS = "os-flavor-manage" class FlavorManageController(wsgi.Controller): """The Flavor Lifecycle API controller for the OpenStack API.""" - _view_builder_class = flavors_view.ViewBuilderV21 + _view_builder_class = flavors_view.ViewBuilder def __init__(self): super(FlavorManageController, self).__init__() @@ -40,25 +47,39 @@ def __init__(self): @wsgi.action("delete") def _delete(self, req, id): context = req.environ['nova.context'] - authorize(context) + # TODO(rb560u): remove this check in future release + using_old_action = \ + policy.verify_deprecated_policy(fm_policies.BASE_POLICY_NAME, + fm_policies.POLICY_ROOT % 'delete', + base.RULE_ADMIN_API, + context) + if not using_old_action: + context.can(fm_policies.POLICY_ROOT % 'delete') + + flavor = objects.Flavor(context=context, flavorid=id) try: - flavor = flavors.get_flavor_by_flavor_id( - id, ctxt=context, read_deleted="no") + flavor.destroy() except exception.FlavorNotFound as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) - flavors.destroy(flavor['name']) - # NOTE(oomichi): Return 200 for backwards compatibility but should be 201 # as this operation complete the creation of flavor resource. @wsgi.action("create") - @extensions.expected_errors((400, 409, 500)) + @extensions.expected_errors((400, 409)) @validation.schema(flavor_manage.create_v20, '2.0', '2.0') @validation.schema(flavor_manage.create, '2.1') def _create(self, req, body): context = req.environ['nova.context'] - authorize(context) + # TODO(rb560u): remove this check in future release + using_old_action = \ + policy.verify_deprecated_policy(fm_policies.BASE_POLICY_NAME, + fm_policies.POLICY_ROOT % 'create', + base.RULE_ADMIN_API, + context) + + if not using_old_action: + context.can(fm_policies.POLICY_ROOT % 'create') vals = body['flavor'] @@ -84,24 +105,8 @@ def _create(self, req, body): except (exception.FlavorExists, exception.FlavorIdExists) as err: raise webob.exc.HTTPConflict(explanation=err.format_message()) - except exception.FlavorCreateFailed as err: - raise webob.exc.HTTPInternalServerError(explanation= - err.format_message()) + except exception.ObjectActionError: + raise webob.exc.HTTPConflict(explanation=_( + 'Not all flavors have been migrated to the API database')) return self._view_builder.show(req, flavor) - - -class FlavorManage(extensions.V21APIExtensionBase): - """Flavor create/delete API support.""" - - name = "FlavorManage" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = FlavorManageController() - extension = extensions.ControllerExtension(self, 'flavors', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/flavor_rxtx.py b/nova/api/openstack/compute/flavor_rxtx.py index cd1b7fd3a17..374a561fd9a 100644 --- a/nova/api/openstack/compute/flavor_rxtx.py +++ b/nova/api/openstack/compute/flavor_rxtx.py @@ -14,11 +14,10 @@ """The Flavor Rxtx API extension.""" -from nova.api.openstack import extensions from nova.api.openstack import wsgi +from nova.policies import flavor_rxtx as fr_policies ALIAS = 'os-flavor-rxtx' -authorize = extensions.os_compute_soft_authorizer(ALIAS) class FlavorRxtxController(wsgi.Controller): @@ -29,7 +28,8 @@ def _extend_flavors(self, req, flavors): flavor[key] = db_flavor['rxtx_factor'] or "" def _show(self, req, resp_obj): - if not authorize(req.environ['nova.context']): + context = req.environ['nova.context'] + if not context.can(fr_policies.BASE_POLICY_NAME, fatal=False): return if 'flavor' in resp_obj.obj: self._extend_flavors(req, [resp_obj.obj['flavor']]) @@ -44,22 +44,7 @@ def create(self, req, resp_obj, body): @wsgi.extends def detail(self, req, resp_obj): - if not authorize(req.environ['nova.context']): + context = req.environ['nova.context'] + if not context.can(fr_policies.BASE_POLICY_NAME, fatal=False): return self._extend_flavors(req, list(resp_obj.obj['flavors'])) - - -class FlavorRxtx(extensions.V21APIExtensionBase): - """Support to show the rxtx status of a flavor.""" - - name = "FlavorRxtx" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = FlavorRxtxController() - extension = extensions.ControllerExtension(self, 'flavors', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/flavors.py b/nova/api/openstack/compute/flavors.py index 33a79e9d000..f9b7e8bee73 100644 --- a/nova/api/openstack/compute/flavors.py +++ b/nova/api/openstack/compute/flavors.py @@ -23,6 +23,7 @@ from nova.compute import flavors from nova import exception from nova.i18n import _ +from nova import objects from nova import utils ALIAS = 'flavors' @@ -31,7 +32,7 @@ class FlavorsController(wsgi.Controller): """Flavor controller for the OpenStack API.""" - _view_builder_class = flavors_view.ViewBuilderV21 + _view_builder_class = flavors_view.ViewBuilder @extensions.expected_errors(400) def index(self, req): @@ -105,7 +106,7 @@ def _get_flavors(self, req): raise webob.exc.HTTPBadRequest(explanation=msg) try: - limited_flavors = flavors.get_all_flavors_sorted_list(context, + limited_flavors = objects.FlavorList.get_all(context, filters=filters, sort_key=sort_key, sort_dir=sort_dir, limit=limit, marker=marker) except exception.MarkerNotFound: @@ -113,26 +114,3 @@ def _get_flavors(self, req): raise webob.exc.HTTPBadRequest(explanation=msg) return limited_flavors - - -class Flavors(extensions.V21APIExtensionBase): - """Flavors Extension.""" - name = "Flavors" - alias = ALIAS - version = 1 - - def get_resources(self): - collection_actions = {'detail': 'GET'} - member_actions = {'action': 'POST'} - - resources = [ - extensions.ResourceExtension(ALIAS, - FlavorsController(), - member_name='flavor', - collection_actions=collection_actions, - member_actions=member_actions) - ] - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/flavors_extraspecs.py b/nova/api/openstack/compute/flavors_extraspecs.py index 6482cfe67d2..aebca70e091 100644 --- a/nova/api/openstack/compute/flavors_extraspecs.py +++ b/nova/api/openstack/compute/flavors_extraspecs.py @@ -23,18 +23,12 @@ from nova.api import validation from nova import exception from nova.i18n import _ +from nova.policies import flavor_extra_specs as fes_policies from nova import utils -ALIAS = 'os-flavor-extra-specs' -authorize = extensions.os_compute_authorizer(ALIAS) - class FlavorExtraSpecsController(wsgi.Controller): """The flavor extra specs API controller for the OpenStack API.""" - - def __init__(self, *args, **kwargs): - super(FlavorExtraSpecsController, self).__init__(*args, **kwargs) - def _get_extra_specs(self, context, flavor_id): flavor = common.get_flavor(context, flavor_id) return dict(extra_specs=flavor.extra_specs) @@ -42,7 +36,7 @@ def _get_extra_specs(self, context, flavor_id): # NOTE(gmann): Max length for numeric value is being checked # explicitly as json schema cannot have max length check for numeric value def _check_extra_specs_value(self, specs): - for key, value in six.iteritems(specs): + for value in specs.values(): try: if isinstance(value, (six.integer_types, float)): value = six.text_type(value) @@ -56,7 +50,7 @@ def _check_extra_specs_value(self, specs): def index(self, req, flavor_id): """Returns the list of extra specs for a given flavor.""" context = req.environ['nova.context'] - authorize(context, action='index') + context.can(fes_policies.POLICY_ROOT % 'index') return self._get_extra_specs(context, flavor_id) # NOTE(gmann): Here should be 201 instead of 200 by v2.1 @@ -66,7 +60,7 @@ def index(self, req, flavor_id): @validation.schema(flavors_extraspecs.create) def create(self, req, flavor_id, body): context = req.environ['nova.context'] - authorize(context, action='create') + context.can(fes_policies.POLICY_ROOT % 'create') specs = body['extra_specs'] self._check_extra_specs_value(specs) @@ -84,7 +78,7 @@ def create(self, req, flavor_id, body): @validation.schema(flavors_extraspecs.update) def update(self, req, flavor_id, id, body): context = req.environ['nova.context'] - authorize(context, action='update') + context.can(fes_policies.POLICY_ROOT % 'update') self._check_extra_specs_value(body) if id not in body: @@ -104,7 +98,7 @@ def update(self, req, flavor_id, id, body): def show(self, req, flavor_id, id): """Return a single extra spec item.""" context = req.environ['nova.context'] - authorize(context, action='show') + context.can(fes_policies.POLICY_ROOT % 'show') flavor = common.get_flavor(context, flavor_id) try: return {id: flavor.extra_specs[id]} @@ -121,7 +115,7 @@ def show(self, req, flavor_id, id): def delete(self, req, flavor_id, id): """Deletes an existing extra spec.""" context = req.environ['nova.context'] - authorize(context, action='delete') + context.can(fes_policies.POLICY_ROOT % 'delete') flavor = common.get_flavor(context, flavor_id) try: del flavor.extra_specs[id] @@ -134,21 +128,3 @@ def delete(self, req, flavor_id, id): "key %(key)s.") % dict(flavor_id=flavor_id, key=id) raise webob.exc.HTTPNotFound(explanation=msg) - - -class FlavorsExtraSpecs(extensions.V21APIExtensionBase): - """Flavors extra specs support.""" - name = 'FlavorExtraSpecs' - alias = ALIAS - version = 1 - - def get_resources(self): - extra_specs = extensions.ResourceExtension( - 'os-extra_specs', - FlavorExtraSpecsController(), - parent=dict(member_name='flavor', collection_name='flavors')) - - return [extra_specs] - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/floating_ip_dns.py b/nova/api/openstack/compute/floating_ip_dns.py index 5da98a1b7a2..3e2d432126c 100644 --- a/nova/api/openstack/compute/floating_ip_dns.py +++ b/nova/api/openstack/compute/floating_ip_dns.py @@ -12,11 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. -import urllib - from oslo_utils import netutils +from six.moves import urllib import webob +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import common from nova.api.openstack.compute.schemas import floating_ip_dns from nova.api.openstack import extensions @@ -25,10 +26,7 @@ from nova import exception from nova.i18n import _ from nova import network - - -ALIAS = "os-floating-ip-dns" -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import floating_ip_dns as fid_policies def _translate_dns_entry_view(dns_entry): @@ -68,7 +66,7 @@ def _unquote_domain(domain): but Routes tends to choke on them, so we need an extra level of by-hand quoting here. """ - return urllib.unquote(domain).replace('%2E', '.') + return urllib.parse.unquote(domain).replace('%2E', '.') def _create_dns_entry(ip, name, domain): @@ -85,13 +83,14 @@ class FloatingIPDNSDomainController(wsgi.Controller): def __init__(self): super(FloatingIPDNSDomainController, self).__init__() - self.network_api = network.API(skip_policy_check=True) + self.network_api = network.API() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(501) def index(self, req): """Return a list of available DNS domains.""" context = req.environ['nova.context'] - authorize(context) + context.can(fid_policies.BASE_POLICY_NAME) try: domains = self.network_api.get_dns_domains(context) @@ -106,12 +105,13 @@ def index(self, req): return _translate_domain_entries_view(domainlist) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 501)) @validation.schema(floating_ip_dns.domain_entry_update) def update(self, req, id, body): """Add or modify domain entry.""" context = req.environ['nova.context'] - authorize(context, action="domain:update") + context.can(fid_policies.POLICY_ROOT % "domain:update") fqdomain = _unquote_domain(id) entry = body['domain_entry'] scope = entry['scope'] @@ -141,12 +141,13 @@ def update(self, req, id, body): 'scope': scope, area_name: area}) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((404, 501)) @wsgi.response(202) def delete(self, req, id): """Delete the domain identified by id.""" context = req.environ['nova.context'] - authorize(context, action="domain:delete") + context.can(fid_policies.POLICY_ROOT % "domain:delete") domain = _unquote_domain(id) # Delete the whole domain @@ -163,13 +164,14 @@ class FloatingIPDNSEntryController(wsgi.Controller): def __init__(self): super(FloatingIPDNSEntryController, self).__init__() - self.network_api = network.API(skip_policy_check=True) + self.network_api = network.API() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((404, 501)) def show(self, req, domain_id, id): """Return the DNS entry that corresponds to domain_id and id.""" context = req.environ['nova.context'] - authorize(context) + context.can(fid_policies.BASE_POLICY_NAME) domain = _unquote_domain(domain_id) floating_ip = None @@ -202,12 +204,13 @@ def show(self, req, domain_id, id): entry = _create_dns_entry(entries[0], id, domain) return _translate_dns_entry_view(entry) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(501) @validation.schema(floating_ip_dns.dns_entry_update) def update(self, req, domain_id, id, body): """Add or modify dns entry.""" context = req.environ['nova.context'] - authorize(context) + context.can(fid_policies.BASE_POLICY_NAME) domain = _unquote_domain(domain_id) name = id entry = body['dns_entry'] @@ -233,12 +236,13 @@ def update(self, req, domain_id, id, body): 'type': dns_type, 'domain': domain}) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((404, 501)) @wsgi.response(202) def delete(self, req, domain_id, id): """Delete the entry identified by req and id.""" context = req.environ['nova.context'] - authorize(context) + context.can(fid_policies.BASE_POLICY_NAME) domain = _unquote_domain(domain_id) name = id @@ -248,32 +252,3 @@ def delete(self, req, domain_id, id): common.raise_feature_not_supported() except exception.NotFound as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) - - -class FloatingIpDns(extensions.V21APIExtensionBase): - """Floating IP DNS support.""" - - name = "FloatingIpDns" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension(ALIAS, - controller=FloatingIPDNSDomainController()) - resources.append(res) - - res = extensions.ResourceExtension('entries', - controller=FloatingIPDNSEntryController(), - parent={'member_name': 'domain', - 'collection_name': 'os-floating-ip-dns'}) - resources.append(res) - - return resources - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] diff --git a/nova/api/openstack/compute/floating_ip_pools.py b/nova/api/openstack/compute/floating_ip_pools.py index b9ea7866c11..d1ddcb61a80 100644 --- a/nova/api/openstack/compute/floating_ip_pools.py +++ b/nova/api/openstack/compute/floating_ip_pools.py @@ -12,13 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import network - - -ALIAS = 'os-floating-ip-pools' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import floating_ip_pools as fip_policies def _translate_floating_ip_view(pool_name): @@ -38,32 +37,14 @@ class FloatingIPPoolsController(wsgi.Controller): """The Floating IP Pool API controller for the OpenStack API.""" def __init__(self): - self.network_api = network.API(skip_policy_check=True) + self.network_api = network.API() super(FloatingIPPoolsController, self).__init__() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(()) def index(self, req): """Return a list of pools.""" context = req.environ['nova.context'] - authorize(context) + context.can(fip_policies.BASE_POLICY_NAME) pools = self.network_api.get_floating_ip_pools(context) return _translate_floating_ip_pools_view(pools) - - -class FloatingIpPools(extensions.V21APIExtensionBase): - """Floating IPs support.""" - - name = "FloatingIpPools" - alias = ALIAS - version = 1 - - def get_resources(self): - resource = [extensions.ResourceExtension(ALIAS, - FloatingIPPoolsController())] - return resource - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] diff --git a/nova/api/openstack/compute/floating_ips.py b/nova/api/openstack/compute/floating_ips.py index 1121eef5d7d..45dc1627abf 100644 --- a/nova/api/openstack/compute/floating_ips.py +++ b/nova/api/openstack/compute/floating_ips.py @@ -15,27 +15,26 @@ # License for the specific language governing permissions and limitations # under the License. -import netaddr from oslo_log import log as logging +from oslo_utils import netutils from oslo_utils import uuidutils import webob +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import common from nova.api.openstack.compute.schemas import floating_ips from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api import validation from nova import compute -from nova.compute import utils as compute_utils from nova import exception from nova.i18n import _ -from nova.i18n import _LW from nova import network +from nova.policies import floating_ips as fi_policies LOG = logging.getLogger(__name__) -ALIAS = 'os-floating-ips' -authorize = extensions.os_compute_authorizer(ALIAS) def _translate_floating_ip_view(floating_ip): @@ -44,12 +43,31 @@ def _translate_floating_ip_view(floating_ip): 'ip': floating_ip['address'], 'pool': floating_ip['pool'], } + + # If fixed_ip is unset on floating_ip, then we can't get any of the next + # stuff, so we'll just short-circuit + if 'fixed_ip' not in floating_ip: + result['fixed_ip'] = None + result['instance_id'] = None + return {'floating_ip': result} + + # TODO(rlrossit): These look like dicts, but they're actually versioned + # objects, so we need to do these contain checks because they will not be + # caught by the exceptions below (it raises NotImplementedError and + # OrphanedObjectError. This comment can probably be removed when + # the dict syntax goes away. try: - result['fixed_ip'] = floating_ip['fixed_ip']['address'] + if 'address' in floating_ip['fixed_ip']: + result['fixed_ip'] = floating_ip['fixed_ip']['address'] + else: + result['fixed_ip'] = None except (TypeError, KeyError, AttributeError): result['fixed_ip'] = None try: - result['instance_id'] = floating_ip['fixed_ip']['instance_uuid'] + if 'instance_uuid' in floating_ip['fixed_ip']: + result['instance_id'] = floating_ip['fixed_ip']['instance_uuid'] + else: + result['instance_id'] = None except (TypeError, KeyError, AttributeError): result['instance_id'] = None return {'floating_ip': result} @@ -85,19 +103,20 @@ def disassociate_floating_ip(self, context, instance, address): raise webob.exc.HTTPForbidden(explanation=msg) -class FloatingIPController(object): +class FloatingIPController(wsgi.Controller): """The Floating IPs API controller for the OpenStack API.""" def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) - self.network_api = network.API(skip_policy_check=True) + self.compute_api = compute.API() + self.network_api = network.API() super(FloatingIPController, self).__init__() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 404)) def show(self, req, id): """Return data about the given floating IP.""" context = req.environ['nova.context'] - authorize(context) + context.can(fi_policies.BASE_POLICY_NAME) try: floating_ip = self.network_api.get_floating_ip(context, id) @@ -109,20 +128,22 @@ def show(self, req, id): return _translate_floating_ip_view(floating_ip) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(()) def index(self, req): """Return a list of floating IPs allocated to a project.""" context = req.environ['nova.context'] - authorize(context) + context.can(fi_policies.BASE_POLICY_NAME) floating_ips = self.network_api.get_floating_ips_by_project(context) return _translate_floating_ips_view(floating_ips) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 403, 404)) def create(self, req, body=None): context = req.environ['nova.context'] - authorize(context) + context.can(fi_policies.BASE_POLICY_NAME) pool = None if body and 'pool' in body: @@ -149,11 +170,12 @@ def create(self, req, body=None): return _translate_floating_ip_view(ip) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.response(202) @extensions.expected_errors((400, 403, 404, 409)) def delete(self, req, id): context = req.environ['nova.context'] - authorize(context) + context.can(fi_policies.BASE_POLICY_NAME) # get the floating ip object try: @@ -176,32 +198,37 @@ def delete(self, req, id): except exception.CannotDisassociateAutoAssignedFloatingIP: msg = _('Cannot disassociate auto assigned floating IP') raise webob.exc.HTTPForbidden(explanation=msg) + except exception.FloatingIpNotFoundForAddress as exc: + raise webob.exc.HTTPNotFound(explanation=exc.format_message()) class FloatingIPActionController(wsgi.Controller): + """This API is deprecated from the Microversion '2.44'.""" + def __init__(self, *args, **kwargs): super(FloatingIPActionController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) - self.network_api = network.API(skip_policy_check=True) + self.compute_api = compute.API() + self.network_api = network.API() + @wsgi.Controller.api_version("2.1", "2.43") @extensions.expected_errors((400, 403, 404)) @wsgi.action('addFloatingIp') @validation.schema(floating_ips.add_floating_ip) def _add_floating_ip(self, req, id, body): """Associate floating_ip to an instance.""" context = req.environ['nova.context'] - authorize(context) + context.can(fi_policies.BASE_POLICY_NAME) address = body['addFloatingIp']['address'] instance = common.get_instance(self.compute_api, context, id, expected_attrs=['flavor']) - cached_nwinfo = compute_utils.get_nw_info_for_instance(instance) + cached_nwinfo = instance.get_network_info() if not cached_nwinfo: LOG.warning( - _LW('Info cache is %r during associate with no nw_info cache'), - instance.info_cache, instance=instance) - msg = _('No nw_info cache associated with instance') + 'Info cache is %r during associate with no nw_info cache', + instance.info_cache, instance=instance) + msg = _('Instance network is not ready yet') raise webob.exc.HTTPBadRequest(explanation=msg) fixed_ips = cached_nwinfo.fixed_ips() @@ -222,7 +249,7 @@ def _add_floating_ip(self, req, id, body): if not fixed_address: try: fixed_address = next(ip['address'] for ip in fixed_ips - if netaddr.valid_ipv4(ip['address'])) + if netutils.is_valid_ipv4(ip['address'])) except StopIteration: msg = _('Unable to associate floating IP %(address)s ' 'to any fixed IPs for instance %(id)s. ' @@ -231,8 +258,8 @@ def _add_floating_ip(self, req, id, body): {'address': address, 'id': id}) raise webob.exc.HTTPBadRequest(explanation=msg) if len(fixed_ips) > 1: - LOG.warning(_LW('multiple fixed_ips exist, using the first ' - 'IPv4 fixed_ip: %s'), fixed_address) + LOG.warning('multiple fixed_ips exist, using the first ' + 'IPv4 fixed_ip: %s', fixed_address) try: self.network_api.associate_floating_ip(context, instance, @@ -241,6 +268,8 @@ def _add_floating_ip(self, req, id, body): except exception.FloatingIpAssociated: msg = _('floating IP is already associated') raise webob.exc.HTTPBadRequest(explanation=msg) + except exception.FloatingIpAssociateFailed as e: + raise webob.exc.HTTPBadRequest(explanation=e.format_message()) except exception.NoFloatingIpInterface: msg = _('l3driver call to add floating IP failed') raise webob.exc.HTTPBadRequest(explanation=msg) @@ -262,13 +291,14 @@ def _add_floating_ip(self, req, id, body): return webob.Response(status_int=202) + @wsgi.Controller.api_version("2.1", "2.43") @extensions.expected_errors((400, 403, 404, 409)) @wsgi.action('removeFloatingIp') @validation.schema(floating_ips.remove_floating_ip) def _remove_floating_ip(self, req, id, body): """Dissociate floating_ip from an instance.""" context = req.environ['nova.context'] - authorize(context) + context.can(fi_policies.BASE_POLICY_NAME) address = body['removeFloatingIp']['address'] @@ -299,21 +329,3 @@ def _remove_floating_ip(self, req, id, body): msg = _("Floating IP %(address)s is not associated with instance " "%(id)s.") % {'address': address, 'id': id} raise webob.exc.HTTPConflict(explanation=msg) - - -class FloatingIps(extensions.V21APIExtensionBase): - """Floating IPs support.""" - - name = "FloatingIps" - alias = ALIAS - version = 1 - - def get_resources(self): - resource = [extensions.ResourceExtension(ALIAS, - FloatingIPController())] - return resource - - def get_controller_extensions(self): - controller = FloatingIPActionController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/floating_ips_bulk.py b/nova/api/openstack/compute/floating_ips_bulk.py index 273eedecf0e..ea973b98d69 100644 --- a/nova/api/openstack/compute/floating_ips_bulk.py +++ b/nova/api/openstack/compute/floating_ips_bulk.py @@ -13,42 +13,41 @@ # under the License. import netaddr -from oslo_config import cfg import six import webob.exc +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack.compute.schemas import floating_ips_bulk from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api import validation +import nova.conf from nova import exception from nova.i18n import _ from nova import objects +from nova.policies import floating_ips_bulk as fib_policies -CONF = cfg.CONF -CONF.import_opt('default_floating_pool', 'nova.network.floating_ips') -CONF.import_opt('public_interface', 'nova.network.linux_net') - - -ALIAS = 'os-floating-ips-bulk' -authorize = extensions.os_compute_authorizer(ALIAS) +CONF = nova.conf.CONF class FloatingIPBulkController(wsgi.Controller): + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(404) def index(self, req): """Return a list of all floating IPs.""" context = req.environ['nova.context'] - authorize(context) + context.can(fib_policies.BASE_POLICY_NAME) return self._get_floating_ip_info(context) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(404) def show(self, req, id): """Return a list of all floating IPs for a given host.""" context = req.environ['nova.context'] - authorize(context) + context.can(fib_policies.BASE_POLICY_NAME) return self._get_floating_ip_info(context, id) @@ -84,12 +83,13 @@ def _get_floating_ip_info(self, context, host=None): return floating_ip_info + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 409)) @validation.schema(floating_ips_bulk.create) def create(self, req, body): """Bulk create floating IPs.""" context = req.environ['nova.context'] - authorize(context) + context.can(fib_policies.BASE_POLICY_NAME) params = body['floating_ips_bulk_create'] ip_range = params['ip_range'] @@ -112,12 +112,13 @@ def create(self, req, body): "pool": pool, "interface": interface}} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 404)) @validation.schema(floating_ips_bulk.delete) def update(self, req, id, body): """Bulk delete floating IPs.""" context = req.environ['nova.context'] - authorize(context) + context.can(fib_policies.BASE_POLICY_NAME) if id != "delete": msg = _("Unknown action") @@ -150,22 +151,3 @@ def _address_to_hosts(self, addresses): return net.iter_hosts() except netaddr.AddrFormatError as exc: raise exception.InvalidInput(reason=six.text_type(exc)) - - -class FloatingIpsBulk(extensions.V21APIExtensionBase): - """Bulk handling of Floating IPs.""" - - name = "FloatingIpsBulk" - alias = ALIAS - version = 1 - - def get_resources(self): - resource = [extensions.ResourceExtension(ALIAS, - FloatingIPBulkController())] - return resource - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] diff --git a/nova/api/openstack/compute/fping.py b/nova/api/openstack/compute/fping.py index 8a71be61179..9ece098177c 100644 --- a/nova/api/openstack/compute/fping.py +++ b/nova/api/openstack/compute/fping.py @@ -17,40 +17,37 @@ import itertools import os -from oslo_config import cfg -import six + from webob import exc +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute +import nova.conf from nova.i18n import _ +from nova.policies import fping as fping_policies from nova import utils -ALIAS = "os-fping" - -authorize = extensions.os_compute_authorizer(ALIAS) - -CONF = cfg.CONF -CONF.import_opt('fping_path', 'nova.api.openstack.compute.legacy_v2.contrib.' - 'fping') +CONF = nova.conf.CONF class FpingController(wsgi.Controller): def __init__(self, network_api=None): - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() self.last_call = {} def check_fping(self): - if not os.access(CONF.fping_path, os.X_OK): + if not os.access(CONF.api.fping_path, os.X_OK): raise exc.HTTPServiceUnavailable( explanation=_("fping utility is not found.")) @staticmethod def fping(ips): - fping_ret = utils.execute(CONF.fping_path, *ips, + fping_ret = utils.execute(CONF.api.fping_path, *ips, check_exit_code=False) if not fping_ret: return set() @@ -70,14 +67,15 @@ def _get_instance_ips(context, instance): ret += [ip["address"] for ip in all_ips] return ret + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(503) def index(self, req): context = req.environ["nova.context"] search_opts = dict(deleted=False) if "all_tenants" in req.GET: - authorize(context, action='all_tenants') + context.can(fping_policies.POLICY_ROOT % 'all_tenants') else: - authorize(context) + context.can(fping_policies.BASE_POLICY_NAME) if context.project_id: search_opts["project_id"] = context.project_id else: @@ -96,7 +94,7 @@ def index(self, req): exclude = set() instance_list = self.compute_api.get_all( - context, search_opts=search_opts, want_objects=True) + context, search_opts=search_opts) ip_list = [] instance_ips = {} instance_projects = {} @@ -112,7 +110,7 @@ def index(self, req): ip_list += ips alive_ips = self.fping(ip_list) res = [] - for instance_uuid, ips in six.iteritems(instance_ips): + for instance_uuid, ips in instance_ips.items(): res.append({ "id": instance_uuid, "project_id": instance_projects[instance_uuid], @@ -120,10 +118,11 @@ def index(self, req): }) return {"servers": res} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((404, 503)) def show(self, req, id): context = req.environ["nova.context"] - authorize(context) + context.can(fping_policies.BASE_POLICY_NAME) self.check_fping() instance = common.get_instance(self.compute_api, context, id) ips = [str(ip) for ip in self._get_instance_ips(context, instance)] @@ -135,18 +134,3 @@ def show(self, req, id): "alive": bool(set(ips) & alive_ips), } } - - -class Fping(extensions.V21APIExtensionBase): - """Fping Management Extension.""" - - name = "Fping" - alias = ALIAS - version = 1 - - def get_resources(self): - res = extensions.ResourceExtension(ALIAS, FpingController()) - return [res] - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/helpers.py b/nova/api/openstack/compute/helpers.py new file mode 100644 index 00000000000..24dc99301b7 --- /dev/null +++ b/nova/api/openstack/compute/helpers.py @@ -0,0 +1,116 @@ +# Copyright 2016 HPE, Inc. +# +# 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 oslo_utils import strutils +from webob import exc + +from nova.i18n import _ + +API_DISK_CONFIG = "OS-DCF:diskConfig" +API_ACCESS_V4 = "accessIPv4" +API_ACCESS_V6 = "accessIPv6" + +# possible ops +CREATE = 'create' +UPDATE = 'update' +REBUILD = 'rebuild' +RESIZE = 'resize' + + +def disk_config_from_api(value): + if value == 'AUTO': + return True + elif value == 'MANUAL': + return False + else: + msg = _("%s must be either 'MANUAL' or 'AUTO'.") % API_DISK_CONFIG + raise exc.HTTPBadRequest(explanation=msg) + + +def get_injected_files(personality): + """Create a list of injected files from the personality attribute. + + At this time, injected_files must be formatted as a list of + (file_path, file_content) pairs for compatibility with the + underlying compute service. + """ + injected_files = [] + for item in personality: + injected_files.append((item['path'], item['contents'])) + return injected_files + + +def translate_attributes(op, server_dict, operation_kwargs): + """Translate REST attributes on create to server object kwargs. + + Our REST API is relatively fixed, but internal representations + change over time, this is a translator for inbound REST request + attributes that modifies the server dict that we get and adds + appropriate attributes to ``operation_kwargs`` that will be passed + down to instance objects later. + + It's done in a common function as this is used for create / resize + / rebuild / update + + The ``op`` is the operation that we are transforming, because + there are times when we translate differently for different + operations. (Yes, it's a little nuts, but legacy... ) + + The ``server_dict`` is a representation of the server in + question. During ``create`` and ``update`` operations this will + actually be the ``server`` element of the request body. + + During actions, such as ``rebuild`` and ``resize`` this will be + the attributes passed to the action object during the + operation. This is equivalent to the ``server`` object. + + Not all operations support all attributes listed here. Which is + why it's important to only set operation_kwargs if there is + something to set. Input validation will ensure that we are only + operating on appropriate attributes for each operation. + + """ + # Disk config + auto_disk_config_raw = server_dict.pop(API_DISK_CONFIG, None) + if auto_disk_config_raw is not None: + auto_disk_config = disk_config_from_api(auto_disk_config_raw) + operation_kwargs['auto_disk_config'] = auto_disk_config + + if API_ACCESS_V4 in server_dict: + operation_kwargs['access_ip_v4'] = server_dict.pop(API_ACCESS_V4) + if API_ACCESS_V6 in server_dict: + operation_kwargs['access_ip_v6'] = server_dict.pop(API_ACCESS_V6) + + # This is only ever expected during rebuild operations, and only + # does anything with Ironic driver. It also demonstrates the lack + # of understanding of the word ephemeral. + if 'preserve_ephemeral' in server_dict and op == REBUILD: + preserve = strutils.bool_from_string( + server_dict.pop('preserve_ephemeral'), strict=True) + operation_kwargs['preserve_ephemeral'] = preserve + + # yes, we use different kwargs, this goes all the way back to + # commit cebc98176926f57016a508d5c59b11f55dfcf2b3. + if 'personality' in server_dict: + if op == REBUILD: + operation_kwargs['files_to_inject'] = get_injected_files( + server_dict.pop('personality')) + # NOTE(sdague): the deprecated hooks infrastructure doesn't + # function if injected files is not defined as a list. Once hooks + # are removed, this should go back inside the personality + # conditional above. + if op == CREATE: + operation_kwargs['injected_files'] = get_injected_files( + server_dict.pop('personality', [])) diff --git a/nova/api/openstack/compute/hide_server_addresses.py b/nova/api/openstack/compute/hide_server_addresses.py index 07f23599dfd..b6e837710aa 100644 --- a/nova/api/openstack/compute/hide_server_addresses.py +++ b/nova/api/openstack/compute/hide_server_addresses.py @@ -15,30 +15,19 @@ """Extension for hiding server addresses in certain states.""" -from oslo_config import cfg - -from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.compute import vm_states - -opts = [ - cfg.ListOpt('osapi_hide_server_address_states', - default=[vm_states.BUILDING], - help='List of instance states that should hide network info'), -] +import nova.conf +from nova.policies import hide_server_addresses as hsa_policies -CONF = cfg.CONF -CONF.register_opts(opts) - -ALIAS = 'os-hide-server-addresses' -authorize = extensions.os_compute_soft_authorizer(ALIAS) +CONF = nova.conf.CONF class Controller(wsgi.Controller): def __init__(self, *args, **kwargs): super(Controller, self).__init__(*args, **kwargs) - hidden_states = CONF.osapi_hide_server_address_states + hidden_states = CONF.api.hide_server_address_states # NOTE(jkoelker) _ is not considered uppercase ;) valid_vm_states = [getattr(vm_states, state) @@ -55,34 +44,23 @@ def _perhaps_hide_addresses(self, instance, resp_server): @wsgi.extends def show(self, req, resp_obj, id): resp = resp_obj - if not authorize(req.environ['nova.context']): + context = req.environ['nova.context'] + if not context.can(hsa_policies.BASE_POLICY_NAME, fatal=False): return if 'server' in resp.obj and 'addresses' in resp.obj['server']: - instance = req.get_db_instance(id) - self._perhaps_hide_addresses(instance, resp.obj['server']) + resp_server = resp.obj['server'] + instance = req.get_db_instance(resp_server['id']) + self._perhaps_hide_addresses(instance, resp_server) @wsgi.extends def detail(self, req, resp_obj): resp = resp_obj - if not authorize(req.environ['nova.context']): + context = req.environ['nova.context'] + if not context.can(hsa_policies.BASE_POLICY_NAME, fatal=False): return for server in list(resp.obj['servers']): if 'addresses' in server: instance = req.get_db_instance(server['id']) self._perhaps_hide_addresses(instance, server) - - -class HideServerAddresses(extensions.V21APIExtensionBase): - """Support hiding server addresses in certain states.""" - - name = 'HideServerAddresses' - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - return [extensions.ControllerExtension(self, 'servers', Controller())] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/hosts.py b/nova/api/openstack/compute/hosts.py index 8ea750449e6..ba5fac3db85 100644 --- a/nova/api/openstack/compute/hosts.py +++ b/nova/api/openstack/compute/hosts.py @@ -25,13 +25,12 @@ from nova.api.openstack import wsgi from nova.api import validation from nova import compute +from nova import context as nova_context from nova import exception -from nova.i18n import _LI from nova import objects +from nova.policies import hosts as hosts_policies LOG = logging.getLogger(__name__) -ALIAS = 'os-hosts' -authorize = extensions.os_compute_authorizer(ALIAS) class HostController(wsgi.Controller): @@ -40,6 +39,7 @@ def __init__(self): self.api = compute.HostAPI() super(HostController, self).__init__() + @wsgi.Controller.api_version("2.1", "2.42") @extensions.expected_errors(()) def index(self, req): """Returns a dict in the format @@ -59,7 +59,7 @@ def index(self, req): | {'host_name': 'network1.host.com', | 'service': 'network', | 'zone': 'internal'}, - | {'host_name': 'netwwork2.host.com', + | {'host_name': 'network2.host.com', | 'service': 'network', | 'zone': 'internal'}, | {'host_name': 'compute1.host.com', @@ -80,13 +80,13 @@ def index(self, req): """ context = req.environ['nova.context'] - authorize(context) + context.can(hosts_policies.BASE_POLICY_NAME) filters = {'disabled': False} zone = req.GET.get('zone', None) if zone: filters['availability_zone'] = zone services = self.api.service_get_all(context, filters=filters, - set_zones=True) + set_zones=True, all_cells=True) hosts = [] api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata') for service in services: @@ -96,6 +96,7 @@ def index(self, req): 'zone': service['availability_zone']}) return {'hosts': hosts} + @wsgi.Controller.api_version("2.1", "2.42") @extensions.expected_errors((400, 404, 501)) @validation.schema(hosts.update) def update(self, req, id, body): @@ -116,7 +117,7 @@ def read_enabled(orig_val): return val == "enable" context = req.environ['nova.context'] - authorize(context) + context.can(hosts_policies.BASE_POLICY_NAME) # See what the user wants to 'update' status = body.get('status') maint_mode = body.get('maintenance_mode') @@ -138,14 +139,13 @@ def _set_host_maintenance(self, context, host_name, mode=True): """Start/Stop host maintenance window. On start, it triggers guest VMs evacuation. """ - LOG.info(_LI("Putting host %(host_name)s in maintenance mode " - "%(mode)s."), - {'host_name': host_name, 'mode': mode}) + LOG.info("Putting host %(host_name)s in maintenance mode %(mode)s.", + {'host_name': host_name, 'mode': mode}) try: result = self.api.set_host_maintenance(context, host_name, mode) except NotImplementedError: common.raise_feature_not_supported() - except exception.HostNotFound as e: + except (exception.HostNotFound, exception.HostMappingNotFound) as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) except exception.ComputeServiceUnavailable as e: raise webob.exc.HTTPBadRequest(explanation=e.format_message()) @@ -159,15 +159,14 @@ def _set_enabled_status(self, context, host_name, enabled): on the host. """ if enabled: - LOG.info(_LI("Enabling host %s."), host_name) + LOG.info("Enabling host %s.", host_name) else: - LOG.info(_LI("Disabling host %s."), host_name) + LOG.info("Disabling host %s.", host_name) try: - result = self.api.set_host_enabled(context, host_name=host_name, - enabled=enabled) + result = self.api.set_host_enabled(context, host_name, enabled) except NotImplementedError: common.raise_feature_not_supported() - except exception.HostNotFound as e: + except (exception.HostNotFound, exception.HostMappingNotFound) as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) except exception.ComputeServiceUnavailable as e: raise webob.exc.HTTPBadRequest(explanation=e.format_message()) @@ -178,26 +177,28 @@ def _set_enabled_status(self, context, host_name, enabled): def _host_power_action(self, req, host_name, action): """Reboots, shuts down or powers up the host.""" context = req.environ['nova.context'] - authorize(context) + context.can(hosts_policies.BASE_POLICY_NAME) try: - result = self.api.host_power_action(context, host_name=host_name, - action=action) + result = self.api.host_power_action(context, host_name, action) except NotImplementedError: common.raise_feature_not_supported() - except exception.HostNotFound as e: + except (exception.HostNotFound, exception.HostMappingNotFound) as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) except exception.ComputeServiceUnavailable as e: raise webob.exc.HTTPBadRequest(explanation=e.format_message()) return {"host": host_name, "power_action": result} + @wsgi.Controller.api_version("2.1", "2.42") @extensions.expected_errors((400, 404, 501)) def startup(self, req, id): return self._host_power_action(req, host_name=id, action="startup") + @wsgi.Controller.api_version("2.1", "2.42") @extensions.expected_errors((400, 404, 501)) def shutdown(self, req, id): return self._host_power_action(req, host_name=id, action="shutdown") + @wsgi.Controller.api_version("2.1", "2.42") @extensions.expected_errors((400, 404, 501)) def reboot(self, req, id): return self._host_power_action(req, host_name=id, action="reboot") @@ -206,17 +207,17 @@ def reboot(self, req, id): def _get_total_resources(host_name, compute_node): return {'resource': {'host': host_name, 'project': '(total)', - 'cpu': compute_node['vcpus'], - 'memory_mb': compute_node['memory_mb'], - 'disk_gb': compute_node['local_gb']}} + 'cpu': compute_node.vcpus, + 'memory_mb': compute_node.memory_mb, + 'disk_gb': compute_node.local_gb}} @staticmethod def _get_used_now_resources(host_name, compute_node): return {'resource': {'host': host_name, 'project': '(used_now)', - 'cpu': compute_node['vcpus_used'], - 'memory_mb': compute_node['memory_mb_used'], - 'disk_gb': compute_node['local_gb_used']}} + 'cpu': compute_node.vcpus_used, + 'memory_mb': compute_node.memory_mb_used, + 'disk_gb': compute_node.local_gb_used}} @staticmethod def _get_resource_totals_from_instances(host_name, instances): @@ -251,6 +252,7 @@ def _get_resources_by_project(host_name, instances): instance['ephemeral_gb']) return project_map + @wsgi.Controller.api_version("2.1", "2.42") @extensions.expected_errors(404) def show(self, req, id): """Shows the physical/usage resource given by hosts. @@ -264,15 +266,18 @@ def show(self, req, id): 'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30} """ context = req.environ['nova.context'] - authorize(context) + context.can(hosts_policies.BASE_POLICY_NAME) host_name = id try: + mapping = objects.HostMapping.get_by_host(context, host_name) + nova_context.set_target_cell(context, mapping.cell_mapping) compute_node = ( objects.ComputeNode.get_first_node_by_host_for_old_compat( context, host_name)) - except exception.ComputeHostNotFound as e: + instances = self.api.instance_get_all_by_host(context, host_name) + except (exception.ComputeHostNotFound, + exception.HostMappingNotFound) as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) - instances = self.api.instance_get_all_by_host(context, host_name) resources = [self._get_total_resources(host_name, compute_node)] resources.append(self._get_used_now_resources(host_name, compute_node)) @@ -283,21 +288,3 @@ def show(self, req, id): for resource in six.itervalues(by_proj_resources): resources.append({'resource': resource}) return {'host': resources} - - -class Hosts(extensions.V21APIExtensionBase): - """Admin-only host administration.""" - - name = "Hosts" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [extensions.ResourceExtension(ALIAS, - HostController(), - member_actions={"startup": "GET", "shutdown": "GET", - "reboot": "GET"})] - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/hypervisors.py b/nova/api/openstack/compute/hypervisors.py index c9a0af86472..1bf20700792 100644 --- a/nova/api/openstack/compute/hypervisors.py +++ b/nova/api/openstack/compute/hypervisors.py @@ -15,55 +15,79 @@ """The hypervisors admin extension.""" +from oslo_log import log as logging +from oslo_serialization import jsonutils +from oslo_utils import strutils +from oslo_utils import uuidutils import webob.exc +from nova.api.openstack import api_version_request from nova.api.openstack import common +from nova.api.openstack.compute.schemas import hypervisors as hyper_schema +from nova.api.openstack.compute.views import hypervisors as hyper_view from nova.api.openstack import extensions from nova.api.openstack import wsgi +from nova.api import validation +from nova.cells import utils as cells_utils from nova import compute from nova import exception from nova.i18n import _ +from nova.policies import hypervisors as hv_policies from nova import servicegroup +from nova import utils +LOG = logging.getLogger(__name__) -ALIAS = "os-hypervisors" -authorize = extensions.os_compute_authorizer(ALIAS) +UUID_FOR_ID_MIN_VERSION = '2.53' class HypervisorsController(wsgi.Controller): """The Hypervisors API controller for the OpenStack API.""" + _view_builder_class = hyper_view.ViewBuilder + def __init__(self): self.host_api = compute.HostAPI() self.servicegroup_api = servicegroup.API() super(HypervisorsController, self).__init__() - def _view_hypervisor(self, hypervisor, service, detail, servers=None, + def _view_hypervisor(self, hypervisor, service, detail, req, servers=None, **kwargs): alive = self.servicegroup_api.service_is_up(service) + # The 2.53 microversion returns the compute node uuid rather than id. + uuid_for_id = api_version_request.is_supported( + req, min_version=UUID_FOR_ID_MIN_VERSION) hyp_dict = { - 'id': hypervisor.id, + 'id': hypervisor.uuid if uuid_for_id else hypervisor.id, 'hypervisor_hostname': hypervisor.hypervisor_hostname, 'state': 'up' if alive else 'down', 'status': ('disabled' if service.disabled else 'enabled'), } - if detail and not servers: + if detail: for field in ('vcpus', 'memory_mb', 'local_gb', 'vcpus_used', 'memory_mb_used', 'local_gb_used', 'hypervisor_type', 'hypervisor_version', 'free_ram_mb', 'free_disk_gb', 'current_workload', - 'running_vms', 'cpu_info', 'disk_available_least', - 'host_ip'): - hyp_dict[field] = hypervisor[field] + 'running_vms', 'disk_available_least', 'host_ip'): + hyp_dict[field] = getattr(hypervisor, field) + service_id = service.uuid if uuid_for_id else service.id hyp_dict['service'] = { - 'id': service.id, + 'id': service_id, 'host': hypervisor.host, 'disabled_reason': service.disabled_reason, } + if api_version_request.is_supported(req, min_version='2.28'): + if hypervisor.cpu_info: + hyp_dict['cpu_info'] = jsonutils.loads(hypervisor.cpu_info) + else: + hyp_dict['cpu_info'] = {} + else: + hyp_dict['cpu_info'] = hypervisor.cpu_info + if servers: hyp_dict['servers'] = [dict(name=serv['name'], uuid=serv['uuid']) for serv in servers] @@ -74,53 +98,236 @@ def _view_hypervisor(self, hypervisor, service, detail, servers=None, return hyp_dict + def _get_compute_nodes_by_name_pattern(self, context, hostname_match): + compute_nodes = self.host_api.compute_node_search_by_hypervisor( + context, hostname_match) + if not compute_nodes: + msg = (_("No hypervisor matching '%s' could be found.") % + hostname_match) + raise webob.exc.HTTPNotFound(explanation=msg) + return compute_nodes + + def _get_hypervisors(self, req, detail=False, limit=None, marker=None, + links=False): + """Get hypervisors for the given request. + + :param req: nova.api.openstack.wsgi.Request for the GET request + :param detail: If True, return a detailed response. + :param limit: An optional user-supplied page limit. + :param marker: An optional user-supplied marker for paging. + :param links: If True, return links in the response for paging. + """ + context = req.environ['nova.context'] + context.can(hv_policies.BASE_POLICY_NAME) + + # The 2.53 microversion moves the search and servers routes into + # GET /os-hypervisors and GET /os-hypervisors/detail with query + # parameters. + if api_version_request.is_supported( + req, min_version=UUID_FOR_ID_MIN_VERSION): + hypervisor_match = req.GET.get('hypervisor_hostname_pattern') + with_servers = strutils.bool_from_string( + req.GET.get('with_servers', False), strict=True) + else: + hypervisor_match = None + with_servers = False + + if hypervisor_match is not None: + # We have to check for 'limit' in the request itself because + # the limit passed in is CONF.api.max_limit by default. + if 'limit' in req.GET or marker: + # Paging with hostname pattern isn't supported. + raise webob.exc.HTTPBadRequest( + _('Paging over hypervisors with the ' + 'hypervisor_hostname_pattern query parameter is not ' + 'supported.')) + + # Explicitly do not try to generate links when querying with the + # hostname pattern since the request in the link would fail the + # check above. + links = False + + # Get all compute nodes with a hypervisor_hostname that matches + # the given pattern. If none are found then it's a 404 error. + compute_nodes = self._get_compute_nodes_by_name_pattern( + context, hypervisor_match) + else: + # Get all compute nodes. + try: + compute_nodes = self.host_api.compute_node_get_all( + context, limit=limit, marker=marker) + except exception.MarkerNotFound: + msg = _('marker [%s] not found') % marker + raise webob.exc.HTTPBadRequest(explanation=msg) + + hypervisors_list = [] + for hyp in compute_nodes: + try: + instances = None + if with_servers: + instances = self.host_api.instance_get_all_by_host( + context, hyp.host) + service = self.host_api.service_get_by_compute_host( + context, hyp.host) + hypervisors_list.append( + self._view_hypervisor( + hyp, service, detail, req, servers=instances)) + except (exception.ComputeHostNotFound, + exception.HostMappingNotFound): + # The compute service could be deleted which doesn't delete + # the compute node record, that has to be manually removed + # from the database so we just ignore it when listing nodes. + LOG.debug('Unable to find service for compute node %s. The ' + 'service may be deleted and compute nodes need to ' + 'be manually cleaned up.', hyp.host) + + hypervisors_dict = dict(hypervisors=hypervisors_list) + if links: + hypervisors_links = self._view_builder.get_links( + req, hypervisors_list, detail) + if hypervisors_links: + hypervisors_dict['hypervisors_links'] = hypervisors_links + return hypervisors_dict + + @wsgi.Controller.api_version(UUID_FOR_ID_MIN_VERSION) + @validation.query_schema(hyper_schema.list_query_schema_v253, + UUID_FOR_ID_MIN_VERSION) + @extensions.expected_errors((400, 404)) + def index(self, req): + """Starting with the 2.53 microversion, the id field in the response + is the compute_nodes.uuid value. Also, the search and servers routes + are superseded and replaced with query parameters for listing + hypervisors by a hostname pattern and whether or not to include + hosted servers in the response. + """ + limit, marker = common.get_limit_and_marker(req) + return self._index(req, limit=limit, marker=marker, links=True) + + @wsgi.Controller.api_version("2.33", "2.52") # noqa + @extensions.expected_errors((400)) + def index(self, req): + limit, marker = common.get_limit_and_marker(req) + return self._index(req, limit=limit, marker=marker, links=True) + + @wsgi.Controller.api_version("2.1", "2.32") # noqa @extensions.expected_errors(()) def index(self, req): - context = req.environ['nova.context'] - authorize(context) - compute_nodes = self.host_api.compute_node_get_all(context) - req.cache_db_compute_nodes(compute_nodes) - return dict(hypervisors=[self._view_hypervisor( - hyp, - self.host_api.service_get_by_compute_host( - context, hyp.host), - False) - for hyp in compute_nodes]) + return self._index(req) + def _index(self, req, limit=None, marker=None, links=False): + return self._get_hypervisors(req, detail=False, limit=limit, + marker=marker, links=links) + + @wsgi.Controller.api_version(UUID_FOR_ID_MIN_VERSION) + @validation.query_schema(hyper_schema.list_query_schema_v253, + UUID_FOR_ID_MIN_VERSION) + @extensions.expected_errors((400, 404)) + def detail(self, req): + """Starting with the 2.53 microversion, the id field in the response + is the compute_nodes.uuid value. Also, the search and servers routes + are superseded and replaced with query parameters for listing + hypervisors by a hostname pattern and whether or not to include + hosted servers in the response. + """ + limit, marker = common.get_limit_and_marker(req) + return self._detail(req, limit=limit, marker=marker, links=True) + + @wsgi.Controller.api_version("2.33", "2.52") # noqa + @extensions.expected_errors((400)) + def detail(self, req): + limit, marker = common.get_limit_and_marker(req) + return self._detail(req, limit=limit, marker=marker, links=True) + + @wsgi.Controller.api_version("2.1", "2.32") # noqa @extensions.expected_errors(()) def detail(self, req): - context = req.environ['nova.context'] - authorize(context) - compute_nodes = self.host_api.compute_node_get_all(context) - req.cache_db_compute_nodes(compute_nodes) - return dict(hypervisors=[self._view_hypervisor( - hyp, - self.host_api.service_get_by_compute_host( - context, hyp.host), - True) - for hyp in compute_nodes]) + return self._detail(req) + + def _detail(self, req, limit=None, marker=None, links=False): + return self._get_hypervisors(req, detail=True, limit=limit, + marker=marker, links=links) + @staticmethod + def _validate_id(req, hypervisor_id): + """Validates that the id is a uuid for microversions that require it. + + :param req: The HTTP request object which contains the requested + microversion information. + :param hypervisor_id: The provided hypervisor id. + :raises: webob.exc.HTTPBadRequest if the requested microversion is + greater than or equal to 2.53 and the id is not a uuid. + :raises: webob.exc.HTTPNotFound if the requested microversion is + less than 2.53 and the id is not an integer. + """ + expect_uuid = api_version_request.is_supported( + req, min_version=UUID_FOR_ID_MIN_VERSION) + if expect_uuid: + if not uuidutils.is_uuid_like(hypervisor_id): + msg = _('Invalid uuid %s') % hypervisor_id + raise webob.exc.HTTPBadRequest(explanation=msg) + else: + # This API is supported for cells v1 and as such the id can be + # a cell v1 delimited string, so we have to parse it first. + if cells_utils.CELL_ITEM_SEP in str(hypervisor_id): + hypervisor_id = cells_utils.split_cell_and_item( + hypervisor_id)[1] + try: + utils.validate_integer(hypervisor_id, 'id') + except exception.InvalidInput: + msg = (_("Hypervisor with ID '%s' could not be found.") % + hypervisor_id) + raise webob.exc.HTTPNotFound(explanation=msg) + + @wsgi.Controller.api_version(UUID_FOR_ID_MIN_VERSION) + @validation.query_schema(hyper_schema.show_query_schema_v253, + UUID_FOR_ID_MIN_VERSION) + @extensions.expected_errors((400, 404)) + def show(self, req, id): + """The 2.53 microversion requires that the id is a uuid and as a result + it can also return a 400 response if an invalid uuid is passed. + + The 2.53 microversion also supports the with_servers query parameter + to include a list of servers on the given hypervisor if requested. + """ + with_servers = strutils.bool_from_string( + req.GET.get('with_servers', False), strict=True) + return self._show(req, id, with_servers) + + @wsgi.Controller.api_version("2.1", "2.52") # noqa F811 @extensions.expected_errors(404) def show(self, req, id): + return self._show(req, id) + + def _show(self, req, id, with_servers=False): context = req.environ['nova.context'] - authorize(context) + context.can(hv_policies.BASE_POLICY_NAME) + + self._validate_id(req, id) + try: hyp = self.host_api.compute_node_get(context, id) - req.cache_db_compute_node(hyp) - except (ValueError, exception.ComputeHostNotFound): + instances = None + if with_servers: + instances = self.host_api.instance_get_all_by_host( + context, hyp.host) + service = self.host_api.service_get_by_compute_host( + context, hyp.host) + except (ValueError, exception.ComputeHostNotFound, + exception.HostMappingNotFound): msg = _("Hypervisor with ID '%s' could not be found.") % id raise webob.exc.HTTPNotFound(explanation=msg) - service = self.host_api.service_get_by_compute_host( - context, hyp.host) - return dict(hypervisor=self._view_hypervisor(hyp, service, True)) + return dict(hypervisor=self._view_hypervisor( + hyp, service, True, req, instances)) - @extensions.expected_errors((404, 501)) + @extensions.expected_errors((400, 404, 501)) def uptime(self, req, id): context = req.environ['nova.context'] - authorize(context) + context.can(hv_policies.BASE_POLICY_NAME) + + self._validate_id(req, id) + try: hyp = self.host_api.compute_node_get(context, id) - req.cache_db_compute_node(hyp) except (ValueError, exception.ComputeHostNotFound): msg = _("Hypervisor with ID '%s' could not be found.") % id raise webob.exc.HTTPNotFound(explanation=msg) @@ -129,46 +336,66 @@ def uptime(self, req, id): try: host = hyp.host uptime = self.host_api.get_host_uptime(context, host) + service = self.host_api.service_get_by_compute_host(context, host) except NotImplementedError: common.raise_feature_not_supported() + except exception.ComputeServiceUnavailable as e: + raise webob.exc.HTTPBadRequest(explanation=e.format_message()) + except exception.HostMappingNotFound: + # NOTE(danms): This mirrors the compute_node_get() behavior + # where the node is missing, resulting in NotFound instead of + # BadRequest if we fail on the map lookup. + msg = _("Hypervisor with ID '%s' could not be found.") % id + raise webob.exc.HTTPNotFound(explanation=msg) - service = self.host_api.service_get_by_compute_host(context, host) - return dict(hypervisor=self._view_hypervisor(hyp, service, False, + return dict(hypervisor=self._view_hypervisor(hyp, service, False, req, uptime=uptime)) + @wsgi.Controller.api_version('2.1', '2.52') @extensions.expected_errors(404) def search(self, req, id): + """Prior to microversion 2.53 you could search for hypervisors by a + hostname pattern on a dedicated route. Starting with 2.53, searching + by a hostname pattern is a query parameter in the GET /os-hypervisors + index and detail methods. + """ context = req.environ['nova.context'] - authorize(context) - hypervisors = self.host_api.compute_node_search_by_hypervisor( - context, id) - if hypervisors: - return dict(hypervisors=[self._view_hypervisor( - hyp, - self.host_api.service_get_by_compute_host( - context, hyp.host), - False) - for hyp in hypervisors]) - else: + context.can(hv_policies.BASE_POLICY_NAME) + hypervisors = self._get_compute_nodes_by_name_pattern(context, id) + try: + return dict(hypervisors=[ + self._view_hypervisor( + hyp, + self.host_api.service_get_by_compute_host(context, + hyp.host), + False, req) + for hyp in hypervisors]) + except exception.HostMappingNotFound: msg = _("No hypervisor matching '%s' could be found.") % id raise webob.exc.HTTPNotFound(explanation=msg) + @wsgi.Controller.api_version('2.1', '2.52') @extensions.expected_errors(404) def servers(self, req, id): + """Prior to microversion 2.53 you could search for hypervisors by a + hostname pattern and include servers on those hosts in the response on + a dedicated route. Starting with 2.53, searching by a hostname pattern + and including hosted servers is a query parameter in the + GET /os-hypervisors index and detail methods. + """ context = req.environ['nova.context'] - authorize(context) - compute_nodes = self.host_api.compute_node_search_by_hypervisor( - context, id) - if not compute_nodes: - msg = _("No hypervisor matching '%s' could be found.") % id - raise webob.exc.HTTPNotFound(explanation=msg) + context.can(hv_policies.BASE_POLICY_NAME) + compute_nodes = self._get_compute_nodes_by_name_pattern(context, id) hypervisors = [] for compute_node in compute_nodes: - instances = self.host_api.instance_get_all_by_host(context, + try: + instances = self.host_api.instance_get_all_by_host(context, compute_node.host) - service = self.host_api.service_get_by_compute_host( - context, compute_node.host) - hyp = self._view_hypervisor(compute_node, service, False, + service = self.host_api.service_get_by_compute_host( + context, compute_node.host) + except exception.HostMappingNotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + hyp = self._view_hypervisor(compute_node, service, False, req, instances) hypervisors.append(hyp) return dict(hypervisors=hypervisors) @@ -176,28 +403,6 @@ def servers(self, req, id): @extensions.expected_errors(()) def statistics(self, req): context = req.environ['nova.context'] - authorize(context) + context.can(hv_policies.BASE_POLICY_NAME) stats = self.host_api.compute_node_statistics(context) return dict(hypervisor_statistics=stats) - - -class Hypervisors(extensions.V21APIExtensionBase): - """Admin-only hypervisor administration.""" - - name = "Hypervisors" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [extensions.ResourceExtension(ALIAS, - HypervisorsController(), - collection_actions={'detail': 'GET', - 'statistics': 'GET'}, - member_actions={'uptime': 'GET', - 'search': 'GET', - 'servers': 'GET'})] - - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/image_metadata.py b/nova/api/openstack/compute/image_metadata.py index f9b0f43bb02..b66e82dd22f 100644 --- a/nova/api/openstack/compute/image_metadata.py +++ b/nova/api/openstack/compute/image_metadata.py @@ -13,9 +13,11 @@ # License for the specific language governing permissions and limitations # under the License. -import six + from webob import exc +from nova.api.openstack.api_version_request import \ + MAX_IMAGE_META_PROXY_API_VERSION from nova.api.openstack import common from nova.api.openstack.compute.schemas import image_metadata from nova.api.openstack import extensions @@ -25,8 +27,6 @@ from nova.i18n import _ import nova.image -ALIAS = 'image-metadata' - class ImageMetadataController(wsgi.Controller): """The image metadata API controller for the OpenStack API.""" @@ -43,6 +43,7 @@ def _get_image(self, context, image_id): msg = _("Image not found.") raise exc.HTTPNotFound(explanation=msg) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) @extensions.expected_errors((403, 404)) def index(self, req, image_id): """Returns the list of metadata for a given instance.""" @@ -50,6 +51,7 @@ def index(self, req, image_id): metadata = self._get_image(context, image_id)['properties'] return dict(metadata=metadata) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) @extensions.expected_errors((403, 404)) def show(self, req, image_id, id): context = req.environ['nova.context'] @@ -59,12 +61,13 @@ def show(self, req, image_id, id): else: raise exc.HTTPNotFound() - @extensions.expected_errors((400, 403, 404, 413)) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) + @extensions.expected_errors((400, 403, 404)) @validation.schema(image_metadata.create) def create(self, req, image_id, body): context = req.environ['nova.context'] image = self._get_image(context, image_id) - for key, value in six.iteritems(body['metadata']): + for key, value in body['metadata'].items(): image['properties'][key] = value common.check_img_metadata_properties_quota(context, image['properties']) @@ -75,7 +78,8 @@ def create(self, req, image_id, body): raise exc.HTTPForbidden(explanation=e.format_message()) return dict(metadata=image['properties']) - @extensions.expected_errors((400, 403, 404, 413)) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) + @extensions.expected_errors((400, 403, 404)) @validation.schema(image_metadata.update) def update(self, req, image_id, id, body): context = req.environ['nova.context'] @@ -97,7 +101,8 @@ def update(self, req, image_id, id, body): raise exc.HTTPForbidden(explanation=e.format_message()) return dict(meta=meta) - @extensions.expected_errors((400, 403, 404, 413)) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) + @extensions.expected_errors((400, 403, 404)) @validation.schema(image_metadata.update_all) def update_all(self, req, image_id, body): context = req.environ['nova.context'] @@ -112,6 +117,7 @@ def update_all(self, req, image_id, body): raise exc.HTTPForbidden(explanation=e.format_message()) return dict(metadata=metadata) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) @extensions.expected_errors((403, 404)) @wsgi.response(204) def delete(self, req, image_id, id): @@ -126,36 +132,3 @@ def delete(self, req, image_id, id): purge_props=True) except exception.ImageNotAuthorized as e: raise exc.HTTPForbidden(explanation=e.format_message()) - - -class ImageMetadata(extensions.V21APIExtensionBase): - """Image Metadata API.""" - name = "ImageMetadata" - alias = ALIAS - version = 1 - - def get_resources(self): - parent = {'member_name': 'image', - 'collection_name': 'images'} - resources = [extensions.ResourceExtension('metadata', - ImageMetadataController(), - member_name='image_meta', - parent=parent, - custom_routes_fn= - self.image_metadata_map - )] - return resources - - def get_controller_extensions(self): - return [] - - def image_metadata_map(self, mapper, wsgi_resource): - mapper.connect("metadata", - "/{project_id}/images/{image_id}/metadata", - controller=wsgi_resource, - action='update_all', conditions={"method": ['PUT']}) - # Also connect the non project_id route - mapper.connect("metadata", - "/images/{image_id}/metadata", - controller=wsgi_resource, - action='update_all', conditions={"method": ['PUT']}) diff --git a/nova/api/openstack/compute/image_size.py b/nova/api/openstack/compute/image_size.py index f76b874b3d8..8ed4ae6135f 100644 --- a/nova/api/openstack/compute/image_size.py +++ b/nova/api/openstack/compute/image_size.py @@ -13,12 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.api.openstack import extensions from nova.api.openstack import wsgi - -ALIAS = "image-size" - -authorize = extensions.os_compute_soft_authorizer(ALIAS) +from nova.policies import image_size as is_policies class ImageSizeController(wsgi.Controller): @@ -33,7 +29,7 @@ def _extend_image(self, image, image_cache): @wsgi.extends def show(self, req, resp_obj, id): context = req.environ["nova.context"] - if authorize(context): + if context.can(is_policies.BASE_POLICY_NAME, fatal=False): image_resp = resp_obj.obj['image'] # image guaranteed to be in the cache due to the core API adding # it in its 'show' method @@ -43,26 +39,10 @@ def show(self, req, resp_obj, id): @wsgi.extends def detail(self, req, resp_obj): context = req.environ['nova.context'] - if authorize(context): + if context.can(is_policies.BASE_POLICY_NAME, fatal=False): images_resp = list(resp_obj.obj['images']) # images guaranteed to be in the cache due to the core API adding # it in its 'detail' method for image in images_resp: image_cached = req.get_db_item('images', image['id']) self._extend_image(image, image_cached) - - -class ImageSize(extensions.V21APIExtensionBase): - """Adds image size to image listings.""" - - name = "ImageSize" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = ImageSizeController() - extension = extensions.ControllerExtension(self, 'images', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/images.py b/nova/api/openstack/compute/images.py index ecb2046d896..44c5e0c5fc9 100644 --- a/nova/api/openstack/compute/images.py +++ b/nova/api/openstack/compute/images.py @@ -15,6 +15,8 @@ import webob.exc +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import common from nova.api.openstack.compute.views import images as views_images from nova.api.openstack import extensions @@ -25,8 +27,6 @@ import nova.utils -ALIAS = 'images' - SUPPORTED_FILTERS = { 'name': 'name', 'status': 'status', @@ -74,6 +74,7 @@ def _get_filters(self, req): return filters + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(404) def show(self, req, id): """Return detailed information about a specific image. @@ -92,6 +93,7 @@ def show(self, req, id): req.cache_db_items('images', [image], 'id') return self._view_builder.show(req, image) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((403, 404)) @wsgi.response(204) def delete(self, req, id): @@ -112,6 +114,7 @@ def delete(self, req, id): explanation = _("You are not allowed to delete the image.") raise webob.exc.HTTPForbidden(explanation=explanation) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(400) def index(self, req): """Return an index listing of images available to the request. @@ -130,6 +133,7 @@ def index(self, req): raise webob.exc.HTTPBadRequest(explanation=e.format_message()) return self._view_builder.index(req, images) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(400) def detail(self, req): """Return a detailed index listing of images available to the request. @@ -148,22 +152,3 @@ def detail(self, req): req.cache_db_items('images', images, 'id') return self._view_builder.detail(req, images) - - -class Images(extensions.V21APIExtensionBase): - """Proxying API for Images.""" - - name = "Images" - alias = ALIAS - version = 1 - - def get_resources(self): - coll_actions = {'detail': 'GET'} - resource = extensions.ResourceExtension(ALIAS, - ImagesController(), - collection_actions=coll_actions) - - return [resource] - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/instance_actions.py b/nova/api/openstack/compute/instance_actions.py index a84c5b1c0e6..7f2ff0df9a1 100644 --- a/nova/api/openstack/compute/instance_actions.py +++ b/nova/api/openstack/compute/instance_actions.py @@ -15,17 +15,15 @@ from webob import exc +from nova.api.openstack import api_version_request from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute from nova.i18n import _ +from nova.policies import instance_actions as ia_policies from nova import utils -ALIAS = "os-instance-actions" -authorize = extensions.os_compute_authorizer(ALIAS) -soft_authorize = extensions.os_compute_soft_authorizer(ALIAS) - ACTION_KEYS = ['action', 'instance_uuid', 'request_id', 'user_id', 'project_id', 'start_time', 'message'] EVENT_KEYS = ['event', 'start_time', 'finish_time', 'result', 'traceback'] @@ -44,9 +42,12 @@ def _format_action(self, action_raw): action[key] = action_raw.get(key) return action - def _format_event(self, event_raw): + def _format_event(self, event_raw, show_traceback=False): event = {} for key in EVENT_KEYS: + # By default, non-admins are not allowed to see traceback details. + if key == 'traceback' and not show_traceback: + continue event[key] = event_raw.get(key) return event @@ -64,7 +65,7 @@ def index(self, req, server_id): """Returns the list of actions recorded for a given instance.""" context = req.environ["nova.context"] instance = self._get_instance(req, context, server_id) - authorize(context, target=instance) + context.can(ia_policies.BASE_POLICY_NAME, instance) actions_raw = self.action_api.actions_get(context, instance) actions = [self._format_action(action) for action in actions_raw] return {'instanceActions': actions} @@ -74,7 +75,7 @@ def show(self, req, server_id, id): """Return data about the given instance action.""" context = req.environ['nova.context'] instance = self._get_instance(req, context, server_id) - authorize(context, target=instance) + context.can(ia_policies.BASE_POLICY_NAME, instance) action = self.action_api.action_get_by_request_id(context, instance, id) if action is None: @@ -83,30 +84,25 @@ def show(self, req, server_id, id): action_id = action['id'] action = self._format_action(action) - if soft_authorize(context, action='events'): + # Prior to microversion 2.51, events would only be returned in the + # response for admins by default policy rules. Starting in + # microversion 2.51, events are returned for admin_or_owner (of the + # instance) but the "traceback" field is only shown for admin users + # by default. + show_events = False + show_traceback = False + if context.can(ia_policies.POLICY_ROOT % 'events', fatal=False): + # For all microversions, the user can see all event details + # including the traceback. + show_events = show_traceback = True + elif api_version_request.is_supported(req, '2.51'): + # The user is not able to see all event details, but they can at + # least see the non-traceback event details. + show_events = True + + if show_events: events_raw = self.action_api.action_events_get(context, instance, action_id) - action['events'] = [self._format_event(evt) for evt in events_raw] + action['events'] = [self._format_event(evt, show_traceback) + for evt in events_raw] return {'instanceAction': action} - - -class InstanceActions(extensions.V21APIExtensionBase): - """View a log of actions and events taken on an instance.""" - - name = "InstanceActions" - alias = ALIAS - version = 1 - - def get_resources(self): - ext = extensions.ResourceExtension(ALIAS, - InstanceActionsController(), - parent=dict( - member_name='server', - collection_name='servers')) - return [ext] - - def get_controller_extensions(self): - """It's an abstract function V21APIExtensionBase and the extension - will not be loaded without it. - """ - return [] diff --git a/nova/api/openstack/compute/instance_usage_audit_log.py b/nova/api/openstack/compute/instance_usage_audit_log.py index 130f0466233..2eb9ecea638 100644 --- a/nova/api/openstack/compute/instance_usage_audit_log.py +++ b/nova/api/openstack/compute/instance_usage_audit_log.py @@ -16,21 +16,18 @@ import datetime -from oslo_config import cfg import webob.exc from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute +from nova.compute import rpcapi as compute_rpcapi +import nova.conf from nova.i18n import _ +from nova.policies import instance_usage_audit_log as iual_policies from nova import utils -CONF = cfg.CONF -CONF.import_opt('compute_topic', 'nova.compute.rpcapi') - - -ALIAS = 'os-instance-usage-audit-log' -authorize = extensions.os_compute_authorizer(ALIAS) +CONF = nova.conf.CONF class InstanceUsageAuditLogController(wsgi.Controller): @@ -40,14 +37,14 @@ def __init__(self): @extensions.expected_errors(()) def index(self, req): context = req.environ['nova.context'] - authorize(context) + context.can(iual_policies.BASE_POLICY_NAME) task_log = self._get_audit_task_logs(context) return {'instance_usage_audit_logs': task_log} @extensions.expected_errors(400) def show(self, req, id): context = req.environ['nova.context'] - authorize(context) + context.can(iual_policies.BASE_POLICY_NAME) try: if '.' in id: before_date = datetime.datetime.strptime(str(id), @@ -58,36 +55,25 @@ def show(self, req, id): except ValueError: msg = _("Invalid timestamp for date %s") % id raise webob.exc.HTTPBadRequest(explanation=msg) - task_log = self._get_audit_task_logs(context, - before=before_date) + task_log = self._get_audit_task_logs(context, before=before_date) return {'instance_usage_audit_log': task_log} - def _get_audit_task_logs(self, context, begin=None, end=None, - before=None): + def _get_audit_task_logs(self, context, before=None): """Returns a full log for all instance usage audit tasks on all computes. - :param begin: datetime beginning of audit period to get logs for, - Defaults to the beginning of the most recently completed - audit period prior to the 'before' date. - :param end: datetime ending of audit period to get logs for, - Defaults to the ending of the most recently completed - audit period prior to the 'before' date. + :param context: Nova request context. :param before: By default we look for the audit period most recently completed before this datetime. Has no effect if both begin and end are specified. """ - defbegin, defend = utils.last_completed_audit_period(before=before) - if begin is None: - begin = defbegin - if end is None: - end = defend + begin, end = utils.last_completed_audit_period(before=before) task_logs = self.host_api.task_log_get_all(context, "instance_usage_audit", begin, end) - # We do this this way to include disabled compute services, + # We do this in this way to include disabled compute services, # which can have instances on them. (mdragon) - filters = {'topic': CONF.compute_topic} + filters = {'topic': compute_rpcapi.RPC_TOPIC} services = self.host_api.service_get_all(context, filters=filters) hosts = set(serv['host'] for serv in services) seen_hosts = set() @@ -124,18 +110,3 @@ def _get_audit_task_logs(self, context, begin=None, end=None, total_errors=total_errors, overall_status=overall_status, log=log) - - -class InstanceUsageAuditLog(extensions.V21APIExtensionBase): - """Admin-only Task Log Monitoring.""" - name = "OSInstanceUsageAuditLog" - alias = ALIAS - version = 1 - - def get_resources(self): - ext = extensions.ResourceExtension('os-instance_usage_audit_log', - InstanceUsageAuditLogController()) - return [ext] - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/ips.py b/nova/api/openstack/compute/ips.py index 36ef7f88959..73f0f3d4f65 100644 --- a/nova/api/openstack/compute/ips.py +++ b/nova/api/openstack/compute/ips.py @@ -21,9 +21,7 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.i18n import _ - -ALIAS = 'ips' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import ips as ips_policies class IPsController(wsgi.Controller): @@ -36,12 +34,12 @@ class IPsController(wsgi.Controller): def __init__(self, **kwargs): super(IPsController, self).__init__(**kwargs) - self._compute_api = nova.compute.API(skip_policy_check=True) + self._compute_api = nova.compute.API() @extensions.expected_errors(404) def index(self, req, server_id): context = req.environ["nova.context"] - authorize(context, action='index') + context.can(ips_policies.POLICY_ROOT % 'index') instance = common.get_instance(self._compute_api, context, server_id) networks = common.get_networks_for_instance(context, instance) return self._view_builder.index(networks) @@ -49,7 +47,7 @@ def index(self, req, server_id): @extensions.expected_errors(404) def show(self, req, server_id, id): context = req.environ["nova.context"] - authorize(context, action='show') + context.can(ips_policies.POLICY_ROOT % 'show') instance = common.get_instance(self._compute_api, context, server_id) networks = common.get_networks_for_instance(context, instance) if id not in networks: @@ -57,23 +55,3 @@ def show(self, req, server_id, id): raise exc.HTTPNotFound(explanation=msg) return self._view_builder.show(networks[id], id) - - -class IPs(extensions.V21APIExtensionBase): - """Server addresses.""" - - name = "Ips" - alias = ALIAS - version = 1 - - def get_resources(self): - parent = {'member_name': 'server', - 'collection_name': 'servers'} - resources = [ - extensions.ResourceExtension( - ALIAS, IPsController(), parent=parent, member_name='ip')] - - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/keypairs.py b/nova/api/openstack/compute/keypairs.py index 7f646d11907..63a7a4e80f9 100644 --- a/nova/api/openstack/compute/keypairs.py +++ b/nova/api/openstack/compute/keypairs.py @@ -18,8 +18,10 @@ import webob import webob.exc +from nova.api.openstack import api_version_request from nova.api.openstack import common from nova.api.openstack.compute.schemas import keypairs +from nova.api.openstack.compute.views import keypairs as keypairs_view from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api import validation @@ -27,18 +29,18 @@ from nova import exception from nova.i18n import _ from nova.objects import keypair as keypair_obj - - -ALIAS = 'os-keypairs' -authorize = extensions.os_compute_authorizer(ALIAS) -soft_authorize = extensions.os_compute_soft_authorizer(ALIAS) +from nova.policies import keypairs as kp_policies class KeypairController(wsgi.Controller): """Keypair API controller for the OpenStack API.""" + + _view_builder_class = keypairs_view.ViewBuilder + def __init__(self): self.api = compute_api.KeypairAPI() + super(KeypairController, self).__init__() def _filter_keypair(self, keypair, **attrs): # TODO(claudiub): After v2 and v2.1 is no longer supported, @@ -116,9 +118,9 @@ def _create(self, req, body, user_id=None, **keypair_filters): name = common.normalize_name(params['name']) key_type = params.get('type', keypair_obj.KEYPAIR_TYPE_SSH) user_id = user_id or context.user_id - authorize(context, action='create', - target={'user_id': user_id, - 'project_id': context.project_id}) + context.can(kp_policies.POLICY_ROOT % 'create', + target={'user_id': user_id, + 'project_id': context.project_id}) try: if 'public_key' in params: @@ -145,18 +147,21 @@ def _create(self, req, body, user_id=None, **keypair_filters): raise webob.exc.HTTPConflict(explanation=exc.format_message()) @wsgi.Controller.api_version("2.1", "2.1") + @validation.query_schema(keypairs.delete_query_schema_v20) @wsgi.response(202) @extensions.expected_errors(404) def delete(self, req, id): self._delete(req, id) @wsgi.Controller.api_version("2.2", "2.9") # noqa + @validation.query_schema(keypairs.delete_query_schema_v20) @wsgi.response(204) @extensions.expected_errors(404) def delete(self, req, id): self._delete(req, id) @wsgi.Controller.api_version("2.10") # noqa + @validation.query_schema(keypairs.delete_query_schema_v210) @wsgi.response(204) @extensions.expected_errors(404) def delete(self, req, id): @@ -169,9 +174,9 @@ def _delete(self, req, id, user_id=None): context = req.environ['nova.context'] # handle optional user-id for admin only user_id = user_id or context.user_id - authorize(context, action='delete', - target={'user_id': user_id, - 'project_id': context.project_id}) + context.can(kp_policies.POLICY_ROOT % 'delete', + target={'user_id': user_id, + 'project_id': context.project_id}) try: self.api.delete_key_pair(context, user_id, id) except exception.KeypairNotFound as exc: @@ -183,6 +188,7 @@ def _get_user_id(self, req): return user_id @wsgi.Controller.api_version("2.10") + @validation.query_schema(keypairs.show_query_schema_v210) @extensions.expected_errors(404) def show(self, req, id): # handle optional user-id for admin only @@ -190,11 +196,13 @@ def show(self, req, id): return self._show(req, id, type=True, user_id=user_id) @wsgi.Controller.api_version("2.2", "2.9") # noqa + @validation.query_schema(keypairs.show_query_schema_v20) @extensions.expected_errors(404) def show(self, req, id): return self._show(req, id, type=True) @wsgi.Controller.api_version("2.1", "2.1") # noqa + @validation.query_schema(keypairs.show_query_schema_v20) @extensions.expected_errors(404) def show(self, req, id): return self._show(req, id) @@ -203,9 +211,9 @@ def _show(self, req, id, user_id=None, **keypair_filters): """Return data for the given key name.""" context = req.environ['nova.context'] user_id = user_id or context.user_id - authorize(context, action='show', - target={'user_id': user_id, - 'project_id': context.project_id}) + context.can(kp_policies.POLICY_ROOT % 'show', + target={'user_id': user_id, + 'project_id': context.project_id}) try: # The return object needs to be a dict in order to pop the 'type' @@ -222,7 +230,15 @@ def _show(self, req, id, user_id=None, **keypair_filters): # behaviors in this keypair resource. return {'keypair': keypair} - @wsgi.Controller.api_version("2.10") + @wsgi.Controller.api_version("2.35") + @validation.query_schema(keypairs.index_query_schema_v235) + @extensions.expected_errors(400) + def index(self, req): + user_id = self._get_user_id(req) + return self._index(req, links=True, type=True, user_id=user_id) + + @wsgi.Controller.api_version("2.10", "2.34") # noqa + @validation.query_schema(keypairs.index_query_schema_v210) @extensions.expected_errors(()) def index(self, req): # handle optional user-id for admin only @@ -230,29 +246,49 @@ def index(self, req): return self._index(req, type=True, user_id=user_id) @wsgi.Controller.api_version("2.2", "2.9") # noqa + @validation.query_schema(keypairs.index_query_schema_v20) @extensions.expected_errors(()) def index(self, req): return self._index(req, type=True) @wsgi.Controller.api_version("2.1", "2.1") # noqa + @validation.query_schema(keypairs.index_query_schema_v20) @extensions.expected_errors(()) def index(self, req): return self._index(req) - def _index(self, req, user_id=None, **keypair_filters): + def _index(self, req, user_id=None, links=False, **keypair_filters): """List of keypairs for a user.""" context = req.environ['nova.context'] user_id = user_id or context.user_id - authorize(context, action='index', - target={'user_id': user_id, - 'project_id': context.project_id}) - key_pairs = self.api.get_key_pairs(context, user_id) - rval = [] - for key_pair in key_pairs: - rval.append({'keypair': self._filter_keypair(key_pair, - **keypair_filters)}) + context.can(kp_policies.POLICY_ROOT % 'index', + target={'user_id': user_id, + 'project_id': context.project_id}) - return {'keypairs': rval} + if api_version_request.is_supported(req, min_version='2.35'): + limit, marker = common.get_limit_and_marker(req) + else: + limit = marker = None + + try: + key_pairs = self.api.get_key_pairs( + context, user_id, limit=limit, marker=marker) + except exception.MarkerNotFound as e: + raise webob.exc.HTTPBadRequest(explanation=e.format_message()) + + key_pairs = [self._filter_keypair(key_pair, **keypair_filters) + for key_pair in key_pairs] + + keypairs_list = [{'keypair': key_pair} for key_pair in key_pairs] + keypairs_dict = {'keypairs': keypairs_list} + + if links: + keypairs_links = self._view_builder.get_links(req, key_pairs) + + if keypairs_links: + keypairs_dict['keypairs_links'] = keypairs_links + + return keypairs_dict class Controller(wsgi.Controller): @@ -272,48 +308,30 @@ def _show(self, req, resp_obj): @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['nova.context'] - if soft_authorize(context): + if context.can(kp_policies.BASE_POLICY_NAME, fatal=False): self._show(req, resp_obj) @wsgi.extends def detail(self, req, resp_obj): context = req.environ['nova.context'] - if 'servers' in resp_obj.obj and soft_authorize(context): + if 'servers' in resp_obj.obj and context.can( + kp_policies.BASE_POLICY_NAME, fatal=False): servers = resp_obj.obj['servers'] self._add_key_name(req, servers) -class Keypairs(extensions.V21APIExtensionBase): - """Keypair Support.""" - - name = "Keypairs" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [ - extensions.ResourceExtension(ALIAS, - KeypairController())] - return resources - - def get_controller_extensions(self): - controller = Controller() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - # use nova.api.extensions.server.extensions entry point to modify - # server create kwargs - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - # NOTE(alex_xu): The v2.1 API compat mode, we strip the spaces for - # keypair create. But we didn't strip spaces at here for - # backward-compatible some users already created keypair and name with - # leading/trailing spaces by legacy v2 API. - create_kwargs['key_name'] = server_dict.get('key_name') - - def get_server_create_schema(self, version): - if version == '2.0': - return keypairs.server_create_v20 - else: - return keypairs.server_create +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + # NOTE(alex_xu): The v2.1 API compat mode, we strip the spaces for + # keypair create. But we didn't strip spaces at here for + # backward-compatible some users already created keypair and name with + # leading/trailing spaces by legacy v2 API. + create_kwargs['key_name'] = server_dict.get('key_name') + + +def get_server_create_schema(version): + if version == '2.0': + return keypairs.server_create_v20 + else: + return keypairs.server_create diff --git a/nova/api/openstack/compute/legacy_v2/consoles.py b/nova/api/openstack/compute/legacy_v2/consoles.py deleted file mode 100644 index 24b943897d7..00000000000 --- a/nova/api/openstack/compute/legacy_v2/consoles.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2010 OpenStack Foundation -# 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 webob -from webob import exc - -from nova.api.openstack import wsgi -from nova.console import api as console_api -from nova import exception - - -def _translate_keys(cons): - """Coerces a console instance into proper dictionary format.""" - pool = cons['pool'] - info = {'id': cons['id'], - 'console_type': pool['console_type']} - return dict(console=info) - - -def _translate_detail_keys(cons): - """Coerces a console instance into proper dictionary format with - correctly mapped attributes. - """ - pool = cons['pool'] - info = {'id': cons['id'], - 'console_type': pool['console_type'], - 'password': cons['password'], - 'instance_name': cons['instance_name'], - 'port': cons['port'], - 'host': pool['public_hostname']} - return dict(console=info) - - -class Controller(object): - """The Consoles controller for the OpenStack API.""" - - def __init__(self): - self.console_api = console_api.API() - - def index(self, req, server_id): - """Returns a list of consoles for this instance.""" - consoles = self.console_api.get_consoles( - req.environ['nova.context'], - server_id) - return dict(consoles=[_translate_keys(console) - for console in consoles]) - - def create(self, req, server_id, body): - """Creates a new console.""" - try: - self.console_api.create_console( - req.environ['nova.context'], - server_id) - except exception.InstanceNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - - def show(self, req, server_id, id): - """Shows in-depth information on a specific console.""" - try: - console = self.console_api.get_console( - req.environ['nova.context'], - server_id, - int(id)) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - return _translate_detail_keys(console) - - def delete(self, req, server_id, id): - """Deletes a console.""" - try: - self.console_api.delete_console(req.environ['nova.context'], - server_id, - int(id)) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - return webob.Response(status_int=202) - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/nova/api/openstack/compute/legacy_v2/contrib/__init__.py b/nova/api/openstack/compute/legacy_v2/contrib/__init__.py deleted file mode 100644 index a59ffe8e9eb..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2011 Justin Santa Barbara -# 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. - -"""Contrib contains extensions that are shipped with nova. - -It can't be called 'extensions' because that causes namespacing problems. - -""" - -from oslo_config import cfg -from oslo_log import log as logging - -from nova.api.openstack import extensions - -ext_opts = [ - cfg.ListOpt('osapi_compute_ext_list', - default=[], - help='DEPRECATED: Specify list of extensions to load when ' - 'using osapi_compute_extension option with nova.api.' - 'openstack.compute.legacy_v2.contrib.select_extensions ' - 'This option will be removed in the near future. ' - 'After that point you have to run all of the API.', - deprecated_for_removal=True), -] -CONF = cfg.CONF -CONF.register_opts(ext_opts) - -LOG = logging.getLogger(__name__) - - -def standard_extensions(ext_mgr): - extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__) - - -def select_extensions(ext_mgr): - extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__, - CONF.osapi_compute_ext_list) diff --git a/nova/api/openstack/compute/legacy_v2/contrib/admin_actions.py b/nova/api/openstack/compute/legacy_v2/contrib/admin_actions.py deleted file mode 100644 index 19d0043d786..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/admin_actions.py +++ /dev/null @@ -1,403 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# 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 traceback - -from oslo_log import log as logging -from oslo_utils import strutils -import six -import webob -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova.compute import vm_states -from nova import exception -from nova.i18n import _ -from nova.i18n import _LE -from nova import utils - -LOG = logging.getLogger(__name__) - -# States usable in resetState action -state_map = dict(active=vm_states.ACTIVE, error=vm_states.ERROR) - - -def authorize(context, action_name): - action = 'admin_actions:%s' % action_name - extensions.extension_authorizer('compute', action)(context) - - -class AdminActionsController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(AdminActionsController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - - @wsgi.action('pause') - def _pause(self, req, id, body): - """Permit Admins to pause the server.""" - ctxt = req.environ['nova.context'] - authorize(ctxt, 'pause') - server = common.get_instance(self.compute_api, ctxt, id) - try: - self.compute_api.pause(ctxt, server) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'pause', id) - except exception.InstanceNotFound: - msg = _("Server not found") - raise exc.HTTPNotFound(explanation=msg) - except Exception: - readable = traceback.format_exc() - LOG.exception(_LE("Compute.api::pause %s"), readable) - raise exc.HTTPUnprocessableEntity() - return webob.Response(status_int=202) - - @wsgi.action('unpause') - def _unpause(self, req, id, body): - """Permit Admins to unpause the server.""" - ctxt = req.environ['nova.context'] - authorize(ctxt, 'unpause') - server = common.get_instance(self.compute_api, ctxt, id) - try: - self.compute_api.unpause(ctxt, server) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'unpause', id) - except exception.InstanceNotFound: - msg = _("Server not found") - raise exc.HTTPNotFound(explanation=msg) - except Exception: - readable = traceback.format_exc() - LOG.exception(_LE("Compute.api::unpause %s"), readable) - raise exc.HTTPUnprocessableEntity() - return webob.Response(status_int=202) - - @wsgi.action('suspend') - def _suspend(self, req, id, body): - """Permit admins to suspend the server.""" - context = req.environ['nova.context'] - authorize(context, 'suspend') - server = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.suspend(context, server) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'suspend', id) - except exception.InstanceNotFound: - msg = _("Server not found") - raise exc.HTTPNotFound(explanation=msg) - except Exception: - readable = traceback.format_exc() - LOG.exception(_LE("compute.api::suspend %s"), readable) - raise exc.HTTPUnprocessableEntity() - return webob.Response(status_int=202) - - @wsgi.action('resume') - def _resume(self, req, id, body): - """Permit admins to resume the server from suspend.""" - context = req.environ['nova.context'] - authorize(context, 'resume') - server = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.resume(context, server) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'resume', id) - except exception.InstanceNotFound: - msg = _("Server not found") - raise exc.HTTPNotFound(explanation=msg) - except Exception: - readable = traceback.format_exc() - LOG.exception(_LE("compute.api::resume %s"), readable) - raise exc.HTTPUnprocessableEntity() - return webob.Response(status_int=202) - - @wsgi.action('migrate') - def _migrate(self, req, id, body): - """Permit admins to migrate a server to a new host.""" - context = req.environ['nova.context'] - authorize(context, 'migrate') - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.resize(req.environ['nova.context'], instance) - except exception.QuotaError as error: - raise exc.HTTPForbidden(explanation=error.format_message()) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'migrate', id) - except exception.InstanceNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.NoValidHost as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - except Exception: - LOG.exception(_LE("Error in migrate")) - raise exc.HTTPBadRequest() - return webob.Response(status_int=202) - - @wsgi.action('resetNetwork') - def _reset_network(self, req, id, body): - """Permit admins to reset networking on a server.""" - context = req.environ['nova.context'] - authorize(context, 'resetNetwork') - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.reset_network(context, instance) - except exception.InstanceNotFound: - msg = _("Server not found") - raise exc.HTTPNotFound(explanation=msg) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except Exception: - readable = traceback.format_exc() - LOG.exception(_LE("Compute.api::reset_network %s"), readable) - raise exc.HTTPUnprocessableEntity() - return webob.Response(status_int=202) - - @wsgi.action('injectNetworkInfo') - def _inject_network_info(self, req, id, body): - """Permit admins to inject network info into a server.""" - context = req.environ['nova.context'] - authorize(context, 'injectNetworkInfo') - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.inject_network_info(context, instance) - except exception.InstanceNotFound: - msg = _("Server not found") - raise exc.HTTPNotFound(explanation=msg) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except Exception: - readable = traceback.format_exc() - LOG.exception(_LE("Compute.api::inject_network_info %s"), readable) - raise exc.HTTPUnprocessableEntity() - return webob.Response(status_int=202) - - @wsgi.action('lock') - def _lock(self, req, id, body): - """Lock a server instance.""" - context = req.environ['nova.context'] - authorize(context, 'lock') - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.lock(context, instance) - except exception.InstanceNotFound: - msg = _("Server not found") - raise exc.HTTPNotFound(explanation=msg) - except Exception: - readable = traceback.format_exc() - LOG.exception(_LE("Compute.api::lock %s"), readable) - raise exc.HTTPUnprocessableEntity() - return webob.Response(status_int=202) - - @wsgi.action('unlock') - def _unlock(self, req, id, body): - """Unlock a server instance.""" - context = req.environ['nova.context'] - authorize(context, 'unlock') - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.unlock(context, instance) - except exception.PolicyNotAuthorized as e: - raise webob.exc.HTTPForbidden(explanation=e.format_message()) - except exception.InstanceNotFound: - msg = _("Server not found") - raise exc.HTTPNotFound(explanation=msg) - except Exception: - readable = traceback.format_exc() - LOG.exception(_LE("Compute.api::unlock %s"), readable) - raise exc.HTTPUnprocessableEntity() - return webob.Response(status_int=202) - - @wsgi.action('createBackup') - def _create_backup(self, req, id, body): - """Backup a server instance. - - Images now have an `image_type` associated with them, which can be - 'snapshot' or the backup type, like 'daily' or 'weekly'. - - If the image_type is backup-like, then the rotation factor can be - included and that will cause the oldest backups that exceed the - rotation factor to be deleted. - - """ - context = req.environ["nova.context"] - authorize(context, 'createBackup') - entity = body["createBackup"] - - try: - image_name = entity["name"] - backup_type = entity["backup_type"] - rotation = entity["rotation"] - - except KeyError as missing_key: - msg = _("createBackup entity requires %s attribute") % missing_key - raise exc.HTTPBadRequest(explanation=msg) - - except TypeError: - msg = _("Malformed createBackup entity") - raise exc.HTTPBadRequest(explanation=msg) - - try: - rotation = utils.validate_integer(rotation, "rotation", - min_value=0) - except exception.InvalidInput as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - - props = {} - metadata = entity.get('metadata', {}) - common.check_img_metadata_properties_quota(context, metadata) - try: - props.update(metadata) - except ValueError: - msg = _("Invalid metadata") - raise exc.HTTPBadRequest(explanation=msg) - - instance = common.get_instance(self.compute_api, context, id) - try: - image = self.compute_api.backup(context, instance, image_name, - backup_type, rotation, extra_properties=props) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'createBackup', id) - except exception.InvalidRequest as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - resp = webob.Response(status_int=202) - - # build location of newly-created image entity if rotation is not zero - if rotation > 0: - image_id = str(image['id']) - image_ref = common.url_join(req.application_url, 'images', - image_id) - resp.headers['Location'] = image_ref - - return resp - - @wsgi.action('os-migrateLive') - def _migrate_live(self, req, id, body): - """Permit admins to (live) migrate a server to a new host.""" - context = req.environ["nova.context"] - authorize(context, 'migrateLive') - - try: - block_migration = body["os-migrateLive"]["block_migration"] - disk_over_commit = body["os-migrateLive"]["disk_over_commit"] - host = body["os-migrateLive"]["host"] - except (TypeError, KeyError): - msg = _("host, block_migration and disk_over_commit must " - "be specified for live migration.") - raise exc.HTTPBadRequest(explanation=msg) - - try: - block_migration = strutils.bool_from_string(block_migration, - strict=True) - disk_over_commit = strutils.bool_from_string(disk_over_commit, - strict=True) - except ValueError as err: - raise exc.HTTPBadRequest(explanation=six.text_type(err)) - - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.live_migrate(context, instance, block_migration, - disk_over_commit, host) - except (exception.NoValidHost, - exception.ComputeServiceUnavailable, - exception.InvalidHypervisorType, - exception.InvalidCPUInfo, - exception.UnableToMigrateToSelf, - exception.DestinationHypervisorTooOld, - exception.InvalidLocalStorage, - exception.InvalidSharedStorage, - exception.HypervisorUnavailable, - exception.MigrationPreCheckError, - exception.LiveMigrationWithOldNovaNotSafe) as ex: - raise exc.HTTPBadRequest(explanation=ex.format_message()) - except exception.InstanceNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'os-migrateLive', id) - except Exception: - if host is None: - msg = _("Live migration of instance %s to another host " - "failed") % id - else: - msg = _("Live migration of instance %(id)s to host %(host)s " - "failed") % {'id': id, 'host': host} - LOG.exception(msg) - # Return messages from scheduler - raise exc.HTTPInternalServerError(explanation=msg) - - return webob.Response(status_int=202) - - @wsgi.action('os-resetState') - def _reset_state(self, req, id, body): - """Permit admins to reset the state of a server.""" - context = req.environ["nova.context"] - authorize(context, 'resetState') - - # Identify the desired state from the body - try: - state = state_map[body["os-resetState"]["state"]] - except (TypeError, KeyError): - msg = _("Desired state must be specified. Valid states " - "are: %s") % ', '.join(sorted(state_map.keys())) - raise exc.HTTPBadRequest(explanation=msg) - - instance = common.get_instance(self.compute_api, context, id) - try: - instance.vm_state = state - instance.task_state = None - instance.save(admin_state_reset=True) - except exception.InstanceNotFound: - msg = _("Server not found") - raise exc.HTTPNotFound(explanation=msg) - except Exception: - readable = traceback.format_exc() - LOG.exception(_LE("Compute.api::resetState %s"), readable) - raise exc.HTTPUnprocessableEntity() - return webob.Response(status_int=202) - - -class Admin_actions(extensions.ExtensionDescriptor): - """Enable admin-only server actions - - Actions include: pause, unpause, suspend, resume, migrate, - resetNetwork, injectNetworkInfo, lock, unlock, createBackup - """ - - name = "AdminActions" - alias = "os-admin-actions" - namespace = "http://docs.openstack.org/compute/ext/admin-actions/api/v1.1" - updated = "2011-09-20T00:00:00Z" - - def get_controller_extensions(self): - controller = AdminActionsController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/agents.py b/nova/api/openstack/compute/legacy_v2/contrib/agents.py deleted file mode 100644 index 881926944fb..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/agents.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright 2012 IBM Corp. -# -# 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 webob.exc - -from nova.api.openstack import extensions -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import objects -from nova import utils - - -authorize = extensions.extension_authorizer('compute', 'agents') - - -class AgentController(object): - """The agent is talking about guest agent.The host can use this for - things like accessing files on the disk, configuring networking, - or running other applications/scripts in the guest while it is - running. Typically this uses some hypervisor-specific transport - to avoid being dependent on a working network configuration. - Xen, VMware, and VirtualBox have guest agents,although the Xen - driver is the only one with an implementation for managing them - in openstack. KVM doesn't really have a concept of a guest agent - (although one could be written). - - You can find the design of agent update in this link: - http://wiki.openstack.org/AgentUpdate - and find the code in nova.virt.xenapi.vmops.VMOps._boot_new_instance. - In this design We need update agent in guest from host, so we need - some interfaces to update the agent info in host. - - You can find more information about the design of the GuestAgent in - the following link: - http://wiki.openstack.org/GuestAgent - http://wiki.openstack.org/GuestAgentXenStoreCommunication - """ - def index(self, req): - """Return a list of all agent builds. Filter by hypervisor.""" - context = req.environ['nova.context'] - authorize(context) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - hypervisor = None - agents = [] - if 'hypervisor' in req.GET: - hypervisor = req.GET['hypervisor'] - - builds = objects.AgentList.get_all(context, hypervisor=hypervisor) - for agent_build in builds: - agents.append({'hypervisor': agent_build.hypervisor, - 'os': agent_build.os, - 'architecture': agent_build.architecture, - 'version': agent_build.version, - 'md5hash': agent_build.md5hash, - 'agent_id': agent_build.id, - 'url': agent_build.url}) - - return {'agents': agents} - - def update(self, req, id, body): - """Update an existing agent build.""" - context = req.environ['nova.context'] - authorize(context) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - try: - para = body['para'] - url = para['url'] - md5hash = para['md5hash'] - version = para['version'] - except (TypeError, KeyError) as ex: - msg = _("Invalid request body: %s") % ex - raise webob.exc.HTTPBadRequest(explanation=msg) - - try: - utils.validate_integer(id, 'id') - utils.check_string_length(url, 'url', max_length=255) - utils.check_string_length(md5hash, 'md5hash', max_length=255) - utils.check_string_length(version, 'version', max_length=255) - except exception.InvalidInput as exc: - raise webob.exc.HTTPBadRequest(explanation=exc.format_message()) - - try: - agent = objects.Agent(context=context, id=id) - agent.obj_reset_changes() - agent.version = version - agent.url = url - agent.md5hash = md5hash - agent.save() - except exception.AgentBuildNotFound as ex: - raise webob.exc.HTTPNotFound(explanation=ex.format_message()) - - # NOTE(alex_xu): The agent_id should be integer that consistent with - # create/index actions. But parameter 'id' is string type that parsed - # from url. This is a bug, but because back-compatibility, it can't be - # fixed for v2 API. This will be fixed in v2.1 API by Microversions in - # the future. lp bug #1333494 - return {"agent": {'agent_id': id, 'version': version, - 'url': url, 'md5hash': md5hash}} - - def delete(self, req, id): - """Deletes an existing agent build.""" - context = req.environ['nova.context'] - authorize(context) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - try: - utils.validate_integer(id, 'id') - except exception.InvalidInput as exc: - raise webob.exc.HTTPBadRequest(explanation=exc.format_message()) - - try: - agent = objects.Agent(context=context, id=id) - agent.destroy() - except exception.AgentBuildNotFound as ex: - raise webob.exc.HTTPNotFound(explanation=ex.format_message()) - - def create(self, req, body): - """Creates a new agent build.""" - context = req.environ['nova.context'] - authorize(context) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - try: - agent = body['agent'] - hypervisor = agent['hypervisor'] - os = agent['os'] - architecture = agent['architecture'] - version = agent['version'] - url = agent['url'] - md5hash = agent['md5hash'] - except (TypeError, KeyError) as ex: - msg = _("Invalid request body: %s") % ex - raise webob.exc.HTTPBadRequest(explanation=msg) - - try: - utils.check_string_length(hypervisor, 'hypervisor', max_length=255) - utils.check_string_length(os, 'os', max_length=255) - utils.check_string_length(architecture, 'architecture', - max_length=255) - utils.check_string_length(version, 'version', max_length=255) - utils.check_string_length(url, 'url', max_length=255) - utils.check_string_length(md5hash, 'md5hash', max_length=255) - except exception.InvalidInput as exc: - raise webob.exc.HTTPBadRequest(explanation=exc.format_message()) - - try: - agent_obj = objects.Agent(context=context) - agent_obj.hypervisor = hypervisor - agent_obj.os = os - agent_obj.architecture = architecture - agent_obj.version = version - agent_obj.url = url - agent_obj.md5hash = md5hash - agent_obj.create() - agent['agent_id'] = agent_obj.id - except exception.AgentBuildExists as ex: - raise webob.exc.HTTPConflict(explanation=ex.format_message()) - return {'agent': agent} - - -class Agents(extensions.ExtensionDescriptor): - """Agents support.""" - - name = "Agents" - alias = "os-agents" - namespace = "http://docs.openstack.org/compute/ext/agents/api/v2" - updated = "2012-10-28T00:00:00Z" - - def get_resources(self): - resources = [] - resource = extensions.ResourceExtension('os-agents', - AgentController()) - resources.append(resource) - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/aggregates.py b/nova/api/openstack/compute/legacy_v2/contrib/aggregates.py deleted file mode 100644 index bebf942050d..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/aggregates.py +++ /dev/null @@ -1,308 +0,0 @@ -# Copyright (c) 2012 Citrix Systems, 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. - -"""The Aggregate admin API extension.""" - -import datetime - -import six -from webob import exc - -from nova.api.openstack import extensions -from nova.compute import api as compute_api -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import utils - -authorize = extensions.extension_authorizer('compute', 'aggregates') - - -def _get_context(req): - return req.environ['nova.context'] - - -def get_host_from_body(fn): - """Makes sure that the host exists.""" - def wrapped(self, req, id, body, *args, **kwargs): - if len(body) != 1: - msg = _('Only host parameter can be specified') - raise exc.HTTPBadRequest(explanation=msg) - elif 'host' not in body: - msg = _('Host parameter must be specified') - raise exc.HTTPBadRequest(explanation=msg) - try: - utils.check_string_length(body['host'], 'host', 1, 255) - except exception.InvalidInput as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - host = body['host'] - - return fn(self, req, id, host, *args, **kwargs) - return wrapped - - -class AggregateController(object): - """The Host Aggregates API controller for the OpenStack API.""" - def __init__(self): - self.api = compute_api.AggregateAPI() - - def index(self, req): - """Returns a list a host aggregate's id, name, availability_zone.""" - context = _get_context(req) - authorize(context) - aggregates = self.api.get_aggregate_list(context) - return {'aggregates': [self._marshall_aggregate(a)['aggregate'] - for a in aggregates]} - - def create(self, req, body): - """Creates an aggregate, given its name and - optional availability zone. - """ - context = _get_context(req) - authorize(context) - - if len(body) != 1: - raise exc.HTTPBadRequest() - try: - host_aggregate = body["aggregate"] - name = host_aggregate["name"] - except KeyError: - raise exc.HTTPBadRequest() - avail_zone = host_aggregate.get("availability_zone") - try: - utils.check_string_length(name, "Aggregate name", 1, 255) - if avail_zone is not None: - utils.check_string_length(avail_zone, "Availability_zone", 1, - 255) - except exception.InvalidInput as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - try: - aggregate = self.api.create_aggregate(context, name, avail_zone) - except exception.AggregateNameExists as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InvalidAggregateAction as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - agg = self._marshall_aggregate(aggregate) - - # To maintain the same API result as before the changes for returning - # nova objects were made. - del agg['aggregate']['hosts'] - del agg['aggregate']['metadata'] - - return agg - - def show(self, req, id): - """Shows the details of an aggregate, hosts and metadata included.""" - context = _get_context(req) - authorize(context) - try: - aggregate = self.api.get_aggregate(context, id) - except exception.AggregateNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - return self._marshall_aggregate(aggregate) - - def update(self, req, id, body): - """Updates the name and/or availability_zone of given aggregate.""" - context = _get_context(req) - authorize(context) - - if len(body) != 1: - raise exc.HTTPBadRequest() - try: - updates = body["aggregate"] - except KeyError: - raise exc.HTTPBadRequest() - - if len(updates) < 1: - raise exc.HTTPBadRequest() - - for key in updates.keys(): - if key not in ["name", "availability_zone"]: - raise exc.HTTPBadRequest() - - try: - if 'name' in updates: - utils.check_string_length(updates['name'], "Aggregate name", 1, - 255) - if updates.get("availability_zone") is not None: - utils.check_string_length(updates['availability_zone'], - "Availability_zone", 1, 255) - except exception.InvalidInput as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - try: - aggregate = self.api.update_aggregate(context, id, updates) - except exception.AggregateNameExists as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.AggregateNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.InvalidAggregateAction as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - return self._marshall_aggregate(aggregate) - - def delete(self, req, id): - """Removes an aggregate by id.""" - context = _get_context(req) - authorize(context) - try: - self.api.delete_aggregate(context, id) - except exception.AggregateNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.InvalidAggregateAction as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - def action(self, req, id, body): - _actions = { - 'add_host': self._add_host, - 'remove_host': self._remove_host, - 'set_metadata': self._set_metadata, - } - for action, data in six.iteritems(body): - if action not in _actions.keys(): - msg = _('Aggregates does not have %s action') % action - raise exc.HTTPBadRequest(explanation=msg) - return _actions[action](req, id, data) - - raise exc.HTTPBadRequest(explanation=_("Invalid request body")) - - @get_host_from_body - def _add_host(self, req, id, host): - """Adds a host to the specified aggregate.""" - context = _get_context(req) - authorize(context) - - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - - try: - aggregate = self.api.add_host_to_aggregate(context, id, host) - except (exception.AggregateNotFound, exception.ComputeHostNotFound): - msg = _('Cannot add host %(host)s in aggregate' - ' %(id)s: not found') % {'host': host, 'id': id} - raise exc.HTTPNotFound(explanation=msg) - except (exception.AggregateHostExists, - exception.InvalidAggregateAction): - msg = _('Cannot add host %(host)s in aggregate' - ' %(id)s: host exists') % {'host': host, 'id': id} - raise exc.HTTPConflict(explanation=msg) - return self._marshall_aggregate(aggregate) - - @get_host_from_body - def _remove_host(self, req, id, host): - """Removes a host from the specified aggregate.""" - context = _get_context(req) - authorize(context) - - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - - try: - aggregate = self.api.remove_host_from_aggregate(context, id, host) - except (exception.AggregateNotFound, exception.AggregateHostNotFound, - exception.ComputeHostNotFound): - msg = _('Cannot remove host %(host)s in aggregate' - ' %(id)s: not found') % {'host': host, 'id': id} - raise exc.HTTPNotFound(explanation=msg) - except exception.InvalidAggregateAction: - msg = _('Cannot remove host %(host)s in aggregate' - ' %(id)s: invalid') % {'host': host, 'id': id} - raise exc.HTTPConflict(explanation=msg) - return self._marshall_aggregate(aggregate) - - def _set_metadata(self, req, id, body): - """Replaces the aggregate's existing metadata with new metadata.""" - context = _get_context(req) - authorize(context) - - if len(body) != 1: - raise exc.HTTPBadRequest() - try: - metadata = body["metadata"] - except KeyError: - raise exc.HTTPBadRequest() - - # The metadata should be a dict - if not isinstance(metadata, dict): - msg = _('The value of metadata must be a dict') - raise exc.HTTPBadRequest(explanation=msg) - try: - for key, value in metadata.items(): - utils.check_string_length(key, "metadata.key", 1, 255) - if value is not None: - utils.check_string_length(value, "metadata.value", 0, 255) - except exception.InvalidInput as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - try: - aggregate = self.api.update_aggregate_metadata(context, - id, metadata) - except exception.AggregateNotFound: - msg = _('Cannot set metadata %(metadata)s in aggregate' - ' %(id)s') % {'metadata': metadata, 'id': id} - raise exc.HTTPNotFound(explanation=msg) - except exception.InvalidAggregateAction as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - return self._marshall_aggregate(aggregate) - - def _marshall_aggregate(self, aggregate): - _aggregate = {} - for key, value in self._build_aggregate_items(aggregate): - # NOTE(danms): The original API specified non-TZ-aware timestamps - if isinstance(value, datetime.datetime): - value = value.replace(tzinfo=None) - _aggregate[key] = value - return {"aggregate": _aggregate} - - def _build_aggregate_items(self, aggregate): - # NOTE(rlrossit): Within the compute API, metadata will always be - # set on the aggregate object (at a minimum to {}). Because of this, - # we can freely use getattr() on keys in obj_extra_fields (in this - # case it is only ['availability_zone']) without worrying about - # lazy-loading an unset variable - keys = aggregate.obj_fields - for key in keys: - # NOTE(danms): Skip the uuid field because we have no microversion - # to expose it - if ((aggregate.obj_attr_is_set(key) - or key in aggregate.obj_extra_fields) and - key != 'uuid'): - yield key, getattr(aggregate, key) - - -class Aggregates(extensions.ExtensionDescriptor): - """Admin-only aggregate administration.""" - - name = "Aggregates" - alias = "os-aggregates" - namespace = "http://docs.openstack.org/compute/ext/aggregates/api/v1.1" - updated = "2012-01-12T00:00:00Z" - - def get_resources(self): - resources = [] - res = extensions.ResourceExtension('os-aggregates', - AggregateController(), - member_actions={"action": "POST", }) - resources.append(res) - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/assisted_volume_snapshots.py b/nova/api/openstack/compute/legacy_v2/contrib/assisted_volume_snapshots.py deleted file mode 100644 index 73f965f13d5..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/assisted_volume_snapshots.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright 2013 Red Hat, Inc. -# -# 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 oslo_log import log as logging -from oslo_serialization import jsonutils -import six -import webob - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova.i18n import _LI - - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', - 'os-assisted-volume-snapshots') - - -class AssistedVolumeSnapshotsController(wsgi.Controller): - - def __init__(self): - self.compute_api = compute.API() - super(AssistedVolumeSnapshotsController, self).__init__() - - def create(self, req, body): - """Creates a new snapshot.""" - context = req.environ['nova.context'] - authorize(context, action='create') - - if not self.is_valid_body(body, 'snapshot'): - raise webob.exc.HTTPBadRequest() - - try: - snapshot = body['snapshot'] - create_info = snapshot['create_info'] - volume_id = snapshot['volume_id'] - except KeyError: - raise webob.exc.HTTPBadRequest() - - LOG.info(_LI("Create assisted snapshot from volume %s"), volume_id, - context=context) - - return self.compute_api.volume_snapshot_create(context, volume_id, - create_info) - - def delete(self, req, id): - """Delete a snapshot.""" - context = req.environ['nova.context'] - authorize(context, action='delete') - - LOG.info(_LI("Delete snapshot with id: %s"), id, context=context) - - delete_metadata = {} - delete_metadata.update(req.GET) - - try: - delete_info = jsonutils.loads(delete_metadata['delete_info']) - volume_id = delete_info['volume_id'] - except (KeyError, ValueError) as e: - raise webob.exc.HTTPBadRequest(explanation=six.text_type(e)) - - try: - self.compute_api.volume_snapshot_delete(context, volume_id, - id, delete_info) - except exception.NotFound: - return webob.exc.HTTPNotFound() - - return webob.Response(status_int=204) - - -class Assisted_volume_snapshots(extensions.ExtensionDescriptor): - """Assisted volume snapshots.""" - - name = "AssistedVolumeSnapshots" - alias = "os-assisted-volume-snapshots" - namespace = ("http://docs.openstack.org/compute/ext/" - "assisted-volume-snapshots/api/v2") - updated = "2013-08-29T00:00:00Z" - - def get_resources(self): - resource = extensions.ResourceExtension('os-assisted-volume-snapshots', - AssistedVolumeSnapshotsController()) - - return [resource] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/attach_interfaces.py b/nova/api/openstack/compute/legacy_v2/contrib/attach_interfaces.py deleted file mode 100644 index 6a92c4b9a4a..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/attach_interfaces.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright 2012 SINA 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. - -"""The instance interfaces extension.""" - -import netaddr -from oslo_log import log as logging -import six -import webob -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova import compute -from nova import exception -from nova.i18n import _ -from nova.i18n import _LI -from nova import network - - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'attach_interfaces') - - -def _translate_interface_attachment_view(port_info): - """Maps keys for interface attachment details view.""" - return { - 'net_id': port_info['network_id'], - 'port_id': port_info['id'], - 'mac_addr': port_info['mac_address'], - 'port_state': port_info['status'], - 'fixed_ips': port_info.get('fixed_ips', None), - } - - -class InterfaceAttachmentController(object): - """The interface attachment API controller for the OpenStack API.""" - - def __init__(self): - self.compute_api = compute.API() - self.network_api = network.API() - super(InterfaceAttachmentController, self).__init__() - - def index(self, req, server_id): - """Returns the list of interface attachments for a given instance.""" - return self._items(req, server_id, - entity_maker=_translate_interface_attachment_view) - - def show(self, req, server_id, id): - """Return data about the given interface attachment.""" - context = req.environ['nova.context'] - authorize(context) - - port_id = id - # NOTE(mriedem): We need to verify the instance actually exists from - # the server_id even though we're not using the instance for anything, - # just the port id. - common.get_instance(self.compute_api, context, server_id) - - try: - port_info = self.network_api.show_port(context, port_id) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.Forbidden as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - - if port_info['port']['device_id'] != server_id: - msg = _("Instance %(instance)s does not have a port with id " - "%(port)s") % {'instance': server_id, 'port': port_id} - raise exc.HTTPNotFound(explanation=msg) - - return {'interfaceAttachment': _translate_interface_attachment_view( - port_info['port'])} - - def create(self, req, server_id, body): - """Attach an interface to an instance.""" - context = req.environ['nova.context'] - authorize(context) - - network_id = None - port_id = None - req_ip = None - if body: - attachment = body['interfaceAttachment'] - network_id = attachment.get('net_id', None) - port_id = attachment.get('port_id', None) - try: - req_ip = attachment['fixed_ips'][0]['ip_address'] - except Exception: - pass - - if network_id and port_id: - msg = _("Must not input both network_id and port_id") - raise exc.HTTPBadRequest(explanation=msg) - if req_ip and not network_id: - msg = _("Must input network_id when request IP address") - raise exc.HTTPBadRequest(explanation=msg) - - if req_ip: - try: - netaddr.IPAddress(req_ip) - except netaddr.AddrFormatError as e: - raise exc.HTTPBadRequest(explanation=six.text_type(e)) - - try: - instance = common.get_instance(self.compute_api, - context, server_id) - LOG.info(_LI("Attach interface"), instance=instance) - vif = self.compute_api.attach_interface(context, - instance, network_id, port_id, req_ip) - except (exception.PortNotFound, - exception.NetworkNotFound) as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except (exception.FixedIpAlreadyInUse, - exception.InterfaceAttachFailedNoNetwork, - exception.NoMoreFixedIps, - exception.PortInUse, - exception.NetworkDuplicated, - exception.NetworkAmbiguous, - exception.PortNotUsable) as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except NotImplementedError: - msg = _("Network driver does not support this function.") - raise webob.exc.HTTPNotImplemented(explanation=msg) - except exception.InterfaceAttachFailed: - msg = _("Failed to attach interface") - raise webob.exc.HTTPInternalServerError(explanation=msg) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'attach_interface', server_id) - - return self.show(req, server_id, vif['id']) - - def delete(self, req, server_id, id): - """Detach an interface from an instance.""" - context = req.environ['nova.context'] - authorize(context) - port_id = id - instance = common.get_instance(self.compute_api, - context, server_id) - LOG.info(_LI("Detach interface %s"), port_id, instance=instance) - try: - self.compute_api.detach_interface(context, - instance, port_id=port_id) - except exception.PortNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except NotImplementedError: - msg = _("Network driver does not support this function.") - raise webob.exc.HTTPNotImplemented(explanation=msg) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'detach_interface', server_id) - - return webob.Response(status_int=202) - - def _items(self, req, server_id, entity_maker): - """Returns a list of attachments, transformed through entity_maker.""" - context = req.environ['nova.context'] - authorize(context) - instance = common.get_instance(self.compute_api, context, server_id) - search_opts = {'device_id': instance.uuid} - - try: - data = self.network_api.list_ports(context, **search_opts) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except NotImplementedError: - msg = _("Network driver does not support this function.") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - ports = data.get('ports', []) - results = [entity_maker(port) for port in ports] - - return {'interfaceAttachments': results} - - -class Attach_interfaces(extensions.ExtensionDescriptor): - """Attach interface support.""" - - name = "AttachInterfaces" - alias = "os-attach-interfaces" - namespace = "http://docs.openstack.org/compute/ext/interfaces/api/v1.1" - updated = "2012-07-22T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-interface', - InterfaceAttachmentController(), - parent=dict( - member_name='server', - collection_name='servers')) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/availability_zone.py b/nova/api/openstack/compute/legacy_v2/contrib/availability_zone.py deleted file mode 100644 index 490805f428f..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/availability_zone.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 oslo_config import cfg - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import availability_zones -from nova import context as nova_context -from nova import objects -from nova import servicegroup - -CONF = cfg.CONF - -authorize_list = extensions.extension_authorizer('compute', - 'availability_zone:list') -authorize_detail = extensions.extension_authorizer('compute', - 'availability_zone:detail') - - -class AvailabilityZoneController(wsgi.Controller): - """The Availability Zone API controller for the OpenStack API.""" - - def __init__(self): - super(AvailabilityZoneController, self).__init__() - self.servicegroup_api = servicegroup.API() - - def _get_filtered_availability_zones(self, zones, is_available): - result = [] - for zone in zones: - # Hide internal_service_availability_zone - if zone == CONF.internal_service_availability_zone: - continue - result.append({'zoneName': zone, - 'zoneState': {'available': is_available}, - "hosts": None}) - return result - - def _describe_availability_zones(self, context, **kwargs): - ctxt = context.elevated() - available_zones, not_available_zones = \ - availability_zones.get_availability_zones(ctxt) - - filtered_available_zones = \ - self._get_filtered_availability_zones(available_zones, True) - filtered_not_available_zones = \ - self._get_filtered_availability_zones(not_available_zones, False) - return {'availabilityZoneInfo': filtered_available_zones + - filtered_not_available_zones} - - def _describe_availability_zones_verbose(self, context, **kwargs): - ctxt = context.elevated() - available_zones, not_available_zones = \ - availability_zones.get_availability_zones(ctxt) - - # Available services - enabled_services = objects.ServiceList.get_all(context, disabled=False, - set_zones=True) - zone_hosts = {} - host_services = {} - api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata') - for service in enabled_services: - if service.binary in api_services: - # Skip API services in the listing since they are not - # maintained in the same way as other services - continue - zone_hosts.setdefault(service['availability_zone'], []) - if service['host'] not in zone_hosts[service['availability_zone']]: - zone_hosts[service['availability_zone']].append( - service['host']) - - host_services.setdefault(service['availability_zone'] + - service['host'], []) - host_services[service['availability_zone'] + service['host']].\ - append(service) - - result = [] - for zone in available_zones: - hosts = {} - for host in zone_hosts.get(zone, []): - hosts[host] = {} - for service in host_services[zone + host]: - alive = self.servicegroup_api.service_is_up(service) - hosts[host][service['binary']] = {'available': alive, - 'active': True != service['disabled'], - 'updated_at': service['updated_at']} - result.append({'zoneName': zone, - 'zoneState': {'available': True}, - "hosts": hosts}) - - for zone in not_available_zones: - result.append({'zoneName': zone, - 'zoneState': {'available': False}, - "hosts": None}) - return {'availabilityZoneInfo': result} - - def index(self, req): - """Returns a summary list of availability zone.""" - context = req.environ['nova.context'] - authorize_list(context) - - return self._describe_availability_zones(context) - - def detail(self, req): - """Returns a detailed list of availability zone.""" - context = req.environ['nova.context'] - authorize_detail(context) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - return self._describe_availability_zones_verbose(context) - - -class Availability_zone(extensions.ExtensionDescriptor): - """1. Add availability_zone to the Create Server v1.1 API. - 2. Add availability zones describing. - """ - - name = "AvailabilityZone" - alias = "os-availability-zone" - namespace = ("http://docs.openstack.org/compute/ext/" - "availabilityzone/api/v1.1") - updated = "2012-12-21T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-availability-zone', - AvailabilityZoneController(), - collection_actions={'detail': 'GET'}) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/baremetal_ext_status.py b/nova/api/openstack/compute/legacy_v2/contrib/baremetal_ext_status.py deleted file mode 100644 index 0f6c3a59402..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/baremetal_ext_status.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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 nova.api.openstack import extensions - - -class Baremetal_ext_status(extensions.ExtensionDescriptor): - """Add extended status in Baremetal Nodes v2 API.""" - - name = "BareMetalExtStatus" - alias = "os-baremetal-ext-status" - namespace = ("http://docs.openstack.org/compute/ext/" - "baremetal_ext_status/api/v2") - updated = "2013-08-27T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/baremetal_nodes.py b/nova/api/openstack/compute/legacy_v2/contrib/baremetal_nodes.py deleted file mode 100644 index ad1fda83507..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/baremetal_nodes.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright (c) 2013 NTT DOCOMO, 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. - -"""The bare-metal admin extension with Ironic Proxy.""" - -from oslo_utils import importutils -import webob - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -import nova.conf -from nova.i18n import _ - -ironic_client = importutils.try_import('ironicclient.client') -ironic_exc = importutils.try_import('ironicclient.exc') - -authorize = extensions.extension_authorizer('compute', 'baremetal_nodes') - -node_fields = ['id', 'cpus', 'local_gb', 'memory_mb', 'pm_address', - 'pm_user', 'service_host', 'terminal_port', 'instance_uuid'] - -node_ext_fields = ['uuid', 'task_state', 'updated_at', 'pxe_config_path'] - -interface_fields = ['id', 'address', 'datapath_id', 'port_no'] - -CONF = nova.conf.CONF -CONF.import_opt('api_version', - 'nova.virt.ironic.driver', - group='ironic') -CONF.import_opt('api_endpoint', - 'nova.virt.ironic.driver', - group='ironic') -CONF.import_opt('admin_username', - 'nova.virt.ironic.driver', - group='ironic') -CONF.import_opt('admin_password', - 'nova.virt.ironic.driver', - group='ironic') -CONF.import_opt('admin_tenant_name', - 'nova.virt.ironic.driver', - group='ironic') - - -def _check_ironic_client_enabled(): - """Check whether Ironic is installed or not.""" - if ironic_client is None: - msg = _("Ironic client unavailable, cannot access Ironic.") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - -def _get_ironic_client(): - """return an Ironic client.""" - # TODO(NobodyCam): Fix insecure setting - kwargs = {'os_username': CONF.ironic.admin_username, - 'os_password': CONF.ironic.admin_password, - 'os_auth_url': CONF.ironic.admin_url, - 'os_tenant_name': CONF.ironic.admin_tenant_name, - 'os_service_type': 'baremetal', - 'os_endpoint_type': 'public', - 'insecure': 'true', - 'ironic_url': CONF.ironic.api_endpoint} - ironicclient = ironic_client.get_client(CONF.ironic.api_version, **kwargs) - return ironicclient - - -def _no_ironic_proxy(cmd): - raise webob.exc.HTTPBadRequest( - explanation=_("Command Not supported. Please use Ironic " - "command %(cmd)s to perform this " - "action.") % {'cmd': cmd}) - - -class BareMetalNodeController(wsgi.Controller): - """The Bare-Metal Node API controller for the OpenStack API. - - Ironic is used for the following commands: - 'baremetal-node-list' - 'baremetal-node-show' - """ - - def __init__(self, ext_mgr=None, *args, **kwargs): - super(BareMetalNodeController, self).__init__(*args, **kwargs) - self.ext_mgr = ext_mgr - - def _node_dict(self, node_ref): - d = {} - for f in node_fields: - d[f] = node_ref.get(f) - if self.ext_mgr.is_loaded('os-baremetal-ext-status'): - for f in node_ext_fields: - d[f] = node_ref.get(f) - return d - - def index(self, req): - context = req.environ['nova.context'] - authorize(context) - nodes = [] - # proxy command to Ironic - _check_ironic_client_enabled() - ironicclient = _get_ironic_client() - ironic_nodes = ironicclient.node.list(detail=True) - for inode in ironic_nodes: - node = {'id': inode.uuid, - 'interfaces': [], - 'host': 'IRONIC MANAGED', - 'task_state': inode.provision_state, - 'cpus': inode.properties.get('cpus', 0), - 'memory_mb': inode.properties.get('memory_mb', 0), - 'disk_gb': inode.properties.get('local_gb', 0)} - nodes.append(node) - return {'nodes': nodes} - - def show(self, req, id): - context = req.environ['nova.context'] - authorize(context) - # proxy command to Ironic - _check_ironic_client_enabled() - icli = _get_ironic_client() - try: - inode = icli.node.get(id) - except ironic_exc.NotFound: - msg = _("Node %s could not be found.") % id - raise webob.exc.HTTPNotFound(explanation=msg) - iports = icli.node.list_ports(id) - node = {'id': inode.uuid, - 'interfaces': [], - 'host': 'IRONIC MANAGED', - 'task_state': inode.provision_state, - 'cpus': inode.properties.get('cpus', 0), - 'memory_mb': inode.properties.get('memory_mb', 0), - 'disk_gb': inode.properties.get('local_gb', 0), - 'instance_uuid': inode.instance_uuid} - for port in iports: - node['interfaces'].append({'address': port.address}) - return {'node': node} - - def create(self, req, body): - _no_ironic_proxy("port-create") - - def delete(self, req, id): - _no_ironic_proxy("port-create") - - @wsgi.action('add_interface') - def _add_interface(self, req, id, body): - _no_ironic_proxy("port-create") - - @wsgi.action('remove_interface') - def _remove_interface(self, req, id, body): - _no_ironic_proxy("port-delete") - - -class Baremetal_nodes(extensions.ExtensionDescriptor): - """Admin-only bare-metal node administration.""" - - name = "BareMetalNodes" - alias = "os-baremetal-nodes" - namespace = "http://docs.openstack.org/compute/ext/baremetal_nodes/api/v2" - updated = "2013-01-04T00:00:00Z" - - def get_resources(self): - resources = [] - res = extensions.ResourceExtension('os-baremetal-nodes', - BareMetalNodeController(self.ext_mgr), - member_actions={"action": "POST", }) - resources.append(res) - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/block_device_mapping_v2_boot.py b/nova/api/openstack/compute/legacy_v2/contrib/block_device_mapping_v2_boot.py deleted file mode 100644 index 463a9da7024..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/block_device_mapping_v2_boot.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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 nova.api.openstack import extensions - - -class Block_device_mapping_v2_boot(extensions.ExtensionDescriptor): - """Allow boot with the new BDM data format.""" - - name = "BlockDeviceMappingV2Boot" - alias = "os-block-device-mapping-v2-boot" - namespace = ("http://docs.openstack.org/compute/ext/" - "block_device_mapping_v2_boot/api/v2") - updated = "2013-07-08T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/cell_capacities.py b/nova/api/openstack/compute/legacy_v2/contrib/cell_capacities.py deleted file mode 100644 index 179d4f6a3bd..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/cell_capacities.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2013 Rackspace Hosting -# -# 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 nova.api.openstack import extensions - - -class Cell_capacities(extensions.ExtensionDescriptor): - """Adding functionality to get cell capacities.""" - - name = "CellCapacities" - alias = "os-cell-capacities" - namespace = ("http://docs.openstack.org/compute/ext/" - "cell_capacities/api/v1.1") - updated = "2013-05-27T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/cells.py b/nova/api/openstack/compute/legacy_v2/contrib/cells.py deleted file mode 100644 index 4c09d312396..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/cells.py +++ /dev/null @@ -1,370 +0,0 @@ -# Copyright 2011-2012 OpenStack Foundation -# All Rights Reserved. -# Copyright 2013 Red Hat, Inc. -# -# 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. - -"""The cells extension.""" - -import oslo_messaging as messaging -from oslo_utils import strutils -from oslo_utils import timeutils -import six -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.cells import rpcapi as cells_rpcapi -import nova.conf -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import rpc - - -CONF = nova.conf.CONF - -authorize = extensions.extension_authorizer('compute', 'cells') - - -def _filter_keys(item, keys): - """Filters all model attributes except for keys - item is a dict - - """ - return {k: v for k, v in six.iteritems(item) if k in keys} - - -def _fixup_cell_info(cell_info, keys): - """If the transport_url is present in the cell, derive username, - rpc_host, and rpc_port from it. - """ - - if 'transport_url' not in cell_info: - return - - # Disassemble the transport URL - transport_url = cell_info.pop('transport_url') - try: - transport_url = rpc.get_transport_url(transport_url) - except messaging.InvalidTransportURL: - # Just go with None's - for key in keys: - cell_info.setdefault(key, None) - return - - if not transport_url.hosts: - return - - transport_host = transport_url.hosts[0] - - transport_field_map = {'rpc_host': 'hostname', 'rpc_port': 'port'} - for key in keys: - if key in cell_info: - continue - - transport_field = transport_field_map.get(key, key) - cell_info[key] = getattr(transport_host, transport_field) - - -def _scrub_cell(cell, detail=False): - keys = ['name', 'username', 'rpc_host', 'rpc_port'] - if detail: - keys.append('capabilities') - - cell_info = _filter_keys(cell, keys + ['transport_url']) - _fixup_cell_info(cell_info, keys) - cell_info['type'] = 'parent' if cell['is_parent'] else 'child' - return cell_info - - -class Controller(object): - """Controller for Cell resources.""" - - def __init__(self, ext_mgr): - self.cells_rpcapi = cells_rpcapi.CellsAPI() - self.ext_mgr = ext_mgr - - def _get_cells(self, ctxt, req, detail=False): - """Return all cells.""" - # Ask the CellsManager for the most recent data - items = self.cells_rpcapi.get_cell_info_for_neighbors(ctxt) - items = common.limited(items, req) - items = [_scrub_cell(item, detail=detail) for item in items] - return dict(cells=items) - - @common.check_cells_enabled - def index(self, req): - """Return all cells in brief.""" - ctxt = req.environ['nova.context'] - authorize(ctxt) - return self._get_cells(ctxt, req) - - @common.check_cells_enabled - def detail(self, req): - """Return all cells in detail.""" - ctxt = req.environ['nova.context'] - authorize(ctxt) - return self._get_cells(ctxt, req, detail=True) - - @common.check_cells_enabled - def info(self, req): - """Return name and capabilities for this cell.""" - context = req.environ['nova.context'] - authorize(context) - cell_capabs = {} - my_caps = CONF.cells.capabilities - for cap in my_caps: - key, value = cap.split('=') - cell_capabs[key] = value - cell = {'name': CONF.cells.name, - 'type': 'self', - 'rpc_host': None, - 'rpc_port': 0, - 'username': None, - 'capabilities': cell_capabs} - return dict(cell=cell) - - @common.check_cells_enabled - def capacities(self, req, id=None): - """Return capacities for a given cell or all cells.""" - # TODO(kaushikc): return capacities as a part of cell info and - # cells detail calls in v2.1, along with capabilities - if not self.ext_mgr.is_loaded('os-cell-capacities'): - raise exc.HTTPNotFound() - - context = req.environ['nova.context'] - authorize(context) - try: - capacities = self.cells_rpcapi.get_capacities(context, - cell_name=id) - except exception.CellNotFound: - msg = (_("Cell %(id)s not found.") % {'id': id}) - raise exc.HTTPNotFound(explanation=msg) - - return dict(cell={"capacities": capacities}) - - @common.check_cells_enabled - def show(self, req, id): - """Return data about the given cell name. 'id' is a cell name.""" - context = req.environ['nova.context'] - authorize(context) - try: - cell = self.cells_rpcapi.cell_get(context, id) - except exception.CellNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - return dict(cell=_scrub_cell(cell)) - - @common.check_cells_enabled - def delete(self, req, id): - """Delete a child or parent cell entry. 'id' is a cell name.""" - context = req.environ['nova.context'] - - authorize(context) - authorize(context, action="delete") - # NOTE(eliqiao): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - - try: - num_deleted = self.cells_rpcapi.cell_delete(context, id) - except exception.CellsUpdateUnsupported as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - if num_deleted == 0: - raise exc.HTTPNotFound() - return {} - - def _validate_cell_name(self, cell_name): - """Validate cell name is not empty and doesn't contain '!', - '.' or '@'. - """ - if not cell_name: - msg = _("Cell name cannot be empty") - raise exc.HTTPBadRequest(explanation=msg) - if '!' in cell_name or '.' in cell_name or '@' in cell_name: - msg = _("Cell name cannot contain '!', '.' or '@'") - raise exc.HTTPBadRequest(explanation=msg) - - def _validate_cell_type(self, cell_type): - """Validate cell_type is 'parent' or 'child'.""" - if cell_type not in ['parent', 'child']: - msg = _("Cell type must be 'parent' or 'child'") - raise exc.HTTPBadRequest(explanation=msg) - - def _normalize_cell(self, cell, existing=None): - """Normalize input cell data. Normalizations include: - - * Converting cell['type'] to is_parent boolean. - * Merging existing transport URL with transport information. - """ - - # Start with the cell type conversion - if 'type' in cell: - self._validate_cell_type(cell['type']) - cell['is_parent'] = cell['type'] == 'parent' - del cell['type'] - # Avoid cell type being overwritten to 'child' - elif existing: - cell['is_parent'] = existing['is_parent'] - else: - cell['is_parent'] = False - - # Now we disassemble the existing transport URL... - transport_url = existing.get('transport_url') if existing else None - transport_url = rpc.get_transport_url(transport_url) - - if 'rpc_virtual_host' in cell: - transport_url.virtual_host = cell.pop('rpc_virtual_host') - - if not transport_url.hosts: - transport_url.hosts.append(messaging.TransportHost()) - transport_host = transport_url.hosts[0] - if cell.get('rpc_port') is not None: - try: - cell['rpc_port'] = int(cell['rpc_port']) - except ValueError: - raise exc.HTTPBadRequest( - explanation=_('rpc_port must be integer')) - # Copy over the input fields - transport_field_map = { - 'username': 'username', - 'password': 'password', - 'hostname': 'rpc_host', - 'port': 'rpc_port', - } - for key, input_field in transport_field_map.items(): - # Only override the value if we're given an override - if input_field in cell: - setattr(transport_host, key, cell.pop(input_field)) - - # Now set the transport URL - cell['transport_url'] = str(transport_url) - - @common.check_cells_enabled - def create(self, req, body): - """Create a child cell entry.""" - context = req.environ['nova.context'] - - authorize(context) - authorize(context, action="create") - # NOTE(eliqiao): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - - if 'cell' not in body: - msg = _("No cell information in request") - raise exc.HTTPBadRequest(explanation=msg) - cell = body['cell'] - if 'name' not in cell: - msg = _("No cell name in request") - raise exc.HTTPBadRequest(explanation=msg) - self._validate_cell_name(cell['name']) - self._normalize_cell(cell) - try: - cell = self.cells_rpcapi.cell_create(context, cell) - except exception.CellsUpdateUnsupported as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - return dict(cell=_scrub_cell(cell)) - - @common.check_cells_enabled - def update(self, req, id, body): - """Update a child cell entry. 'id' is the cell name to update.""" - context = req.environ['nova.context'] - - authorize(context) - authorize(context, action="update") - # NOTE(eliqiao): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - - if 'cell' not in body: - msg = _("No cell information in request") - raise exc.HTTPBadRequest(explanation=msg) - cell = body['cell'] - cell.pop('id', None) - if 'name' in cell: - self._validate_cell_name(cell['name']) - try: - # NOTE(Vek): There is a race condition here if multiple - # callers are trying to update the cell - # information simultaneously. Since this - # operation is administrative in nature, and - # will be going away in the future, I don't see - # it as much of a problem... - existing = self.cells_rpcapi.cell_get(context, id) - except exception.CellNotFound: - raise exc.HTTPNotFound() - self._normalize_cell(cell, existing) - try: - cell = self.cells_rpcapi.cell_update(context, id, cell) - except exception.CellNotFound: - raise exc.HTTPNotFound() - except exception.CellsUpdateUnsupported as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - return dict(cell=_scrub_cell(cell)) - - @common.check_cells_enabled - def sync_instances(self, req, body): - """Tell all cells to sync instance info.""" - context = req.environ['nova.context'] - - authorize(context) - authorize(context, action="sync_instances") - - project_id = body.pop('project_id', None) - deleted = body.pop('deleted', False) - updated_since = body.pop('updated_since', None) - if body: - msg = _("Only 'updated_since', 'project_id' and 'deleted' are " - "understood.") - raise exc.HTTPBadRequest(explanation=msg) - if isinstance(deleted, six.string_types): - try: - deleted = strutils.bool_from_string(deleted, strict=True) - except ValueError as err: - raise exc.HTTPBadRequest(explanation=six.text_type(err)) - if updated_since: - try: - timeutils.parse_isotime(updated_since) - except ValueError: - msg = _('Invalid changes-since value') - raise exc.HTTPBadRequest(explanation=msg) - self.cells_rpcapi.sync_instances(context, project_id=project_id, - updated_since=updated_since, deleted=deleted) - - -class Cells(extensions.ExtensionDescriptor): - """Enables cells-related functionality such as adding neighbor cells, - listing neighbor cells, and getting the capabilities of the local cell. - """ - - name = "Cells" - alias = "os-cells" - namespace = "http://docs.openstack.org/compute/ext/cells/api/v1.1" - updated = "2013-05-14T00:00:00Z" - - def get_resources(self): - coll_actions = { - 'detail': 'GET', - 'info': 'GET', - 'sync_instances': 'POST', - 'capacities': 'GET', - } - memb_actions = { - 'capacities': 'GET', - } - - res = extensions.ResourceExtension('os-cells', - Controller(self.ext_mgr), collection_actions=coll_actions, - member_actions=memb_actions) - return [res] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/certificates.py b/nova/api/openstack/compute/legacy_v2/contrib/certificates.py deleted file mode 100644 index 1b30c6600a4..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/certificates.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2012 OpenStack Foundation -# -# 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 webob.exc - -from nova.api.openstack import extensions -import nova.cert.rpcapi -from nova import exception -from nova.i18n import _ - -authorize = extensions.extension_authorizer('compute', 'certificates') - - -def _translate_certificate_view(certificate, private_key=None): - return { - 'data': certificate, - 'private_key': private_key, - } - - -class CertificatesController(object): - """The x509 Certificates API controller for the OpenStack API.""" - - def __init__(self): - self.cert_rpcapi = nova.cert.rpcapi.CertAPI() - super(CertificatesController, self).__init__() - - def show(self, req, id): - """Return certificate information.""" - context = req.environ['nova.context'] - authorize(context) - if id != 'root': - msg = _("Only root certificate can be retrieved.") - raise webob.exc.HTTPNotImplemented(explanation=msg) - try: - cert = self.cert_rpcapi.fetch_ca(context, - project_id=context.project_id) - except exception.CryptoCAFileNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - return {'certificate': _translate_certificate_view(cert)} - - def create(self, req, body=None): - """Create a certificate.""" - context = req.environ['nova.context'] - authorize(context) - pk, cert = self.cert_rpcapi.generate_x509_cert(context, - user_id=context.user_id, project_id=context.project_id) - return {'certificate': _translate_certificate_view(cert, pk)} - - -class Certificates(extensions.ExtensionDescriptor): - """Certificates support.""" - - name = "Certificates" - alias = "os-certificates" - namespace = ("http://docs.openstack.org/compute/ext/" - "certificates/api/v1.1") - updated = "2012-01-19T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-certificates', - CertificatesController(), - member_actions={}) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe.py b/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe.py deleted file mode 100644 index 1b51e3df448..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# 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. - -"""Connect your vlan to the world.""" - -from oslo_config import cfg -from oslo_utils import fileutils -from webob import exc - -from nova.api.openstack import extensions -from nova.cloudpipe import pipelib -from nova import compute -from nova.compute import utils as compute_utils -from nova.compute import vm_states -from nova import exception -from nova.i18n import _ -from nova import network -from nova import utils - -CONF = cfg.CONF -CONF.import_opt('keys_path', 'nova.crypto') - -authorize = extensions.extension_authorizer('compute', 'cloudpipe') - - -class CloudpipeController(object): - """Handle creating and listing cloudpipe instances.""" - - def __init__(self): - self.compute_api = compute.API() - self.network_api = network.API() - self.cloudpipe = pipelib.CloudPipe() - self.setup() - - def setup(self): - """Ensure the keychains and folders exist.""" - # NOTE(vish): One of the drawbacks of doing this in the api is - # the keys will only be on the api node that launched - # the cloudpipe. - fileutils.ensure_tree(CONF.keys_path) - - def _get_all_cloudpipes(self, context): - """Get all cloudpipes.""" - instances = self.compute_api.get_all(context, - search_opts={'deleted': False}, - want_objects=True) - return [instance for instance in instances - if pipelib.is_vpn_image(instance.image_ref) - and instance.vm_state != vm_states.DELETED] - - def _get_cloudpipe_for_project(self, context): - """Get the cloudpipe instance for a project from context.""" - cloudpipes = self._get_all_cloudpipes(context) or [None] - return cloudpipes[0] - - def _vpn_dict(self, context, project_id, instance): - elevated = context.elevated() - rv = {'project_id': project_id} - if not instance: - rv['state'] = 'pending' - return rv - rv['instance_id'] = instance.uuid - rv['created_at'] = utils.isotime(instance.created_at) - nw_info = compute_utils.get_nw_info_for_instance(instance) - if not nw_info: - return rv - vif = nw_info[0] - ips = [ip for ip in vif.fixed_ips() if ip['version'] == 4] - if ips: - rv['internal_ip'] = ips[0]['address'] - # NOTE(vish): Currently network_api.get does an owner check on - # project_id. This is probably no longer necessary - # but rather than risk changes in the db layer, - # we are working around it here by changing the - # project_id in the context. This can be removed - # if we remove the project_id check in the db. - elevated.project_id = project_id - network = self.network_api.get(elevated, vif['network']['id']) - if network: - vpn_ip = network['vpn_public_address'] - vpn_port = network['vpn_public_port'] - rv['public_ip'] = vpn_ip - rv['public_port'] = vpn_port - if vpn_ip and vpn_port: - if utils.vpn_ping(vpn_ip, vpn_port): - rv['state'] = 'running' - else: - rv['state'] = 'down' - else: - rv['state'] = 'invalid' - return rv - - def create(self, req, body): - """Create a new cloudpipe instance, if none exists. - - Parameters: {cloudpipe: {'project_id': ''}} - """ - - context = req.environ['nova.context'] - authorize(context) - params = body.get('cloudpipe', {}) - project_id = params.get('project_id', context.project_id) - # NOTE(vish): downgrade to project context. Note that we keep - # the same token so we can still talk to glance - context.project_id = project_id - context.user_id = 'project-vpn' - context.is_admin = False - context.roles = [] - instance = self._get_cloudpipe_for_project(context) - if not instance: - try: - result = self.cloudpipe.launch_vpn_instance(context) - instance = result[0][0] - except exception.NoMoreNetworks: - msg = _("Unable to claim IP for VPN instances, ensure it " - "isn't running, and try again in a few minutes") - raise exc.HTTPBadRequest(explanation=msg) - return {'instance_id': instance.uuid} - - def index(self, req): - """List running cloudpipe instances.""" - context = req.environ['nova.context'] - authorize(context) - vpns = [self._vpn_dict(context, x['project_id'], x) - for x in self._get_all_cloudpipes(context)] - return {'cloudpipes': vpns} - - -class Cloudpipe(extensions.ExtensionDescriptor): - """Adds actions to create cloudpipe instances. - - When running with the Vlan network mode, you need a mechanism to route - from the public Internet to your vlans. This mechanism is known as a - cloudpipe. - - At the time of creating this class, only OpenVPN is supported. Support for - a SSH Bastion host is forthcoming. - """ - - name = "Cloudpipe" - alias = "os-cloudpipe" - namespace = "http://docs.openstack.org/compute/ext/cloudpipe/api/v1.1" - updated = "2011-12-16T00:00:00Z" - - def get_resources(self): - resources = [] - res = extensions.ResourceExtension('os-cloudpipe', - CloudpipeController()) - resources.append(res) - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe_update.py b/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe_update.py deleted file mode 100644 index 8e628790629..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe_update.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright 2012 IBM Corp. -# -# 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 webob -import webob.exc - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova.i18n import _ -from nova import objects - -authorize = extensions.extension_authorizer('compute', 'cloudpipe_update') - - -class CloudpipeUpdateController(wsgi.Controller): - """Handle updating the VPN IP/port for cloudpipe instances.""" - - def __init__(self): - super(CloudpipeUpdateController, self).__init__() - - @wsgi.action("update") - def update(self, req, id, body): - """Configure cloudpipe parameters for the project.""" - - context = req.environ['nova.context'] - authorize(context) - - if id != "configure-project": - msg = _("Unknown action %s") % id - raise webob.exc.HTTPBadRequest(explanation=msg) - - project_id = context.project_id - networks = objects.NetworkList.get_by_project(context, project_id) - - try: - params = body['configure_project'] - vpn_ip = params['vpn_ip'] - vpn_port = params['vpn_port'] - for network in networks: - network.vpn_public_address = vpn_ip - network.vpn_public_port = vpn_port - network.save() - except (TypeError, KeyError, ValueError) as ex: - msg = _("Invalid request body: %s") % ex - raise webob.exc.HTTPBadRequest(explanation=msg) - - return webob.Response(status_int=202) - - -class Cloudpipe_update(extensions.ExtensionDescriptor): - """Adds the ability to set the VPN IP/port for cloudpipe instances.""" - - name = "CloudpipeUpdate" - alias = "os-cloudpipe-update" - namespace = "http://docs.openstack.org/compute/ext/cloudpipe-update/api/v2" - updated = "2012-11-14T00:00:00Z" - - def get_controller_extensions(self): - controller = CloudpipeUpdateController() - extension = extensions.ControllerExtension(self, 'os-cloudpipe', - controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/config_drive.py b/nova/api/openstack/compute/legacy_v2/contrib/config_drive.py deleted file mode 100644 index bd7ff41915f..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/config_drive.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# 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. - -"""Config Drive extension.""" - -from nova.api.openstack.compute.legacy_v2 import servers -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - -authorize = extensions.soft_extension_authorizer('compute', 'config_drive') - - -class Controller(servers.Controller): - - def _add_config_drive(self, req, servers): - for server in servers: - db_server = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show'/'detail' methods. - server['config_drive'] = db_server['config_drive'] - - def _show(self, req, resp_obj): - if 'server' in resp_obj.obj: - server = resp_obj.obj['server'] - self._add_config_drive(req, [server]) - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - self._show(req, resp_obj) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if 'servers' in resp_obj.obj and authorize(context): - servers = resp_obj.obj['servers'] - self._add_config_drive(req, servers) - - -class Config_drive(extensions.ExtensionDescriptor): - """Config Drive Extension.""" - - name = "ConfigDrive" - alias = "os-config-drive" - namespace = "http://docs.openstack.org/compute/ext/config_drive/api/v1.1" - updated = "2012-07-16T00:00:00Z" - - def get_controller_extensions(self): - controller = Controller(self.ext_mgr) - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/console_auth_tokens.py b/nova/api/openstack/compute/legacy_v2/contrib/console_auth_tokens.py deleted file mode 100644 index 9b41f41bf4c..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/console_auth_tokens.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# 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 webob - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova.consoleauth import rpcapi as consoleauth_rpcapi -from nova.i18n import _ - - -authorize = extensions.extension_authorizer('compute', 'console_auth_tokens') - - -class ConsoleAuthTokensController(wsgi.Controller): - def __init__(self, *args, **kwargs): - self._consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI() - super(ConsoleAuthTokensController, self).__init__(*args, **kwargs) - - def show(self, req, id): - """Checks a console auth token and returns the related connect info.""" - context = req.environ['nova.context'] - authorize(context) - - token = id - connect_info = self._consoleauth_rpcapi.check_token(context, token) - if not connect_info: - raise webob.exc.HTTPNotFound(explanation=_("Token not found")) - - console_type = connect_info.get('console_type') - # This is currently required only for RDP consoles - if console_type != "rdp-html5": - raise webob.exc.HTTPUnauthorized( - explanation=_("The requested console type details are not " - "accessible")) - - return {'console': - {i: connect_info[i] - for i in ['instance_uuid', 'host', 'port', - 'internal_access_path'] - if i in connect_info}} - - -class Console_auth_tokens(extensions.ExtensionDescriptor): - """Console token authentication support.""" - name = "ConsoleAuthTokens" - alias = "os-console-auth-tokens" - namespace = ("http://docs.openstack.org/compute/ext/" - "consoles-auth-tokens/api/v2") - updated = "2013-08-13T00:00:00Z" - - def get_resources(self): - controller = ConsoleAuthTokensController() - ext = extensions.ResourceExtension('os-console-auth-tokens', - controller) - return [ext] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/console_output.py b/nova/api/openstack/compute/legacy_v2/contrib/console_output.py deleted file mode 100644 index 2956e12596a..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/console_output.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# Copyright 2011 Grid Dynamics -# Copyright 2011 Eldar Nugaev, Kirill Shileev, Ilya Alekseyev -# -# 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 re - -import webob - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova.i18n import _ - - -authorize = extensions.extension_authorizer('compute', 'console_output') - - -class ConsoleOutputController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ConsoleOutputController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - - @wsgi.action('os-getConsoleOutput') - def get_console_output(self, req, id, body): - """Get text console output.""" - context = req.environ['nova.context'] - authorize(context) - - instance = common.get_instance(self.compute_api, context, id) - try: - length = body['os-getConsoleOutput'].get('length') - except (TypeError, KeyError): - raise webob.exc.HTTPBadRequest(_('os-getConsoleOutput malformed ' - 'or missing from request body')) - - if length is not None: - try: - # NOTE(maurosr): cast length into a string before cast into an - # integer to avoid thing like: int(2.5) which is 2 instead of - # raise ValueError like it would when we try int("2.5"). This - # can be removed once we have api validation landed. - int(str(length)) - except ValueError: - raise webob.exc.HTTPBadRequest(_('Length in request body must ' - 'be an integer value')) - - try: - output = self.compute_api.get_console_output(context, - instance, - length) - except exception.ConsoleNotAvailable as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except exception.NotFound: - msg = _('Unable to get console') - raise webob.exc.HTTPNotFound(explanation=msg) - except exception.InstanceNotReady as e: - raise webob.exc.HTTPConflict(explanation=e.format_message()) - except NotImplementedError: - msg = _("Unable to get console log, functionality not implemented") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - # XML output is not correctly escaped, so remove invalid characters - remove_re = re.compile('[\x00-\x08\x0B-\x1F]') - output = remove_re.sub('', output) - - return {'output': output} - - -class Console_output(extensions.ExtensionDescriptor): - """Console log output support, with tailing ability.""" - - name = "ConsoleOutput" - alias = "os-console-output" - namespace = ("http://docs.openstack.org/compute/ext/" - "os-console-output/api/v2") - updated = "2011-12-08T00:00:00Z" - - def get_controller_extensions(self): - controller = ConsoleOutputController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/consoles.py b/nova/api/openstack/compute/legacy_v2/contrib/consoles.py deleted file mode 100644 index 0a8a00aac3e..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/consoles.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 webob - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova.i18n import _ - - -authorize = extensions.extension_authorizer('compute', 'consoles') - - -class ConsolesController(wsgi.Controller): - def __init__(self, *args, **kwargs): - self.compute_api = compute.API() - super(ConsolesController, self).__init__(*args, **kwargs) - - @wsgi.action('os-getVNCConsole') - def get_vnc_console(self, req, id, body): - """Get vnc connection information to access a server.""" - context = req.environ['nova.context'] - authorize(context) - - # If type is not supplied or unknown, get_vnc_console below will cope - console_type = body['os-getVNCConsole'].get('type') - instance = common.get_instance(self.compute_api, context, id) - - try: - output = self.compute_api.get_vnc_console(context, - instance, - console_type) - except exception.InstanceNotReady: - raise webob.exc.HTTPConflict( - explanation=_('Instance not yet ready')) - except exception.InstanceNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except (exception.ConsoleTypeUnavailable, - exception.ConsoleTypeInvalid) as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - except NotImplementedError: - msg = _("Unable to get vnc console, functionality not implemented") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - return {'console': {'type': console_type, 'url': output['url']}} - - @wsgi.action('os-getSPICEConsole') - def get_spice_console(self, req, id, body): - """Get spice connection information to access a server.""" - context = req.environ['nova.context'] - authorize(context) - - # If type is not supplied or unknown, get_spice_console below will cope - console_type = body['os-getSPICEConsole'].get('type') - instance = common.get_instance(self.compute_api, context, id) - - try: - output = self.compute_api.get_spice_console(context, - instance, - console_type) - except (exception.ConsoleTypeUnavailable, - exception.ConsoleTypeInvalid) as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - except exception.InstanceNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except exception.InstanceNotReady as e: - raise webob.exc.HTTPConflict(explanation=e.format_message()) - except NotImplementedError: - msg = _("Unable to get spice console, " - "functionality not implemented") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - return {'console': {'type': console_type, 'url': output['url']}} - - @wsgi.action('os-getRDPConsole') - def get_rdp_console(self, req, id, body): - """Get text console output.""" - context = req.environ['nova.context'] - authorize(context) - - # If type is not supplied or unknown, get_rdp_console below will cope - console_type = body['os-getRDPConsole'].get('type') - instance = common.get_instance(self.compute_api, context, id) - - try: - output = self.compute_api.get_rdp_console(context, - instance, - console_type) - except (exception.ConsoleTypeUnavailable, - exception.ConsoleTypeInvalid) as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - except exception.InstanceNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except exception.InstanceNotReady as e: - raise webob.exc.HTTPConflict(explanation=e.format_message()) - except NotImplementedError: - msg = _("Unable to get rdp console, functionality not implemented") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - return {'console': {'type': console_type, 'url': output['url']}} - - @wsgi.action('os-getSerialConsole') - def get_serial_console(self, req, id, body): - """Get connection to a serial console.""" - context = req.environ['nova.context'] - authorize(context) - - # If type is not supplied or unknown get_serial_console below will cope - console_type = body['os-getSerialConsole'].get('type') - instance = common.get_instance(self.compute_api, context, id) - try: - output = self.compute_api.get_serial_console(context, - instance, - console_type) - except exception.InstanceNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except exception.InstanceNotReady as e: - raise webob.exc.HTTPConflict(explanation=e.format_message()) - except (exception.ConsoleTypeUnavailable, - exception.ConsoleTypeInvalid, - exception.ImageSerialPortNumberInvalid, - exception.ImageSerialPortNumberExceedFlavorValue, - exception.SocketPortRangeExhaustedException) as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - except NotImplementedError: - msg = _("Unable to get serial console, " - "functionality not implemented") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - return {'console': {'type': console_type, 'url': output['url']}} - - -class Consoles(extensions.ExtensionDescriptor): - """Interactive Console support.""" - name = "Consoles" - alias = "os-consoles" - namespace = "http://docs.openstack.org/compute/ext/os-consoles/api/v2" - updated = "2011-12-23T00:00:00Z" - - def get_controller_extensions(self): - controller = ConsolesController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/createserverext.py b/nova/api/openstack/compute/legacy_v2/contrib/createserverext.py deleted file mode 100644 index dd8f1e7dc34..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/createserverext.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# 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 nova.api.openstack import extensions - - -class Createserverext(extensions.ExtensionDescriptor): - """Extended support to the Create Server v1.1 API.""" - - name = "Createserverext" - alias = "os-create-server-ext" - namespace = ("http://docs.openstack.org/compute/ext/" - "createserverext/api/v1.1") - updated = "2011-07-19T00:00:00Z" - - def get_resources(self): - res = extensions.ResourceExtension('os-create-server-ext', - inherits='servers') - return [res] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/deferred_delete.py b/nova/api/openstack/compute/legacy_v2/contrib/deferred_delete.py deleted file mode 100644 index 277d70c4439..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/deferred_delete.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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. - -"""The deferred instance delete extension.""" - -import webob - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception - - -authorize = extensions.extension_authorizer('compute', 'deferred_delete') - - -class DeferredDeleteController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(DeferredDeleteController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - - @wsgi.action('restore') - def _restore(self, req, id, body): - """Restore a previously deleted instance.""" - context = req.environ["nova.context"] - authorize(context) - instance = common.get_instance(self.compute_api, context, id) - - try: - self.compute_api.restore(context, instance) - except exception.QuotaError as error: - raise webob.exc.HTTPForbidden(explanation=error.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'restore', id) - return webob.Response(status_int=202) - - @wsgi.action('forceDelete') - def _force_delete(self, req, id, body): - """Force delete of instance before deferred cleanup.""" - context = req.environ["nova.context"] - authorize(context) - instance = common.get_instance(self.compute_api, context, id) - - try: - self.compute_api.force_delete(context, instance) - except exception.InstanceIsLocked as e: - raise webob.exc.HTTPConflict(explanation=e.format_message()) - return webob.Response(status_int=202) - - -class Deferred_delete(extensions.ExtensionDescriptor): - """Instance deferred delete.""" - - name = "DeferredDelete" - alias = "os-deferred-delete" - namespace = ("http://docs.openstack.org/compute/ext/" - "deferred-delete/api/v1.1") - updated = "2011-09-01T00:00:00Z" - - def get_controller_extensions(self): - controller = DeferredDeleteController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/disk_config.py b/nova/api/openstack/compute/legacy_v2/contrib/disk_config.py deleted file mode 100644 index eb8b05aa015..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/disk_config.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# 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. - -"""Disk Config extension.""" - -from oslo_utils import strutils -from webob import exc - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova.i18n import _ - -ALIAS = 'OS-DCF' -XMLNS_DCF = "http://docs.openstack.org/compute/ext/disk_config/api/v1.1" -API_DISK_CONFIG = "%s:diskConfig" % ALIAS -INTERNAL_DISK_CONFIG = "auto_disk_config" -authorize = extensions.soft_extension_authorizer('compute', 'disk_config') - - -def disk_config_to_api(value): - return 'AUTO' if value else 'MANUAL' - - -def disk_config_from_api(value): - if value == 'AUTO': - return True - elif value == 'MANUAL': - return False - else: - msg = _("%s must be either 'MANUAL' or 'AUTO'.") % API_DISK_CONFIG - raise exc.HTTPBadRequest(explanation=msg) - - -class ImageDiskConfigController(wsgi.Controller): - def _add_disk_config(self, context, images): - for image in images: - metadata = image['metadata'] - if INTERNAL_DISK_CONFIG in metadata: - raw_value = metadata[INTERNAL_DISK_CONFIG] - value = strutils.bool_from_string(raw_value) - image[API_DISK_CONFIG] = disk_config_to_api(value) - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if 'image' in resp_obj.obj and authorize(context): - image = resp_obj.obj['image'] - self._add_disk_config(context, [image]) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if 'images' in resp_obj.obj and authorize(context): - images = resp_obj.obj['images'] - self._add_disk_config(context, images) - - -class ServerDiskConfigController(wsgi.Controller): - def _add_disk_config(self, req, servers): - for server in servers: - db_server = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show'/'detail' methods. - value = db_server.get(INTERNAL_DISK_CONFIG) - server[API_DISK_CONFIG] = disk_config_to_api(value) - - def _show(self, req, resp_obj): - if 'server' in resp_obj.obj: - server = resp_obj.obj['server'] - self._add_disk_config(req, [server]) - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - self._show(req, resp_obj) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if 'servers' in resp_obj.obj and authorize(context): - servers = resp_obj.obj['servers'] - self._add_disk_config(req, servers) - - def _set_disk_config(self, dict_): - if API_DISK_CONFIG in dict_: - api_value = dict_[API_DISK_CONFIG] - internal_value = disk_config_from_api(api_value) - dict_[INTERNAL_DISK_CONFIG] = internal_value - - @wsgi.extends - def create(self, req, body): - context = req.environ['nova.context'] - if authorize(context): - if 'server' in body: - self._set_disk_config(body['server']) - resp_obj = (yield) - self._show(req, resp_obj) - - @wsgi.extends - def update(self, req, id, body): - context = req.environ['nova.context'] - if authorize(context): - if 'server' in body: - self._set_disk_config(body['server']) - resp_obj = (yield) - self._show(req, resp_obj) - - @wsgi.extends(action='rebuild') - def _action_rebuild(self, req, id, body): - context = req.environ['nova.context'] - if authorize(context): - self._set_disk_config(body['rebuild']) - resp_obj = (yield) - self._show(req, resp_obj) - - @wsgi.extends(action='resize') - def _action_resize(self, req, id, body): - context = req.environ['nova.context'] - if authorize(context): - self._set_disk_config(body['resize']) - yield - - -class Disk_config(extensions.ExtensionDescriptor): - """Disk Management Extension.""" - - name = "DiskConfig" - alias = ALIAS - namespace = XMLNS_DCF - updated = "2011-09-27T00:00:00Z" - - def get_controller_extensions(self): - servers_extension = extensions.ControllerExtension( - self, 'servers', ServerDiskConfigController()) - - images_extension = extensions.ControllerExtension( - self, 'images', ImageDiskConfigController()) - - return [servers_extension, images_extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/evacuate.py b/nova/api/openstack/compute/legacy_v2/contrib/evacuate.py deleted file mode 100644 index 6b9dd015611..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/evacuate.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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 oslo_utils import strutils -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import utils - -authorize = extensions.extension_authorizer('compute', 'evacuate') - - -class Controller(wsgi.Controller): - def __init__(self, ext_mgr, *args, **kwargs): - super(Controller, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - self.host_api = compute.HostAPI() - self.ext_mgr = ext_mgr - - @wsgi.action('evacuate') - def _evacuate(self, req, id, body): - """Permit admins to evacuate a server from a failed host - to a new one. - If host is empty, the scheduler will select one. - """ - context = req.environ["nova.context"] - authorize(context) - - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - - if not self.is_valid_body(body, "evacuate"): - raise exc.HTTPBadRequest(_("Malformed request body")) - evacuate_body = body["evacuate"] - - host = evacuate_body.get("host") - - if (not host and - not self.ext_mgr.is_loaded('os-extended-evacuate-find-host')): - msg = _("host must be specified.") - raise exc.HTTPBadRequest(explanation=msg) - - try: - on_shared_storage = strutils.bool_from_string( - evacuate_body["onSharedStorage"]) - except (TypeError, KeyError): - msg = _("onSharedStorage must be specified.") - raise exc.HTTPBadRequest(explanation=msg) - - password = None - if 'adminPass' in evacuate_body: - # check that if requested to evacuate server on shared storage - # password not specified - if on_shared_storage: - msg = _("admin password can't be changed on existing disk") - raise exc.HTTPBadRequest(explanation=msg) - - password = evacuate_body['adminPass'] - elif not on_shared_storage: - password = utils.generate_password() - - if host is not None: - try: - self.host_api.service_get_by_compute_host(context, host) - except exception.NotFound: - msg = _("Compute host %s not found.") % host - raise exc.HTTPNotFound(explanation=msg) - - instance = common.get_instance(self.compute_api, context, id) - try: - if instance.host == host: - msg = _("The target host can't be the same one.") - raise exc.HTTPBadRequest(explanation=msg) - self.compute_api.evacuate(context, instance, host, - on_shared_storage, password) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'evacuate', id) - except exception.InstanceNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.ComputeServiceInUse as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - if password: - return {'adminPass': password} - - -class Evacuate(extensions.ExtensionDescriptor): - """Enables server evacuation.""" - - name = "Evacuate" - alias = "os-evacuate" - namespace = "http://docs.openstack.org/compute/ext/evacuate/api/v2" - updated = "2013-01-06T00:00:00Z" - - def get_controller_extensions(self): - controller = Controller(self.ext_mgr) - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_availability_zone.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_availability_zone.py deleted file mode 100644 index f5cdf6ee326..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_availability_zone.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2013 Netease, LLC. -# 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. - -"""The Extended Availability Zone Status API extension.""" - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import availability_zones as avail_zone - -authorize = extensions.soft_extension_authorizer('compute', - 'extended_availability_zone') - - -class ExtendedAZController(wsgi.Controller): - def _extend_server(self, context, server, instance): - key = "%s:availability_zone" % Extended_availability_zone.alias - az = avail_zone.get_instance_availability_zone(context, instance) - server[key] = az or '' - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - db_instance = req.get_db_instance(server['id']) - self._extend_server(context, server, db_instance) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if authorize(context): - servers = list(resp_obj.obj['servers']) - for server in servers: - db_instance = req.get_db_instance(server['id']) - self._extend_server(context, server, db_instance) - - -class Extended_availability_zone(extensions.ExtensionDescriptor): - """Extended Availability Zone support.""" - - name = "ExtendedAvailabilityZone" - alias = "OS-EXT-AZ" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_availability_zone/api/v2") - updated = "2013-01-30T00:00:00Z" - - def get_controller_extensions(self): - controller = ExtendedAZController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_evacuate_find_host.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_evacuate_find_host.py deleted file mode 100644 index 2dfe3faff55..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_evacuate_find_host.py +++ /dev/null @@ -1,26 +0,0 @@ -# -# 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 nova.api.openstack import extensions - - -class Extended_evacuate_find_host(extensions.ExtensionDescriptor): - """Enables server evacuation without target host. Scheduler will select - one to target. - """ - name = "ExtendedEvacuateFindHost" - alias = "os-extended-evacuate-find-host" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_evacuate_find_host/api/v2") - updated = "2014-02-12T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_floating_ips.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_floating_ips.py deleted file mode 100644 index 5ee55a81b94..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_floating_ips.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 nova.api.openstack import extensions - - -class Extended_floating_ips(extensions.ExtensionDescriptor): - """Adds optional fixed_address to the add floating IP command.""" - - name = "ExtendedFloatingIps" - alias = "os-extended-floating-ips" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_floating_ips/api/v2") - updated = "2013-04-19T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_hypervisors.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_hypervisors.py deleted file mode 100644 index 794c1295f0f..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_hypervisors.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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 nova.api.openstack import extensions - - -class Extended_hypervisors(extensions.ExtensionDescriptor): - """Extended hypervisors support.""" - - name = "ExtendedHypervisors" - alias = "os-extended-hypervisors" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_hypervisors/api/v1.1") - updated = "2014-01-04T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_ips.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_ips.py deleted file mode 100644 index 2b55509a451..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_ips.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright 2013 Nebula, Inc. -# -# 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. - -"""The Extended Ips API extension.""" - -import itertools - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute - -authorize = extensions.soft_extension_authorizer('compute', 'extended_ips') - - -class ExtendedIpsController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ExtendedIpsController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - - def _extend_server(self, context, server, instance): - key = "%s:type" % Extended_ips.alias - networks = common.get_networks_for_instance(context, instance) - for label, network in networks.items(): - # NOTE(vish): ips are hidden in some states via the - # hide_server_addresses extension. - if label in server['addresses']: - all_ips = itertools.chain(network["ips"], - network["floating_ips"]) - for i, ip in enumerate(all_ips): - server['addresses'][label][i][key] = ip['type'] - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show' method. - self._extend_server(context, server, db_instance) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if authorize(context): - servers = list(resp_obj.obj['servers']) - for server in servers: - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'detail' method. - self._extend_server(context, server, db_instance) - - -class Extended_ips(extensions.ExtensionDescriptor): - """Adds type parameter to the ip list.""" - - name = "ExtendedIps" - alias = "OS-EXT-IPS" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_ips/api/v1.1") - updated = "2013-01-06T00:00:00Z" - - def get_controller_extensions(self): - controller = ExtendedIpsController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_ips_mac.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_ips_mac.py deleted file mode 100644 index b5535603f8c..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_ips_mac.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - -"""The Extended Ips API extension.""" - -import itertools - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - -authorize = extensions.soft_extension_authorizer('compute', 'extended_ips_mac') - - -class ExtendedIpsMacController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ExtendedIpsMacController, self).__init__(*args, **kwargs) - - def _extend_server(self, context, server, instance): - key = "%s:mac_addr" % Extended_ips_mac.alias - networks = common.get_networks_for_instance(context, instance) - for label, network in networks.items(): - # NOTE(vish): ips are hidden in some states via the - # hide_server_addresses extension. - if label in server['addresses']: - all_ips = itertools.chain(network["ips"], - network["floating_ips"]) - for i, ip in enumerate(all_ips): - server['addresses'][label][i][key] = ip['mac_address'] - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show' method. - self._extend_server(context, server, db_instance) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if authorize(context): - servers = list(resp_obj.obj['servers']) - for server in servers: - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'detail' method. - self._extend_server(context, server, db_instance) - - -class Extended_ips_mac(extensions.ExtensionDescriptor): - """Adds mac address parameter to the ip list.""" - - name = "ExtendedIpsMac" - alias = "OS-EXT-IPS-MAC" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_ips_mac/api/v1.1") - updated = "2013-03-07T00:00:00Z" - - def get_controller_extensions(self): - controller = ExtendedIpsMacController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_networks.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_networks.py deleted file mode 100644 index f5021a48dc5..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_networks.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2014 Nebula, 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. - -from nova.api.openstack import extensions - - -class Extended_networks(extensions.ExtensionDescriptor): - """Adds additional fields to networks.""" - - name = "ExtendedNetworks" - alias = "os-extended-networks" - namespace = ("http://docs.openstack.org/compute/ext/extended_networks" - "/api/v2") - updated = "2014-05-09T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_quotas.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_quotas.py deleted file mode 100644 index 8161194992c..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_quotas.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2013 Rackspace Hosting -# 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. - -from nova.api.openstack import extensions - - -class Extended_quotas(extensions.ExtensionDescriptor): - """Adds ability for admins to delete quota - and optionally force the update Quota command. - """ - - name = "ExtendedQuotas" - alias = "os-extended-quotas" - namespace = ("http://docs.openstack.org/compute/ext/extended_quotas" - "/api/v1.1") - updated = "2013-06-09T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_rescue_with_image.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_rescue_with_image.py deleted file mode 100644 index bd73197e146..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_rescue_with_image.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2014 OpenStack Foundation -# -# 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 nova.api.openstack import extensions as exts - - -class Extended_rescue_with_image(exts.ExtensionDescriptor): - """Allow the user to specify the image to use for rescue.""" - - name = "ExtendedRescueWithImage" - alias = "os-extended-rescue-with-image" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_rescue_with_image/api/v2") - updated = "2014-01-04T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_server_attributes.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_server_attributes.py deleted file mode 100644 index 1ea5671f07f..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_server_attributes.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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. - -"""The Extended Server Attributes API extension.""" - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - -authorize = extensions.soft_extension_authorizer('compute', - 'extended_server_attributes') - - -class ExtendedServerAttributesController(wsgi.Controller): - def _extend_server(self, context, server, instance): - key = "%s:hypervisor_hostname" % Extended_server_attributes.alias - server[key] = instance.node - - for attr in ['host', 'name']: - if attr == 'name': - key = "%s:instance_%s" % (Extended_server_attributes.alias, - attr) - else: - key = "%s:%s" % (Extended_server_attributes.alias, attr) - server[key] = instance[attr] - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show' method. - self._extend_server(context, server, db_instance) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if authorize(context): - servers = list(resp_obj.obj['servers']) - for server in servers: - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'detail' method. - self._extend_server(context, server, db_instance) - - -class Extended_server_attributes(extensions.ExtensionDescriptor): - """Extended Server Attributes support.""" - - name = "ExtendedServerAttributes" - alias = "OS-EXT-SRV-ATTR" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_status/api/v1.1") - updated = "2011-11-03T00:00:00Z" - - def get_controller_extensions(self): - controller = ExtendedServerAttributesController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_services.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_services.py deleted file mode 100644 index 1c752b5ae58..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_services.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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 nova.api.openstack import extensions - - -class Extended_services(extensions.ExtensionDescriptor): - """Extended services support.""" - - name = "ExtendedServices" - alias = "os-extended-services" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_services/api/v2") - updated = "2013-05-17T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_services_delete.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_services_delete.py deleted file mode 100644 index 78d996c9044..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_services_delete.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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 nova.api.openstack import extensions - - -class Extended_services_delete(extensions.ExtensionDescriptor): - """Extended services deletion support.""" - - name = "ExtendedServicesDelete" - alias = "os-extended-services-delete" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_services_delete/api/v2") - updated = "2013-12-10T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_status.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_status.py deleted file mode 100644 index 89073153ca6..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_status.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# 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. - -"""The Extended Status Admin API extension.""" - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - -authorize = extensions.soft_extension_authorizer('compute', 'extended_status') - - -class ExtendedStatusController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ExtendedStatusController, self).__init__(*args, **kwargs) - - def _extend_server(self, server, instance): - for state in ['task_state', 'vm_state', 'power_state']: - key = "%s:%s" % (Extended_status.alias, state) - server[key] = instance[state] - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show' method. - self._extend_server(server, db_instance) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if authorize(context): - servers = list(resp_obj.obj['servers']) - for server in servers: - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'detail' method. - self._extend_server(server, db_instance) - - -class Extended_status(extensions.ExtensionDescriptor): - """Extended Status support.""" - - name = "ExtendedStatus" - alias = "OS-EXT-STS" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_status/api/v1.1") - updated = "2011-11-03T00:00:00Z" - - def get_controller_extensions(self): - controller = ExtendedStatusController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_virtual_interfaces_net.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_virtual_interfaces_net.py deleted file mode 100644 index 864f0252b41..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_virtual_interfaces_net.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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 nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import network - -authorize = extensions.soft_extension_authorizer('compute', 'extended_vif_net') - - -class ExtendedServerVIFNetController(wsgi.Controller): - def __init__(self): - super(ExtendedServerVIFNetController, self).__init__() - self.network_api = network.API() - - @wsgi.extends - def index(self, req, resp_obj, server_id): - key = "%s:net_id" % Extended_virtual_interfaces_net.alias - context = req.environ['nova.context'] - if authorize(context): - for vif in resp_obj.obj['virtual_interfaces']: - vif1 = self.network_api.get_vif_by_mac_address(context, - vif['mac_address']) - vif[key] = vif1.net_uuid - - -class Extended_virtual_interfaces_net(extensions.ExtensionDescriptor): - """Adds network id parameter to the virtual interface list.""" - - name = "ExtendedVIFNet" - alias = "OS-EXT-VIF-NET" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended-virtual-interfaces-net/api/v1.1") - updated = "2013-03-07T00:00:00Z" - - def get_controller_extensions(self): - controller = ExtendedServerVIFNetController() - extension = extensions.ControllerExtension(self, - 'os-virtual-interfaces', - controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_volumes.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_volumes.py deleted file mode 100644 index 90cd90fcc21..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/extended_volumes.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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. - -"""The Extended Volumes API extension.""" - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import objects - -authorize = extensions.soft_extension_authorizer('compute', 'extended_volumes') - - -class ExtendedVolumesController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ExtendedVolumesController, self).__init__(*args, **kwargs) - - def _extend_server(self, context, server, bdms): - volume_ids = [bdm.volume_id for bdm in bdms if bdm.volume_id] - key = "%s:volumes_attached" % Extended_volumes.alias - server[key] = [{'id': volume_id} for volume_id in volume_ids] - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid( - context, [server['id']]) - instance_bdms = self._get_instance_bdms(bdms, server) - self._extend_server(context, server, instance_bdms) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if authorize(context): - servers = list(resp_obj.obj['servers']) - instance_uuids = [server['id'] for server in servers] - bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid( - context, instance_uuids) - for server in servers: - instance_bdms = self._get_instance_bdms(bdms, server) - self._extend_server(context, server, instance_bdms) - - def _get_instance_bdms(self, bdms, server): - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in the 'detail' or 'show' method. - # If that instance has since been deleted, it won't be in the - # 'bdms' dictionary though, so use 'get' to avoid KeyErrors. - return bdms.get(server['id'], []) - - -class Extended_volumes(extensions.ExtensionDescriptor): - """Extended Volumes support.""" - - name = "ExtendedVolumes" - alias = "os-extended-volumes" - namespace = ("http://docs.openstack.org/compute/ext/" - "extended_volumes/api/v1.1") - updated = "2013-06-07T00:00:00Z" - - def get_controller_extensions(self): - controller = ExtendedVolumesController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/fixed_ips.py b/nova/api/openstack/compute/legacy_v2/contrib/fixed_ips.py deleted file mode 100644 index 558bd66e42b..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/fixed_ips.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2012 IBM Corp. -# -# 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 webob -import webob.exc - -from nova.api.openstack import extensions -from nova import exception -from nova.i18n import _ -from nova import objects - -authorize = extensions.extension_authorizer('compute', 'fixed_ips') - - -class FixedIPController(object): - def show(self, req, id): - """Return data about the given fixed IP.""" - context = req.environ['nova.context'] - authorize(context) - - attrs = ['network', 'instance'] - try: - fixed_ip = objects.FixedIP.get_by_address(context, id, - expected_attrs=attrs) - except exception.FixedIpNotFoundForAddress as ex: - raise webob.exc.HTTPNotFound(explanation=ex.format_message()) - except exception.FixedIpInvalid as ex: - raise webob.exc.HTTPBadRequest(explanation=ex.format_message()) - - fixed_ip_info = {"fixed_ip": {}} - if fixed_ip is None: - msg = _("Fixed IP %s has been deleted") % id - raise webob.exc.HTTPNotFound(explanation=msg) - - fixed_ip_info['fixed_ip']['cidr'] = str(fixed_ip.network.cidr) - fixed_ip_info['fixed_ip']['address'] = str(fixed_ip.address) - - if fixed_ip.instance: - fixed_ip_info['fixed_ip']['hostname'] = fixed_ip.instance.hostname - fixed_ip_info['fixed_ip']['host'] = fixed_ip.instance.host - else: - fixed_ip_info['fixed_ip']['hostname'] = None - fixed_ip_info['fixed_ip']['host'] = None - - return fixed_ip_info - - def action(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - - if 'reserve' in body: - return self._set_reserved(context, id, True) - elif 'unreserve' in body: - return self._set_reserved(context, id, False) - else: - raise webob.exc.HTTPBadRequest( - explanation="No valid action specified") - - def _set_reserved(self, context, address, reserved): - try: - fixed_ip = objects.FixedIP.get_by_address(context, address) - fixed_ip.reserved = reserved - fixed_ip.save() - except exception.FixedIpNotFoundForAddress: - msg = _("Fixed IP %s not found") % address - raise webob.exc.HTTPNotFound(explanation=msg) - except exception.FixedIpInvalid as ex: - raise webob.exc.HTTPBadRequest(explanation=ex.format_message()) - - return webob.Response(status_int=202) - - -class Fixed_ips(extensions.ExtensionDescriptor): - """Fixed IPs support.""" - - name = "FixedIPs" - alias = "os-fixed-ips" - namespace = "http://docs.openstack.org/compute/ext/fixed_ips/api/v2" - updated = "2012-10-18T19:25:27Z" - - def get_resources(self): - member_actions = {'action': 'POST'} - resources = [] - resource = extensions.ResourceExtension('os-fixed-ips', - FixedIPController(), - member_actions=member_actions) - resources.append(resource) - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavor_access.py b/nova/api/openstack/compute/legacy_v2/contrib/flavor_access.py deleted file mode 100644 index a2ff3c2d83b..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/flavor_access.py +++ /dev/null @@ -1,177 +0,0 @@ -# Copyright (c) 2011 OpenStack Foundation -# 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. - -"""The flavor access extension.""" - -import webob - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import objects - - -soft_authorize = extensions.soft_extension_authorizer('compute', - 'flavor_access') -authorize = extensions.extension_authorizer('compute', 'flavor_access') - - -def _marshall_flavor_access(flavor): - rval = [] - for project_id in flavor.projects: - rval.append({'flavor_id': flavor.flavorid, - 'tenant_id': project_id}) - - return {'flavor_access': rval} - - -class FlavorAccessController(object): - """The flavor access API controller for the OpenStack API.""" - - def __init__(self): - super(FlavorAccessController, self).__init__() - - def index(self, req, flavor_id): - context = req.environ['nova.context'] - authorize(context) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - - flavor = common.get_flavor(context, flavor_id) - - # public flavor to all projects - if flavor.is_public: - explanation = _("Access list not available for public flavors.") - raise webob.exc.HTTPNotFound(explanation=explanation) - - # private flavor to listed projects only - return _marshall_flavor_access(flavor) - - -class FlavorActionController(wsgi.Controller): - """The flavor access API controller for the OpenStack API.""" - - def _check_body(self, body): - if body is None or body == "": - raise webob.exc.HTTPBadRequest(explanation=_("No request body")) - - def _extend_flavor(self, flavor_rval, flavor_ref): - key = "%s:is_public" % (Flavor_access.alias) - flavor_rval[key] = flavor_ref['is_public'] - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if soft_authorize(context): - db_flavor = req.get_db_flavor(id) - - self._extend_flavor(resp_obj.obj['flavor'], db_flavor) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if soft_authorize(context): - flavors = list(resp_obj.obj['flavors']) - for flavor_rval in flavors: - db_flavor = req.get_db_flavor(flavor_rval['id']) - self._extend_flavor(flavor_rval, db_flavor) - - @wsgi.extends(action='create') - def create(self, req, body, resp_obj): - context = req.environ['nova.context'] - if soft_authorize(context): - db_flavor = req.get_db_flavor(resp_obj.obj['flavor']['id']) - - self._extend_flavor(resp_obj.obj['flavor'], db_flavor) - - @wsgi.action("addTenantAccess") - def _addTenantAccess(self, req, id, body): - context = req.environ['nova.context'] - authorize(context, action="addTenantAccess") - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - self._check_body(body) - - vals = body['addTenantAccess'] - tenant = vals.get('tenant') - if not tenant: - msg = _("Missing tenant parameter") - raise webob.exc.HTTPBadRequest(explanation=msg) - - flavor = objects.Flavor(context=context, flavorid=id) - try: - flavor.add_access(tenant) - except exception.FlavorAccessExists as err: - raise webob.exc.HTTPConflict(explanation=err.format_message()) - except exception.FlavorNotFound as err: - raise webob.exc.HTTPNotFound(explanation=err.format_message()) - - return _marshall_flavor_access(flavor) - - @wsgi.action("removeTenantAccess") - def _removeTenantAccess(self, req, id, body): - context = req.environ['nova.context'] - authorize(context, action="removeTenantAccess") - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - self._check_body(body) - - vals = body['removeTenantAccess'] - tenant = vals.get('tenant') - if not tenant: - msg = _("Missing tenant parameter") - raise webob.exc.HTTPBadRequest(explanation=msg) - - flavor = objects.Flavor(context=context, flavorid=id) - try: - flavor.remove_access(tenant) - except (exception.FlavorNotFound, - exception.FlavorAccessNotFound) as err: - raise webob.exc.HTTPNotFound(explanation=err.format_message()) - - return _marshall_flavor_access(flavor) - - -class Flavor_access(extensions.ExtensionDescriptor): - """Flavor access support.""" - - name = "FlavorAccess" - alias = "os-flavor-access" - namespace = ("http://docs.openstack.org/compute/ext/" - "flavor_access/api/v2") - updated = "2012-08-01T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension( - 'os-flavor-access', - controller=FlavorAccessController(), - parent=dict(member_name='flavor', collection_name='flavors')) - resources.append(res) - - return resources - - def get_controller_extensions(self): - extension = extensions.ControllerExtension( - self, 'flavors', FlavorActionController()) - - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavor_disabled.py b/nova/api/openstack/compute/legacy_v2/contrib/flavor_disabled.py deleted file mode 100644 index fa8233799c1..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/flavor_disabled.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright 2012 Nebula, Inc. -# -# 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. - -"""The Flavor Disabled API extension.""" - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - - -authorize = extensions.soft_extension_authorizer('compute', 'flavor_disabled') - - -class FlavorDisabledController(wsgi.Controller): - def _extend_flavors(self, req, flavors): - for flavor in flavors: - db_flavor = req.get_db_flavor(flavor['id']) - key = "%s:disabled" % Flavor_disabled.alias - flavor[key] = db_flavor['disabled'] - - def _show(self, req, resp_obj): - if not authorize(req.environ['nova.context']): - return - if 'flavor' in resp_obj.obj: - self._extend_flavors(req, [resp_obj.obj['flavor']]) - - @wsgi.extends - def show(self, req, resp_obj, id): - return self._show(req, resp_obj) - - @wsgi.extends(action='create') - def create(self, req, resp_obj, body): - return self._show(req, resp_obj) - - @wsgi.extends - def detail(self, req, resp_obj): - if not authorize(req.environ['nova.context']): - return - self._extend_flavors(req, list(resp_obj.obj['flavors'])) - - -class Flavor_disabled(extensions.ExtensionDescriptor): - """Support to show the disabled status of a flavor.""" - - name = "FlavorDisabled" - alias = "OS-FLV-DISABLED" - namespace = ("http://docs.openstack.org/compute/ext/" - "flavor_disabled/api/v1.1") - updated = "2012-08-29T00:00:00Z" - - def get_controller_extensions(self): - controller = FlavorDisabledController() - extension = extensions.ControllerExtension(self, 'flavors', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavor_rxtx.py b/nova/api/openstack/compute/legacy_v2/contrib/flavor_rxtx.py deleted file mode 100644 index 7044564f96e..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/flavor_rxtx.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright 2012 Nebula, Inc. -# -# 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. - -"""The Flavor Rxtx API extension.""" - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - - -authorize = extensions.soft_extension_authorizer('compute', 'flavor_rxtx') - - -class FlavorRxtxController(wsgi.Controller): - def _extend_flavors(self, req, flavors): - for flavor in flavors: - db_flavor = req.get_db_flavor(flavor['id']) - key = 'rxtx_factor' - flavor[key] = db_flavor['rxtx_factor'] or "" - - def _show(self, req, resp_obj): - if not authorize(req.environ['nova.context']): - return - if 'flavor' in resp_obj.obj: - self._extend_flavors(req, [resp_obj.obj['flavor']]) - - @wsgi.extends - def show(self, req, resp_obj, id): - return self._show(req, resp_obj) - - @wsgi.extends(action='create') - def create(self, req, resp_obj, body): - return self._show(req, resp_obj) - - @wsgi.extends - def detail(self, req, resp_obj): - if not authorize(req.environ['nova.context']): - return - self._extend_flavors(req, list(resp_obj.obj['flavors'])) - - -class Flavor_rxtx(extensions.ExtensionDescriptor): - """Support to show the rxtx status of a flavor.""" - - name = "FlavorRxtx" - alias = "os-flavor-rxtx" - namespace = ("http://docs.openstack.org/compute/ext/" - "flavor_rxtx/api/v1.1") - updated = "2012-08-29T00:00:00Z" - - def get_controller_extensions(self): - controller = FlavorRxtxController() - extension = extensions.ControllerExtension(self, 'flavors', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavor_swap.py b/nova/api/openstack/compute/legacy_v2/contrib/flavor_swap.py deleted file mode 100644 index c42538daf7e..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/flavor_swap.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright 2012 Nebula, Inc. -# -# 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. - -"""The Flavor Swap API extension.""" - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - - -authorize = extensions.soft_extension_authorizer('compute', 'flavor_swap') - - -class FlavorSwapController(wsgi.Controller): - def _extend_flavors(self, req, flavors): - for flavor in flavors: - db_flavor = req.get_db_flavor(flavor['id']) - key = 'swap' - flavor[key] = db_flavor['swap'] or "" - - def _show(self, req, resp_obj): - if not authorize(req.environ['nova.context']): - return - if 'flavor' in resp_obj.obj: - self._extend_flavors(req, [resp_obj.obj['flavor']]) - - @wsgi.extends - def show(self, req, resp_obj, id): - return self._show(req, resp_obj) - - @wsgi.extends(action='create') - def create(self, req, resp_obj, body): - return self._show(req, resp_obj) - - @wsgi.extends - def detail(self, req, resp_obj): - if not authorize(req.environ['nova.context']): - return - self._extend_flavors(req, list(resp_obj.obj['flavors'])) - - -class Flavor_swap(extensions.ExtensionDescriptor): - """Support to show the swap status of a flavor.""" - - name = "FlavorSwap" - alias = "os-flavor-swap" - namespace = ("http://docs.openstack.org/compute/ext/" - "flavor_swap/api/v1.1") - updated = "2012-08-29T00:00:00Z" - - def get_controller_extensions(self): - controller = FlavorSwapController() - extension = extensions.ControllerExtension(self, 'flavors', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavorextradata.py b/nova/api/openstack/compute/legacy_v2/contrib/flavorextradata.py deleted file mode 100644 index d8965287932..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/flavorextradata.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# Copyright 2011 Canonical Ltd. -# 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. - -"""The Flavor extra data extension - -OpenStack API version 1.1 lists "name", "ram", "disk", "vcpus" as flavor -attributes. This extension adds to that list: - -- OS-FLV-EXT-DATA:ephemeral -""" - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - - -authorize = extensions.soft_extension_authorizer('compute', 'flavorextradata') - - -class FlavorextradataController(wsgi.Controller): - def _extend_flavors(self, req, flavors): - for flavor in flavors: - db_flavor = req.get_db_flavor(flavor['id']) - key = "%s:ephemeral" % Flavorextradata.alias - flavor[key] = db_flavor['ephemeral_gb'] - - def _show(self, req, resp_obj): - if not authorize(req.environ['nova.context']): - return - if 'flavor' in resp_obj.obj: - self._extend_flavors(req, [resp_obj.obj['flavor']]) - - @wsgi.extends - def show(self, req, resp_obj, id): - return self._show(req, resp_obj) - - @wsgi.extends(action='create') - def create(self, req, resp_obj, body): - return self._show(req, resp_obj) - - @wsgi.extends - def detail(self, req, resp_obj): - if not authorize(req.environ['nova.context']): - return - self._extend_flavors(req, list(resp_obj.obj['flavors'])) - - -class Flavorextradata(extensions.ExtensionDescriptor): - """Provide additional data for flavors.""" - - name = "FlavorExtraData" - alias = "OS-FLV-EXT-DATA" - namespace = ("http://docs.openstack.org/compute/ext/" - "flavor_extra_data/api/v1.1") - updated = "2011-09-14T00:00:00Z" - - def get_controller_extensions(self): - controller = FlavorextradataController() - extension = extensions.ControllerExtension(self, 'flavors', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavorextraspecs.py b/nova/api/openstack/compute/legacy_v2/contrib/flavorextraspecs.py deleted file mode 100644 index e3809545966..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/flavorextraspecs.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright 2011 University of Southern California -# 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. - -"""The instance type extra specs extension.""" - -import six -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.compute import flavors -from nova import exception -from nova.i18n import _ -from nova import utils - -authorize = extensions.extension_authorizer('compute', 'flavorextraspecs') - - -class FlavorExtraSpecsController(object): - """The flavor extra specs API controller for the OpenStack API.""" - - def _get_extra_specs(self, context, flavor_id): - flavor = common.get_flavor(context, flavor_id) - return dict(extra_specs=flavor.extra_specs) - - def _check_body(self, body): - if body is None or body == "": - expl = _('No Request Body') - raise exc.HTTPBadRequest(explanation=expl) - - def _check_extra_specs(self, specs): - if type(specs) is not dict: - msg = _('Bad extra_specs provided') - raise exc.HTTPBadRequest(explanation=msg) - - try: - flavors.validate_extra_spec_keys(specs.keys()) - except TypeError: - msg = _("Fail to validate provided extra specs keys. " - "Expected string") - raise exc.HTTPBadRequest(explanation=msg) - except exception.InvalidInput as error: - raise exc.HTTPBadRequest(explanation=error.format_message()) - - for key, value in six.iteritems(specs): - try: - utils.check_string_length(key, 'extra_specs key', - min_length=1, max_length=255) - - # NOTE(dims): The following check was added for backwards - # compatibility. - if (isinstance(value, float) or - type(value) in six.integer_types): - value = six.text_type(value) - utils.check_string_length(value, 'extra_specs value', - max_length=255) - except exception.InvalidInput as error: - raise exc.HTTPBadRequest(explanation=error.format_message()) - - def index(self, req, flavor_id): - """Returns the list of extra specs for a given flavor.""" - context = req.environ['nova.context'] - authorize(context, action='index') - return self._get_extra_specs(context, flavor_id) - - def create(self, req, flavor_id, body): - context = req.environ['nova.context'] - authorize(context, action='create') - self._check_body(body) - specs = body.get('extra_specs') - self._check_extra_specs(specs) - flavor = common.get_flavor(context, flavor_id) - - try: - flavor.extra_specs = dict(flavor.extra_specs, **specs) - flavor.save() - except exception.FlavorExtraSpecUpdateCreateFailed as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.FlavorNotFound as error: - raise exc.HTTPNotFound(explanation=error.format_message()) - return body - - def update(self, req, flavor_id, id, body): - context = req.environ['nova.context'] - authorize(context, action='update') - self._check_extra_specs(body) - if id not in body: - expl = _('Request body and URI mismatch') - raise exc.HTTPBadRequest(explanation=expl) - if len(body) > 1: - expl = _('Request body contains too many items') - raise exc.HTTPBadRequest(explanation=expl) - flavor = common.get_flavor(context, flavor_id) - try: - flavor.extra_specs = dict(flavor.extra_specs, **body) - flavor.save() - except exception.FlavorExtraSpecUpdateCreateFailed as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.FlavorNotFound as error: - raise exc.HTTPNotFound(explanation=error.format_message()) - return body - - def show(self, req, flavor_id, id): - """Return a single extra spec item.""" - context = req.environ['nova.context'] - authorize(context, action='show') - flavor = common.get_flavor(context, flavor_id) - - try: - return {id: flavor.extra_specs[id]} - except KeyError: - msg = _("Flavor %(flavor_id)s has no extra specs with " - "key %(key)s.") % dict(flavor_id=flavor_id, - key=id) - raise exc.HTTPNotFound(explanation=msg) - - def delete(self, req, flavor_id, id): - """Deletes an existing extra spec.""" - context = req.environ['nova.context'] - authorize(context, action='delete') - flavor = common.get_flavor(context, flavor_id) - - try: - del flavor.extra_specs[id] - flavor.save() - except (exception.FlavorNotFound, - exception.FlavorExtraSpecsNotFound) as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except KeyError: - msg = _("Flavor %(flavor_id)s has no extra specs with " - "key %(key)s.") % dict(flavor_id=flavor_id, - key=id) - raise exc.HTTPNotFound(explanation=msg) - - -class Flavorextraspecs(extensions.ExtensionDescriptor): - """Instance type (flavor) extra specs.""" - - name = "FlavorExtraSpecs" - alias = "os-flavor-extra-specs" - namespace = ("http://docs.openstack.org/compute/ext/" - "flavor_extra_specs/api/v1.1") - updated = "2011-06-23T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension( - 'os-extra_specs', - FlavorExtraSpecsController(), - parent=dict(member_name='flavor', collection_name='flavors')) - - resources.append(res) - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavormanage.py b/nova/api/openstack/compute/legacy_v2/contrib/flavormanage.py deleted file mode 100644 index 4c3ee0be90c..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/flavormanage.py +++ /dev/null @@ -1,112 +0,0 @@ -# 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 webob - -from nova.api.openstack.compute.views import flavors as flavors_view -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova.compute import flavors -from nova import exception -from nova.i18n import _ - - -authorize = extensions.extension_authorizer('compute', 'flavormanage') - - -class FlavorManageController(wsgi.Controller): - """The Flavor Lifecycle API controller for the OpenStack API.""" - _view_builder_class = flavors_view.ViewBuilder - - def __init__(self): - super(FlavorManageController, self).__init__() - - @wsgi.action("delete") - def _delete(self, req, id): - context = req.environ['nova.context'] - authorize(context) - try: - flavor = flavors.get_flavor_by_flavor_id( - id, ctxt=context, read_deleted="no") - except exception.FlavorNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - - flavors.destroy(flavor['name']) - - return webob.Response(status_int=202) - - @wsgi.action("create") - def _create(self, req, body): - context = req.environ['nova.context'] - authorize(context) - if not self.is_valid_body(body, 'flavor'): - msg = _("Invalid request body") - raise webob.exc.HTTPBadRequest(explanation=msg) - vals = body['flavor'] - name = vals.get('name') - if name is None: - msg = _("A valid name parameter is required") - raise webob.exc.HTTPBadRequest(explanation=msg) - - flavorid = vals.get('id') - memory = vals.get('ram') - if memory is None: - msg = _("A valid ram parameter is required") - raise webob.exc.HTTPBadRequest(explanation=msg) - - vcpus = vals.get('vcpus') - if vcpus is None: - msg = _("A valid vcpus parameter is required") - raise webob.exc.HTTPBadRequest(explanation=msg) - - root_gb = vals.get('disk') - if root_gb is None: - msg = _("A valid disk parameter is required") - raise webob.exc.HTTPBadRequest(explanation=msg) - - ephemeral_gb = vals.get('OS-FLV-EXT-DATA:ephemeral', 0) - swap = vals.get('swap', 0) - rxtx_factor = vals.get('rxtx_factor', 1.0) - is_public = vals.get('os-flavor-access:is_public', True) - - try: - flavor = flavors.create(name, memory, vcpus, root_gb, - ephemeral_gb=ephemeral_gb, - flavorid=flavorid, swap=swap, - rxtx_factor=rxtx_factor, - is_public=is_public) - req.cache_db_flavor(flavor) - except (exception.FlavorExists, - exception.FlavorIdExists) as err: - raise webob.exc.HTTPConflict(explanation=err.format_message()) - except exception.InvalidInput as exc: - raise webob.exc.HTTPBadRequest(explanation=exc.format_message()) - except exception.FlavorCreateFailed as exc: - raise webob.exc.HTTPInternalServerError(explanation= - exc.format_message()) - - return self._view_builder.show(req, flavor) - - -class Flavormanage(extensions.ExtensionDescriptor): - """Flavor create/delete API support.""" - - name = "FlavorManage" - alias = "os-flavor-manage" - namespace = ("http://docs.openstack.org/compute/ext/" - "flavor_manage/api/v1.1") - updated = "2012-01-19T00:00:00Z" - - def get_controller_extensions(self): - controller = FlavorManageController() - extension = extensions.ControllerExtension(self, 'flavors', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_dns.py b/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_dns.py deleted file mode 100644 index e46254e1f9e..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_dns.py +++ /dev/null @@ -1,280 +0,0 @@ -# Copyright 2011 Andrew Bogott for the Wikimedia Foundation -# -# 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 urllib - -from oslo_utils import netutils -import webob - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import network - - -authorize = extensions.extension_authorizer('compute', 'floating_ip_dns') - - -def _translate_dns_entry_view(dns_entry): - result = {} - result['ip'] = dns_entry.get('ip') - result['id'] = dns_entry.get('id') - result['type'] = dns_entry.get('type') - result['domain'] = dns_entry.get('domain') - result['name'] = dns_entry.get('name') - return {'dns_entry': result} - - -def _translate_dns_entries_view(dns_entries): - return {'dns_entries': [_translate_dns_entry_view(entry)['dns_entry'] - for entry in dns_entries]} - - -def _translate_domain_entry_view(domain_entry): - result = {} - result['domain'] = domain_entry.get('domain') - result['scope'] = domain_entry.get('scope') - result['project'] = domain_entry.get('project') - result['availability_zone'] = domain_entry.get('availability_zone') - return {'domain_entry': result} - - -def _translate_domain_entries_view(domain_entries): - return {'domain_entries': - [_translate_domain_entry_view(entry)['domain_entry'] - for entry in domain_entries]} - - -def _unquote_domain(domain): - """Unquoting function for receiving a domain name in a URL. - - Domain names tend to have .'s in them. Urllib doesn't quote dots, - but Routes tends to choke on them, so we need an extra level of - by-hand quoting here. - """ - return urllib.unquote(domain).replace('%2E', '.') - - -def _create_dns_entry(ip, name, domain): - return {'ip': ip, 'name': name, 'domain': domain} - - -def _create_domain_entry(domain, scope=None, project=None, av_zone=None): - return {'domain': domain, 'scope': scope, 'project': project, - 'availability_zone': av_zone} - - -class FloatingIPDNSDomainController(object): - """DNS domain controller for OpenStack API.""" - - def __init__(self): - self.network_api = network.API() - super(FloatingIPDNSDomainController, self).__init__() - - def index(self, req): - """Return a list of available DNS domains.""" - context = req.environ['nova.context'] - authorize(context) - try: - domains = self.network_api.get_dns_domains(context) - except NotImplementedError: - msg = _("Unable to get dns domain") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - domainlist = [_create_domain_entry(domain['domain'], - domain.get('scope'), - domain.get('project'), - domain.get('availability_zone')) - for domain in domains] - - return _translate_domain_entries_view(domainlist) - - def update(self, req, id, body): - """Add or modify domain entry.""" - context = req.environ['nova.context'] - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. - nova_context.require_admin_context(context) - fqdomain = _unquote_domain(id) - try: - entry = body['domain_entry'] - scope = entry['scope'] - except (TypeError, KeyError): - raise webob.exc.HTTPUnprocessableEntity() - project = entry.get('project', None) - av_zone = entry.get('availability_zone', None) - if (scope not in ('private', 'public') or - project and av_zone or - scope == 'private' and project or - scope == 'public' and av_zone): - raise webob.exc.HTTPUnprocessableEntity() - if scope == 'private': - create_dns_domain = self.network_api.create_private_dns_domain - area_name, area = 'availability_zone', av_zone - else: - create_dns_domain = self.network_api.create_public_dns_domain - area_name, area = 'project', project - try: - create_dns_domain(context, fqdomain, area) - except NotImplementedError: - msg = _("Unable to create dns domain") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - return _translate_domain_entry_view({'domain': fqdomain, - 'scope': scope, - area_name: area}) - - def delete(self, req, id): - """Delete the domain identified by id.""" - context = req.environ['nova.context'] - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. - nova_context.require_admin_context(context) - domain = _unquote_domain(id) - - # Delete the whole domain - try: - self.network_api.delete_dns_domain(context, domain) - except exception.NotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except NotImplementedError: - msg = _("Unable to delete dns domain") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - return webob.Response(status_int=202) - - -class FloatingIPDNSEntryController(object): - """DNS Entry controller for OpenStack API.""" - - def __init__(self): - self.network_api = network.API() - super(FloatingIPDNSEntryController, self).__init__() - - def show(self, req, domain_id, id): - """Return the DNS entry that corresponds to domain_id and id.""" - context = req.environ['nova.context'] - authorize(context) - domain = _unquote_domain(domain_id) - - floating_ip = None - # Check whether id is a valid ipv4/ipv6 address. - if netutils.is_valid_ip(id): - floating_ip = id - - try: - if floating_ip: - entries = self.network_api.get_dns_entries_by_address( - context, floating_ip, domain) - else: - entries = self.network_api.get_dns_entries_by_name( - context, id, domain) - except NotImplementedError: - msg = _("Unable to get dns entry") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - if not entries: - explanation = _("DNS entries not found.") - raise webob.exc.HTTPNotFound(explanation=explanation) - - if floating_ip: - entrylist = [_create_dns_entry(floating_ip, entry, domain) - for entry in entries] - dns_entries = _translate_dns_entries_view(entrylist) - return wsgi.ResponseObject(dns_entries) - - entry = _create_dns_entry(entries[0], id, domain) - return _translate_dns_entry_view(entry) - - def update(self, req, domain_id, id, body): - """Add or modify dns entry.""" - context = req.environ['nova.context'] - authorize(context) - domain = _unquote_domain(domain_id) - name = id - try: - entry = body['dns_entry'] - address = entry['ip'] - dns_type = entry['dns_type'] - except (TypeError, KeyError): - raise webob.exc.HTTPUnprocessableEntity() - - try: - entries = self.network_api.get_dns_entries_by_name( - context, name, domain) - if not entries: - # create! - self.network_api.add_dns_entry(context, address, name, - dns_type, domain) - else: - # modify! - self.network_api.modify_dns_entry(context, name, address, - domain) - except NotImplementedError: - msg = _("Unable to create dns entry") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - return _translate_dns_entry_view({'ip': address, - 'name': name, - 'type': dns_type, - 'domain': domain}) - - def delete(self, req, domain_id, id): - """Delete the entry identified by req and id.""" - context = req.environ['nova.context'] - authorize(context) - domain = _unquote_domain(domain_id) - name = id - - try: - self.network_api.delete_dns_entry(context, name, domain) - except exception.NotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except NotImplementedError: - msg = _("Unable to delete dns entry") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - return webob.Response(status_int=202) - - -class Floating_ip_dns(extensions.ExtensionDescriptor): - """Floating IP DNS support.""" - - name = "FloatingIpDns" - alias = "os-floating-ip-dns" - namespace = "http://docs.openstack.org/ext/floating_ip_dns/api/v1.1" - updated = "2011-12-23T00:00:00Z" - - def __init__(self, ext_mgr): - self.network_api = network.API() - super(Floating_ip_dns, self).__init__(ext_mgr) - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-floating-ip-dns', - FloatingIPDNSDomainController()) - resources.append(res) - - res = extensions.ResourceExtension('entries', - FloatingIPDNSEntryController(), - parent={'member_name': 'domain', - 'collection_name': 'os-floating-ip-dns'}) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_pools.py b/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_pools.py deleted file mode 100644 index 145c51e9b1c..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_pools.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. -# -# 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 nova.api.openstack import extensions -from nova import network - - -authorize = extensions.extension_authorizer('compute', 'floating_ip_pools') - - -def _translate_floating_ip_view(pool_name): - return { - 'name': pool_name, - } - - -def _translate_floating_ip_pools_view(pools): - return { - 'floating_ip_pools': [_translate_floating_ip_view(pool_name) - for pool_name in pools] - } - - -class FloatingIPPoolsController(object): - """The Floating IP Pool API controller for the OpenStack API.""" - - def __init__(self): - self.network_api = network.API() - super(FloatingIPPoolsController, self).__init__() - - def index(self, req): - """Return a list of pools.""" - context = req.environ['nova.context'] - authorize(context) - pools = self.network_api.get_floating_ip_pools(context) - return _translate_floating_ip_pools_view(pools) - - -class Floating_ip_pools(extensions.ExtensionDescriptor): - """Floating IPs support.""" - - name = "FloatingIpPools" - alias = "os-floating-ip-pools" - namespace = ("http://docs.openstack.org/compute/ext/" - "floating_ip_pools/api/v1.1") - updated = "2012-01-04T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-floating-ip-pools', - FloatingIPPoolsController(), - member_actions={}) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/floating_ips.py b/nova/api/openstack/compute/legacy_v2/contrib/floating_ips.py deleted file mode 100644 index d30bb238262..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/floating_ips.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. -# Copyright 2011 Grid Dynamics -# Copyright 2011 Eldar Nugaev, Kirill Shileev, Ilya Alekseyev -# -# 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 netaddr -from oslo_log import log as logging -from oslo_utils import uuidutils -import webob - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova.compute import utils as compute_utils -from nova import exception -from nova.i18n import _ -from nova.i18n import _LW -from nova import network - - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'floating_ips') - - -def _translate_floating_ip_view(floating_ip): - result = { - 'id': floating_ip['id'], - 'ip': floating_ip['address'], - 'pool': floating_ip['pool'], - } - try: - result['fixed_ip'] = floating_ip['fixed_ip']['address'] - except (TypeError, KeyError, AttributeError): - result['fixed_ip'] = None - try: - result['instance_id'] = floating_ip['fixed_ip']['instance_uuid'] - except (TypeError, KeyError, AttributeError): - result['instance_id'] = None - return {'floating_ip': result} - - -def _translate_floating_ips_view(floating_ips): - return {'floating_ips': [_translate_floating_ip_view(ip)['floating_ip'] - for ip in floating_ips]} - - -def get_instance_by_floating_ip_addr(self, context, address): - snagiibfa = self.network_api.get_instance_id_by_floating_address - instance_id = snagiibfa(context, address) - if instance_id: - return common.get_instance(self.compute_api, context, instance_id) - - -def disassociate_floating_ip(self, context, instance, address): - try: - self.network_api.disassociate_floating_ip(context, instance, address) - except exception.Forbidden: - raise webob.exc.HTTPForbidden() - except exception.CannotDisassociateAutoAssignedFloatingIP: - msg = _('Cannot disassociate auto assigned floating IP') - raise webob.exc.HTTPForbidden(explanation=msg) - - -class FloatingIPController(object): - """The Floating IPs API controller for the OpenStack API.""" - - def __init__(self): - self.compute_api = compute.API() - self.network_api = network.API() - super(FloatingIPController, self).__init__() - - def show(self, req, id): - """Return data about the given floating IP.""" - context = req.environ['nova.context'] - authorize(context) - - try: - floating_ip = self.network_api.get_floating_ip(context, id) - except (exception.NotFound, exception.InvalidID): - msg = _("Floating IP not found for ID %s") % id - raise webob.exc.HTTPNotFound(explanation=msg) - - return _translate_floating_ip_view(floating_ip) - - def index(self, req): - """Return a list of floating IPs allocated to a project.""" - context = req.environ['nova.context'] - authorize(context) - - floating_ips = self.network_api.get_floating_ips_by_project(context) - - return _translate_floating_ips_view(floating_ips) - - def create(self, req, body=None): - context = req.environ['nova.context'] - authorize(context) - - pool = None - if body and 'pool' in body: - pool = body['pool'] - try: - address = self.network_api.allocate_floating_ip(context, pool) - ip = self.network_api.get_floating_ip_by_address(context, address) - except exception.NoMoreFloatingIps: - if pool: - msg = _("No more floating IPs in pool %s.") % pool - else: - msg = _("No more floating IPs available.") - raise webob.exc.HTTPNotFound(explanation=msg) - except exception.FloatingIpLimitExceeded: - if pool: - msg = _("IP allocation over quota in pool %s.") % pool - else: - msg = _("IP allocation over quota.") - raise webob.exc.HTTPForbidden(explanation=msg) - except exception.FloatingIpPoolNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except exception.FloatingIpBadRequest as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - - return _translate_floating_ip_view(ip) - - def delete(self, req, id): - context = req.environ['nova.context'] - authorize(context) - - # get the floating ip object - try: - floating_ip = self.network_api.get_floating_ip(context, id) - except (exception.NotFound, exception.InvalidID): - msg = _("Floating IP not found for ID %s") % id - raise webob.exc.HTTPNotFound(explanation=msg) - address = floating_ip['address'] - - # get the associated instance object (if any) - instance = get_instance_by_floating_ip_addr(self, context, address) - try: - self.network_api.disassociate_and_release_floating_ip( - context, instance, floating_ip) - except exception.Forbidden: - raise webob.exc.HTTPForbidden() - except exception.CannotDisassociateAutoAssignedFloatingIP: - msg = _('Cannot disassociate auto assigned floating IP') - raise webob.exc.HTTPForbidden(explanation=msg) - return webob.Response(status_int=202) - - -class FloatingIPActionController(wsgi.Controller): - def __init__(self, ext_mgr=None, *args, **kwargs): - super(FloatingIPActionController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - self.network_api = network.API() - self.ext_mgr = ext_mgr - - @wsgi.action('addFloatingIp') - def _add_floating_ip(self, req, id, body): - """Associate floating_ip to an instance.""" - context = req.environ['nova.context'] - authorize(context) - - try: - address = body['addFloatingIp']['address'] - except TypeError: - msg = _("Missing parameter dict") - raise webob.exc.HTTPBadRequest(explanation=msg) - except KeyError: - msg = _("Address not specified") - raise webob.exc.HTTPBadRequest(explanation=msg) - - instance = common.get_instance(self.compute_api, context, id) - cached_nwinfo = compute_utils.get_nw_info_for_instance(instance) - if not cached_nwinfo: - LOG.warning( - _LW('Info cache is %r during associate'), instance.info_cache, - instance=instance) - msg = _('No nw_info cache associated with instance') - raise webob.exc.HTTPBadRequest(explanation=msg) - - fixed_ips = cached_nwinfo.fixed_ips() - if not fixed_ips: - msg = _('No fixed IPs associated to instance') - raise webob.exc.HTTPBadRequest(explanation=msg) - - fixed_address = None - if self.ext_mgr.is_loaded('os-extended-floating-ips'): - if 'fixed_address' in body['addFloatingIp']: - fixed_address = body['addFloatingIp']['fixed_address'] - for fixed in fixed_ips: - if fixed['address'] == fixed_address: - break - else: - msg = _('Specified fixed address not assigned to instance') - raise webob.exc.HTTPBadRequest(explanation=msg) - - if not fixed_address: - try: - fixed_address = next(ip['address'] for ip in fixed_ips - if netaddr.valid_ipv4(ip['address'])) - except StopIteration: - msg = _('Unable to associate floating IP %(address)s ' - 'to any fixed IPs for instance %(id)s. ' - 'Instance has no fixed IPv4 addresses to ' - 'associate.') % ( - {'address': address, 'id': id}) - raise webob.exc.HTTPBadRequest(explanation=msg) - if len(fixed_ips) > 1: - LOG.warning(_LW('multiple fixed_ips exist, using the first ' - 'IPv4 fixed_ip: %s'), fixed_address) - - try: - self.network_api.associate_floating_ip(context, instance, - floating_address=address, - fixed_address=fixed_address) - except exception.FloatingIpAssociated: - msg = _('floating IP is already associated') - raise webob.exc.HTTPBadRequest(explanation=msg) - except exception.NoFloatingIpInterface: - msg = _('l3driver call to add floating IP failed') - raise webob.exc.HTTPBadRequest(explanation=msg) - except exception.FloatingIpNotFoundForAddress: - msg = _('floating IP not found') - raise webob.exc.HTTPNotFound(explanation=msg) - except exception.Forbidden as e: - raise webob.exc.HTTPForbidden(explanation=e.format_message()) - except Exception as e: - msg = _('Unable to associate floating IP %(address)s to ' - 'fixed IP %(fixed_address)s for instance %(id)s. ' - 'Error: %(error)s') % ( - {'address': address, 'fixed_address': fixed_address, - 'id': id, 'error': e}) - LOG.exception(msg) - raise webob.exc.HTTPBadRequest(explanation=msg) - - return webob.Response(status_int=202) - - @wsgi.action('removeFloatingIp') - def _remove_floating_ip(self, req, id, body): - """Dissociate floating_ip from an instance.""" - context = req.environ['nova.context'] - authorize(context) - - try: - address = body['removeFloatingIp']['address'] - except TypeError: - msg = _("Missing parameter dict") - raise webob.exc.HTTPBadRequest(explanation=msg) - except KeyError: - msg = _("Address not specified") - raise webob.exc.HTTPBadRequest(explanation=msg) - - # get the floating ip object - try: - floating_ip = self.network_api.get_floating_ip_by_address(context, - address) - except exception.FloatingIpNotFoundForAddress: - msg = _("floating IP not found") - raise webob.exc.HTTPNotFound(explanation=msg) - - # get the associated instance object (if any) - instance = get_instance_by_floating_ip_addr(self, context, address) - - # disassociate if associated - if (instance and - floating_ip.get('fixed_ip_id') and - (uuidutils.is_uuid_like(id) and - [instance.uuid == id] or - [instance.id == id])[0]): - try: - disassociate_floating_ip(self, context, instance, address) - except exception.FloatingIpNotAssociated: - msg = _('Floating IP is not associated') - raise webob.exc.HTTPBadRequest(explanation=msg) - return webob.Response(status_int=202) - else: - msg = _("Floating IP %(address)s is not associated with instance " - "%(id)s.") % {'address': address, 'id': id} - raise webob.exc.HTTPConflict(explanation=msg) - - -class Floating_ips(extensions.ExtensionDescriptor): - """Floating IPs support.""" - - name = "FloatingIps" - alias = "os-floating-ips" - namespace = "http://docs.openstack.org/compute/ext/floating_ips/api/v1.1" - updated = "2011-06-16T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-floating-ips', - FloatingIPController(), - member_actions={}) - resources.append(res) - - return resources - - def get_controller_extensions(self): - controller = FloatingIPActionController(self.ext_mgr) - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/floating_ips_bulk.py b/nova/api/openstack/compute/legacy_v2/contrib/floating_ips_bulk.py deleted file mode 100644 index 7371ffc7401..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/floating_ips_bulk.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright 2012 IBM Corp. -# -# 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 netaddr -from oslo_config import cfg -import six -import webob.exc - -from nova.api.openstack import extensions -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import objects - -CONF = cfg.CONF -CONF.import_opt('default_floating_pool', 'nova.network.floating_ips') -CONF.import_opt('public_interface', 'nova.network.linux_net') - - -authorize = extensions.extension_authorizer('compute', 'floating_ips_bulk') - - -class FloatingIPBulkController(object): - - def index(self, req): - """Return a list of all floating IPs.""" - context = req.environ['nova.context'] - authorize(context) - - return self._get_floating_ip_info(context) - - def show(self, req, id): - """Return a list of all floating IPs for a given host.""" - context = req.environ['nova.context'] - authorize(context) - - return self._get_floating_ip_info(context, id) - - def _get_floating_ip_info(self, context, host=None): - floating_ip_info = {"floating_ip_info": []} - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. - nova_context.require_admin_context(context) - - if host is None: - try: - floating_ips = objects.FloatingIPList.get_all(context) - except exception.NoFloatingIpsDefined: - return floating_ip_info - else: - try: - floating_ips = objects.FloatingIPList.get_by_host(context, - host) - except exception.FloatingIpNotFoundForHost as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - - for floating_ip in floating_ips: - instance_uuid = None - fixed_ip = None - if floating_ip.fixed_ip: - instance_uuid = floating_ip.fixed_ip.instance_uuid - fixed_ip = str(floating_ip.fixed_ip.address) - - result = {'address': str(floating_ip.address), - 'pool': floating_ip.pool, - 'interface': floating_ip.interface, - 'project_id': floating_ip.project_id, - 'instance_uuid': instance_uuid, - 'fixed_ip': fixed_ip} - floating_ip_info['floating_ip_info'].append(result) - - return floating_ip_info - - def create(self, req, body): - """Bulk create floating IPs.""" - context = req.environ['nova.context'] - authorize(context) - - if 'floating_ips_bulk_create' not in body: - raise webob.exc.HTTPUnprocessableEntity() - params = body['floating_ips_bulk_create'] - - if 'ip_range' not in params: - raise webob.exc.HTTPUnprocessableEntity() - ip_range = params['ip_range'] - - pool = params.get('pool', CONF.default_floating_pool) - interface = params.get('interface', CONF.public_interface) - - try: - ips = [objects.FloatingIPList.make_ip_info(addr, pool, interface) - for addr in self._address_to_hosts(ip_range)] - except exception.InvalidInput as exc: - raise webob.exc.HTTPBadRequest(explanation=exc.format_message()) - - try: - objects.FloatingIPList.create(context, ips) - except exception.FloatingIpExists as exc: - raise webob.exc.HTTPConflict(explanation=exc.format_message()) - - return {"floating_ips_bulk_create": {"ip_range": ip_range, - "pool": pool, - "interface": interface}} - - def update(self, req, id, body): - """Bulk delete floating IPs.""" - context = req.environ['nova.context'] - authorize(context) - - if id != "delete": - msg = _("Unknown action") - raise webob.exc.HTTPNotFound(explanation=msg) - - try: - ip_range = body['ip_range'] - except (TypeError, KeyError): - raise webob.exc.HTTPUnprocessableEntity() - - try: - ips = (objects.FloatingIPList.make_ip_info(address, None, None) - for address in self._address_to_hosts(ip_range)) - except exception.InvalidInput as exc: - raise webob.exc.HTTPBadRequest(explanation=exc.format_message()) - objects.FloatingIPList.destroy(context, ips) - - return {"floating_ips_bulk_delete": ip_range} - - def _address_to_hosts(self, addresses): - """Iterate over hosts within an address range. - - If an explicit range specifier is missing, the parameter is - interpreted as a specific individual address. - """ - try: - return [netaddr.IPAddress(addresses)] - except ValueError: - net = netaddr.IPNetwork(addresses) - if net.size < 4: - reason = _("/%s should be specified as single address(es) " - "not in cidr format") % net.prefixlen - raise exception.InvalidInput(reason=reason) - else: - return net.iter_hosts() - except netaddr.AddrFormatError as exc: - raise exception.InvalidInput(reason=six.text_type(exc)) - - -class Floating_ips_bulk(extensions.ExtensionDescriptor): - """Bulk handling of Floating IPs.""" - - name = "FloatingIpsBulk" - alias = "os-floating-ips-bulk" - namespace = ("http://docs.openstack.org/compute/ext/" - "floating_ips_bulk/api/v2") - updated = "2012-10-29T19:25:27Z" - - def get_resources(self): - resources = [] - resource = extensions.ResourceExtension('os-floating-ips-bulk', - FloatingIPBulkController()) - resources.append(resource) - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/fping.py b/nova/api/openstack/compute/legacy_v2/contrib/fping.py deleted file mode 100644 index 05aa42bf59b..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/fping.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright 2011 Grid Dynamics -# Copyright 2011 OpenStack Foundation -# 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 itertools -import os - -from oslo_config import cfg -import six -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova import compute -from nova.i18n import _ -from nova import utils - -authorize = extensions.extension_authorizer('compute', 'fping') -authorize_all_tenants = extensions.extension_authorizer( - 'compute', 'fping:all_tenants') -fping_opts = [ - cfg.StrOpt("fping_path", - default="/usr/sbin/fping", - help="Full path to fping."), -] - -CONF = cfg.CONF -CONF.register_opts(fping_opts) - - -class FpingController(object): - - def __init__(self, network_api=None): - self.compute_api = compute.API() - self.last_call = {} - - def check_fping(self): - if not os.access(CONF.fping_path, os.X_OK): - raise exc.HTTPServiceUnavailable( - explanation=_("fping utility is not found.")) - - @staticmethod - def fping(ips): - fping_ret = utils.execute(CONF.fping_path, *ips, - check_exit_code=False) - if not fping_ret: - return set() - alive_ips = set() - for line in fping_ret[0].split("\n"): - ip = line.split(" ", 1)[0] - if "alive" in line: - alive_ips.add(ip) - return alive_ips - - @staticmethod - def _get_instance_ips(context, instance): - ret = [] - for network in common.get_networks_for_instance( - context, instance).values(): - all_ips = itertools.chain(network["ips"], network["floating_ips"]) - ret += [ip["address"] for ip in all_ips] - return ret - - def index(self, req): - context = req.environ["nova.context"] - search_opts = dict(deleted=False) - if "all_tenants" in req.GET: - authorize_all_tenants(context) - else: - authorize(context) - if context.project_id: - search_opts["project_id"] = context.project_id - else: - search_opts["user_id"] = context.user_id - self.check_fping() - include = req.GET.get("include", None) - if include: - include = set(include.split(",")) - exclude = set() - else: - include = None - exclude = req.GET.get("exclude", None) - if exclude: - exclude = set(exclude.split(",")) - else: - exclude = set() - - instance_list = self.compute_api.get_all( - context, search_opts=search_opts, want_objects=True) - ip_list = [] - instance_ips = {} - instance_projects = {} - - for instance in instance_list: - uuid = instance.uuid - if uuid in exclude or (include is not None and - uuid not in include): - continue - ips = [str(ip) for ip in self._get_instance_ips(context, instance)] - instance_ips[uuid] = ips - instance_projects[uuid] = instance.project_id - ip_list += ips - alive_ips = self.fping(ip_list) - res = [] - for instance_uuid, ips in six.iteritems(instance_ips): - res.append({ - "id": instance_uuid, - "project_id": instance_projects[instance_uuid], - "alive": bool(set(ips) & alive_ips), - }) - return {"servers": res} - - def show(self, req, id): - context = req.environ["nova.context"] - authorize(context) - self.check_fping() - instance = common.get_instance(self.compute_api, context, id) - ips = [str(ip) for ip in self._get_instance_ips(context, instance)] - alive_ips = self.fping(ips) - return { - "server": { - "id": instance.uuid, - "project_id": instance.project_id, - "alive": bool(set(ips) & alive_ips), - } - } - - -class Fping(extensions.ExtensionDescriptor): - """Fping Management Extension.""" - - name = "Fping" - alias = "os-fping" - namespace = "http://docs.openstack.org/compute/ext/fping/api/v1.1" - updated = "2012-07-06T00:00:00Z" - - def get_resources(self): - res = extensions.ResourceExtension( - "os-fping", - FpingController()) - return [res] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/hide_server_addresses.py b/nova/api/openstack/compute/legacy_v2/contrib/hide_server_addresses.py deleted file mode 100644 index 507123b7947..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/hide_server_addresses.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# 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. - -"""Extension for hiding server addresses in certain states.""" - -from oslo_config import cfg - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova.compute import vm_states - - -CONF = cfg.CONF -CONF.import_opt('osapi_hide_server_address_states', - 'nova.api.openstack.compute.hide_server_addresses') - -authorize = extensions.soft_extension_authorizer('compute', - 'hide_server_addresses') - - -class Controller(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(Controller, self).__init__(*args, **kwargs) - hidden_states = CONF.osapi_hide_server_address_states - - # NOTE(jkoelker) _ is not considered uppercase ;) - valid_vm_states = [getattr(vm_states, state) - for state in dir(vm_states) - if state.isupper()] - self.hide_address_states = [state.lower() - for state in hidden_states - if state in valid_vm_states] - - def _perhaps_hide_addresses(self, instance, resp_server): - if instance.get('vm_state') in self.hide_address_states: - resp_server['addresses'] = {} - - @wsgi.extends - def show(self, req, resp_obj, id): - resp = resp_obj - if not authorize(req.environ['nova.context']): - return - - if 'server' in resp.obj and 'addresses' in resp.obj['server']: - instance = req.get_db_instance(id) - self._perhaps_hide_addresses(instance, resp.obj['server']) - - @wsgi.extends - def detail(self, req, resp_obj): - resp = resp_obj - if not authorize(req.environ['nova.context']): - return - - for server in list(resp.obj['servers']): - if 'addresses' in server: - instance = req.get_db_instance(server['id']) - self._perhaps_hide_addresses(instance, server) - - -class Hide_server_addresses(extensions.ExtensionDescriptor): - """Support hiding server addresses in certain states.""" - - name = 'HideServerAddresses' - alias = 'os-hide-server-addresses' - namespace = ('http://docs.openstack.org/compute/ext/' - 'hide_server_addresses/api/v1.1') - updated = '2012-12-11T00:00:00Z' - - def get_controller_extensions(self): - return [extensions.ControllerExtension(self, 'servers', Controller())] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/hosts.py b/nova/api/openstack/compute/legacy_v2/contrib/hosts.py deleted file mode 100644 index 8902b377abc..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/hosts.py +++ /dev/null @@ -1,335 +0,0 @@ -# Copyright (c) 2011 OpenStack Foundation -# 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. - -"""The hosts admin extension.""" - -from oslo_log import log as logging -import six -import webob.exc - -from nova.api.openstack import extensions -from nova import compute -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova.i18n import _LI -from nova import objects - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'hosts') - - -class HostController(object): - """The Hosts API controller for the OpenStack API.""" - def __init__(self): - self.api = compute.HostAPI() - super(HostController, self).__init__() - - def index(self, req): - """Returns a dict in the format: - - | {'hosts': [{'host_name': 'some.host.name', - | 'service': 'cells', - | 'zone': 'internal'}, - | {'host_name': 'some.other.host.name', - | 'service': 'cells', - | 'zone': 'internal'}, - | {'host_name': 'some.celly.host.name', - | 'service': 'cells', - | 'zone': 'internal'}, - | {'host_name': 'console1.host.com', - | 'service': 'consoleauth', - | 'zone': 'internal'}, - | {'host_name': 'network1.host.com', - | 'service': 'network', - | 'zone': 'internal'}, - | {'host_name': 'netwwork2.host.com', - | 'service': 'network', - | 'zone': 'internal'}, - | {'host_name': 'compute1.host.com', - | 'service': 'compute', - | 'zone': 'nova'}, - | {'host_name': 'compute2.host.com', - | 'service': 'compute', - | 'zone': 'nova'}, - | {'host_name': 'sched1.host.com', - | 'service': 'scheduler', - | 'zone': 'internal'}, - | {'host_name': 'sched2.host.com', - | 'service': 'scheduler', - | 'zone': 'internal'}, - | {'host_name': 'vol1.host.com', - | 'service': 'volume', - | 'zone': 'internal'}]} - - """ - context = req.environ['nova.context'] - authorize(context) - - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks - nova_context.require_admin_context(context) - - filters = {'disabled': False} - zone = req.GET.get('zone', None) - if zone: - filters['availability_zone'] = zone - services = self.api.service_get_all(context, filters=filters, - set_zones=True) - hosts = [] - api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata') - for service in services: - if service.binary not in api_services: - hosts.append({'host_name': service['host'], - 'service': service['topic'], - 'zone': service['availability_zone']}) - return {'hosts': hosts} - - def update(self, req, id, body): - """Updates a specified body. - - :param body: example format {'status': 'enable', - 'maintenance_mode': 'enable'} - """ - def read_enabled(orig_val, msg): - """Checks a specified orig_val and returns True for 'enabled' - and False for 'disabled'. - - :param orig_val: A string with either 'enable' or 'disable'. May - be surrounded by whitespace, and case doesn't - matter - :param msg: The message to be passed to HTTPBadRequest. A single - %s will be replaced with orig_val. - """ - val = orig_val.strip().lower() - if val == "enable": - return True - elif val == "disable": - return False - else: - raise webob.exc.HTTPBadRequest(explanation=msg % orig_val) - context = req.environ['nova.context'] - authorize(context) - - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - - # See what the user wants to 'update' - params = {k.strip().lower(): v for k, v in six.iteritems(body)} - orig_status = status = params.pop('status', None) - orig_maint_mode = maint_mode = params.pop('maintenance_mode', None) - # Validate the request - if len(params) > 0: - # Some extra param was passed. Fail. - explanation = _("Invalid update setting: '%s'") % list( - params.keys())[0] - raise webob.exc.HTTPBadRequest(explanation=explanation) - if orig_status is not None: - status = read_enabled(orig_status, _("Invalid status: '%s'")) - if orig_maint_mode is not None: - maint_mode = read_enabled(orig_maint_mode, _("Invalid mode: '%s'")) - if status is None and maint_mode is None: - explanation = _("'status' or 'maintenance_mode' needed for " - "host update") - raise webob.exc.HTTPBadRequest(explanation=explanation) - # Make the calls and merge the results - result = {'host': id} - if status is not None: - result['status'] = self._set_enabled_status(context, id, status) - if maint_mode is not None: - result['maintenance_mode'] = self._set_host_maintenance(context, - id, maint_mode) - return result - - def _set_host_maintenance(self, context, host_name, mode=True): - """Start/Stop host maintenance window. On start, it triggers - guest VMs evacuation. - """ - LOG.info(_LI("Putting host %(host_name)s in maintenance mode " - "%(mode)s."), - {'host_name': host_name, 'mode': mode}) - try: - result = self.api.set_host_maintenance(context, host_name, mode) - except NotImplementedError: - msg = _("Virt driver does not implement host maintenance mode.") - raise webob.exc.HTTPNotImplemented(explanation=msg) - except exception.NotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except exception.ComputeServiceUnavailable as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - if result not in ("on_maintenance", "off_maintenance"): - raise webob.exc.HTTPBadRequest(explanation=result) - return result - - def _set_enabled_status(self, context, host_name, enabled): - """Sets the specified host's ability to accept new instances. - - :param enabled: a boolean - if False no new VMs will be able to start - on the host - """ - if enabled: - LOG.info(_LI("Enabling host %s."), host_name) - else: - LOG.info(_LI("Disabling host %s."), host_name) - try: - result = self.api.set_host_enabled(context, host_name=host_name, - enabled=enabled) - except NotImplementedError: - msg = _("Virt driver does not implement host disabled status.") - raise webob.exc.HTTPNotImplemented(explanation=msg) - except exception.NotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except exception.ComputeServiceUnavailable as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - if result not in ("enabled", "disabled"): - raise webob.exc.HTTPBadRequest(explanation=result) - return result - - def _host_power_action(self, req, host_name, action): - """Reboots, shuts down or powers up the host.""" - context = req.environ['nova.context'] - authorize(context) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - try: - result = self.api.host_power_action(context, host_name=host_name, - action=action) - except NotImplementedError: - msg = _("Virt driver does not implement host power management.") - raise webob.exc.HTTPNotImplemented(explanation=msg) - except exception.NotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - except exception.ComputeServiceUnavailable as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - return {"host": host_name, "power_action": result} - - def startup(self, req, id): - return self._host_power_action(req, host_name=id, action="startup") - - def shutdown(self, req, id): - return self._host_power_action(req, host_name=id, action="shutdown") - - def reboot(self, req, id): - return self._host_power_action(req, host_name=id, action="reboot") - - @staticmethod - def _get_total_resources(host_name, compute_node): - return {'resource': {'host': host_name, - 'project': '(total)', - 'cpu': compute_node['vcpus'], - 'memory_mb': compute_node['memory_mb'], - 'disk_gb': compute_node['local_gb']}} - - @staticmethod - def _get_used_now_resources(host_name, compute_node): - return {'resource': {'host': host_name, - 'project': '(used_now)', - 'cpu': compute_node['vcpus_used'], - 'memory_mb': compute_node['memory_mb_used'], - 'disk_gb': compute_node['local_gb_used']}} - - @staticmethod - def _get_resource_totals_from_instances(host_name, instances): - cpu_sum = 0 - mem_sum = 0 - hdd_sum = 0 - for instance in instances: - cpu_sum += instance['vcpus'] - mem_sum += instance['memory_mb'] - hdd_sum += instance['root_gb'] + instance['ephemeral_gb'] - - return {'resource': {'host': host_name, - 'project': '(used_max)', - 'cpu': cpu_sum, - 'memory_mb': mem_sum, - 'disk_gb': hdd_sum}} - - @staticmethod - def _get_resources_by_project(host_name, instances): - # Getting usage resource per project - project_map = {} - for instance in instances: - resource = project_map.setdefault(instance['project_id'], - {'host': host_name, - 'project': instance['project_id'], - 'cpu': 0, - 'memory_mb': 0, - 'disk_gb': 0}) - resource['cpu'] += instance['vcpus'] - resource['memory_mb'] += instance['memory_mb'] - resource['disk_gb'] += (instance['root_gb'] + - instance['ephemeral_gb']) - return project_map - - def show(self, req, id): - """Shows the physical/usage resource given by hosts. - - :param id: hostname - :returns: expected to use HostShowTemplate. - ex.:: - - {'host': {'resource':D},..} - D: {'host': 'hostname','project': 'admin', - 'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30} - """ - context = req.environ['nova.context'] - - # NOTE(eliqiao): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - - host_name = id - try: - compute_node = ( - objects.ComputeNode.get_first_node_by_host_for_old_compat( - context, host_name)) - except exception.NotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - instances = self.api.instance_get_all_by_host(context, host_name) - resources = [self._get_total_resources(host_name, compute_node)] - resources.append(self._get_used_now_resources(host_name, - compute_node)) - resources.append(self._get_resource_totals_from_instances(host_name, - instances)) - by_proj_resources = self._get_resources_by_project(host_name, - instances) - for resource in six.itervalues(by_proj_resources): - resources.append({'resource': resource}) - return {'host': resources} - - -class Hosts(extensions.ExtensionDescriptor): - """Admin-only host administration.""" - - name = "Hosts" - alias = "os-hosts" - namespace = "http://docs.openstack.org/compute/ext/hosts/api/v1.1" - updated = "2011-06-29T00:00:00Z" - - def get_resources(self): - resources = [extensions.ResourceExtension('os-hosts', - HostController(), - collection_actions={'update': 'PUT'}, - member_actions={"startup": "GET", "shutdown": "GET", - "reboot": "GET"})] - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/hypervisor_status.py b/nova/api/openstack/compute/legacy_v2/contrib/hypervisor_status.py deleted file mode 100644 index 94bcabca48e..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/hypervisor_status.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2014 Intel Corp. -# -# 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 nova.api.openstack import extensions - - -class Hypervisor_status(extensions.ExtensionDescriptor): - """Show hypervisor status.""" - - name = "HypervisorStatus" - alias = "os-hypervisor-status" - namespace = ("http://docs.openstack.org/compute/ext/" - "hypervisor_status/api/v1.1") - updated = "2014-04-17T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/hypervisors.py b/nova/api/openstack/compute/legacy_v2/contrib/hypervisors.py deleted file mode 100644 index bd3c910c0a0..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/hypervisors.py +++ /dev/null @@ -1,230 +0,0 @@ -# Copyright (c) 2012 OpenStack Foundation -# 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. - -"""The hypervisors admin extension.""" - -import webob.exc - -from nova.api.openstack import extensions -from nova import compute -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import servicegroup - - -authorize = extensions.extension_authorizer('compute', 'hypervisors') - - -class HypervisorsController(object): - """The Hypervisors API controller for the OpenStack API.""" - - def __init__(self, ext_mgr): - self.host_api = compute.HostAPI() - self.servicegroup_api = servicegroup.API() - super(HypervisorsController, self).__init__() - self.ext_mgr = ext_mgr - - def _view_hypervisor(self, hypervisor, service, detail, servers=None, - **kwargs): - hyp_dict = { - 'id': hypervisor.id, - 'hypervisor_hostname': hypervisor.hypervisor_hostname, - } - - ext_status_loaded = self.ext_mgr.is_loaded('os-hypervisor-status') - if ext_status_loaded: - alive = self.servicegroup_api.service_is_up(service) - hyp_dict['state'] = 'up' if alive else "down" - hyp_dict['status'] = ( - 'disabled' if service.disabled else 'enabled') - - if detail and not servers: - fields = ('vcpus', 'memory_mb', 'local_gb', 'vcpus_used', - 'memory_mb_used', 'local_gb_used', - 'hypervisor_type', 'hypervisor_version', - 'free_ram_mb', 'free_disk_gb', 'current_workload', - 'running_vms', 'cpu_info', 'disk_available_least') - ext_loaded = self.ext_mgr.is_loaded('os-extended-hypervisors') - if ext_loaded: - fields += ('host_ip',) - for field in fields: - hyp_dict[field] = hypervisor[field] - - hyp_dict['service'] = { - 'id': service.id, - 'host': hypervisor.host, - } - if ext_status_loaded: - hyp_dict['service'].update( - disabled_reason=service.disabled_reason) - - if servers: - hyp_dict['servers'] = [dict(name=serv['name'], uuid=serv['uuid']) - for serv in servers] - - # Add any additional info - if kwargs: - hyp_dict.update(kwargs) - - return hyp_dict - - def index(self, req): - context = req.environ['nova.context'] - authorize(context) - - # NOTE(eliqiao): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - - compute_nodes = self.host_api.compute_node_get_all(context) - req.cache_db_compute_nodes(compute_nodes) - return dict(hypervisors=[self._view_hypervisor( - hyp, - self.host_api.service_get_by_compute_host( - context, hyp.host), - False) - for hyp in compute_nodes]) - - def detail(self, req): - context = req.environ['nova.context'] - authorize(context) - - # NOTE(eliqiao): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - - compute_nodes = self.host_api.compute_node_get_all(context) - req.cache_db_compute_nodes(compute_nodes) - return dict(hypervisors=[self._view_hypervisor( - hyp, - self.host_api.service_get_by_compute_host( - context, hyp.host), - True) - for hyp in compute_nodes]) - - def show(self, req, id): - context = req.environ['nova.context'] - authorize(context) - try: - hyp = self.host_api.compute_node_get(context, id) - req.cache_db_compute_node(hyp) - except (ValueError, exception.ComputeHostNotFound): - msg = _("Hypervisor with ID '%s' could not be found.") % id - raise webob.exc.HTTPNotFound(explanation=msg) - service = self.host_api.service_get_by_compute_host( - context, hyp.host) - return dict(hypervisor=self._view_hypervisor(hyp, service, True)) - - def uptime(self, req, id): - context = req.environ['nova.context'] - authorize(context) - try: - hyp = self.host_api.compute_node_get(context, id) - req.cache_db_compute_node(hyp) - except (ValueError, exception.ComputeHostNotFound): - msg = _("Hypervisor with ID '%s' could not be found.") % id - raise webob.exc.HTTPNotFound(explanation=msg) - - # Get the uptime - try: - host = hyp.host - uptime = self.host_api.get_host_uptime(context, host) - except NotImplementedError: - msg = _("Virt driver does not implement uptime function.") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - service = self.host_api.service_get_by_compute_host(context, host) - return dict(hypervisor=self._view_hypervisor(hyp, service, False, - uptime=uptime)) - - def search(self, req, id): - context = req.environ['nova.context'] - authorize(context) - - # NOTE(eliqiao): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - - hypervisors = self.host_api.compute_node_search_by_hypervisor( - context, id) - if hypervisors: - return dict(hypervisors=[self._view_hypervisor( - hyp, - self.host_api.service_get_by_compute_host( - context, hyp.host), - False) - for hyp in hypervisors]) - else: - msg = _("No hypervisor matching '%s' could be found.") % id - raise webob.exc.HTTPNotFound(explanation=msg) - - def servers(self, req, id): - context = req.environ['nova.context'] - authorize(context) - - # NOTE(eliqiao): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova_context.require_admin_context(context) - - compute_nodes = self.host_api.compute_node_search_by_hypervisor( - context, id) - if not compute_nodes: - msg = _("No hypervisor matching '%s' could be found.") % id - raise webob.exc.HTTPNotFound(explanation=msg) - hypervisors = [] - for compute_node in compute_nodes: - instances = self.host_api.instance_get_all_by_host(context, - compute_node.host) - service = self.host_api.service_get_by_compute_host( - context, compute_node.host) - hyp = self._view_hypervisor(compute_node, service, False, - instances) - hypervisors.append(hyp) - return dict(hypervisors=hypervisors) - - def statistics(self, req): - context = req.environ['nova.context'] - authorize(context) - stats = self.host_api.compute_node_statistics(context) - return dict(hypervisor_statistics=stats) - - -class Hypervisors(extensions.ExtensionDescriptor): - """Admin-only hypervisor administration.""" - - name = "Hypervisors" - alias = "os-hypervisors" - namespace = "http://docs.openstack.org/compute/ext/hypervisors/api/v1.1" - updated = "2012-06-21T00:00:00Z" - - def get_resources(self): - resources = [extensions.ResourceExtension('os-hypervisors', - HypervisorsController(self.ext_mgr), - collection_actions={'detail': 'GET', - 'statistics': 'GET'}, - member_actions={'uptime': 'GET', - 'search': 'GET', - 'servers': 'GET'})] - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/image_size.py b/nova/api/openstack/compute/legacy_v2/contrib/image_size.py deleted file mode 100644 index 265375d4bcb..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/image_size.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2013 Rackspace Hosting -# 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. - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi - -authorize = extensions.soft_extension_authorizer('compute', 'image_size') - - -class ImageSizeController(wsgi.Controller): - - def _extend_image(self, image, image_cache): - key = "%s:size" % Image_size.alias - image[key] = image_cache['size'] - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ["nova.context"] - if authorize(context): - image_resp = resp_obj.obj['image'] - # image guaranteed to be in the cache due to the core API adding - # it in its 'show' method - image_cached = req.get_db_item('images', image_resp['id']) - self._extend_image(image_resp, image_cached) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if authorize(context): - images_resp = list(resp_obj.obj['images']) - # images guaranteed to be in the cache due to the core API adding - # it in its 'detail' method - for image in images_resp: - image_cached = req.get_db_item('images', image['id']) - self._extend_image(image, image_cached) - - -class Image_size(extensions.ExtensionDescriptor): - """Adds image size to image listings.""" - - name = "ImageSize" - alias = "OS-EXT-IMG-SIZE" - namespace = ("http://docs.openstack.org/compute/ext/" - "image_size/api/v1.1") - updated = "2013-02-19T00:00:00Z" - - def get_controller_extensions(self): - controller = ImageSizeController() - extension = extensions.ControllerExtension(self, 'images', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/instance_actions.py b/nova/api/openstack/compute/legacy_v2/contrib/instance_actions.py deleted file mode 100644 index 290fcf9ff18..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/instance_actions.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright 2013 Rackspace Hosting -# 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. - -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute - -authorize_actions = extensions.extension_authorizer('compute', - 'instance_actions') -authorize_events = extensions.soft_extension_authorizer('compute', - 'instance_actions:events') - -ACTION_KEYS = ['action', 'instance_uuid', 'request_id', 'user_id', - 'project_id', 'start_time', 'message'] -EVENT_KEYS = ['event', 'start_time', 'finish_time', 'result', 'traceback'] - - -class InstanceActionsController(wsgi.Controller): - - def __init__(self): - super(InstanceActionsController, self).__init__() - self.compute_api = compute.API() - self.action_api = compute.InstanceActionAPI() - - def _format_action(self, action_raw): - action = {} - for key in ACTION_KEYS: - action[key] = action_raw.get(key) - return action - - def _format_event(self, event_raw): - event = {} - for key in EVENT_KEYS: - event[key] = event_raw.get(key) - return event - - def index(self, req, server_id): - """Returns the list of actions recorded for a given instance.""" - context = req.environ["nova.context"] - instance = common.get_instance(self.compute_api, context, server_id) - authorize_actions(context, target=instance) - actions_raw = self.action_api.actions_get(context, instance) - actions = [self._format_action(action) for action in actions_raw] - return {'instanceActions': actions} - - def show(self, req, server_id, id): - """Return data about the given instance action.""" - context = req.environ['nova.context'] - instance = common.get_instance(self.compute_api, context, server_id) - authorize_actions(context, target=instance) - action = self.action_api.action_get_by_request_id(context, instance, - id) - if action is None: - raise exc.HTTPNotFound() - - action_id = action['id'] - action = self._format_action(action) - if authorize_events(context): - events_raw = self.action_api.action_events_get(context, instance, - action_id) - action['events'] = [self._format_event(evt) for evt in events_raw] - return {'instanceAction': action} - - -class Instance_actions(extensions.ExtensionDescriptor): - """View a log of actions and events taken on an instance.""" - - name = "InstanceActions" - alias = "os-instance-actions" - namespace = ("http://docs.openstack.org/compute/ext/" - "instance-actions/api/v1.1") - updated = "2013-02-08T00:00:00Z" - - def get_resources(self): - ext = extensions.ResourceExtension('os-instance-actions', - InstanceActionsController(), - parent=dict( - member_name='server', - collection_name='servers')) - return [ext] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/instance_usage_audit_log.py b/nova/api/openstack/compute/legacy_v2/contrib/instance_usage_audit_log.py deleted file mode 100644 index bbf154b3f68..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/instance_usage_audit_log.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# 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_config import cfg -import webob.exc - -from nova.api.openstack import extensions -from nova import compute -from nova import context as nova_context -from nova.i18n import _ -from nova import utils - -CONF = cfg.CONF -CONF.import_opt('compute_topic', 'nova.compute.rpcapi') - - -authorize = extensions.extension_authorizer('compute', - 'instance_usage_audit_log') - - -class InstanceUsageAuditLogController(object): - def __init__(self): - self.host_api = compute.HostAPI() - - def index(self, req): - context = req.environ['nova.context'] - authorize(context) - task_log = self._get_audit_task_logs(context) - return {'instance_usage_audit_logs': task_log} - - def show(self, req, id): - context = req.environ['nova.context'] - authorize(context) - try: - if '.' in id: - before_date = datetime.datetime.strptime(str(id), - "%Y-%m-%d %H:%M:%S.%f") - else: - before_date = datetime.datetime.strptime(str(id), - "%Y-%m-%d %H:%M:%S") - except ValueError: - msg = _("Invalid timestamp for date %s") % id - raise webob.exc.HTTPBadRequest(explanation=msg) - task_log = self._get_audit_task_logs(context, - before=before_date) - return {'instance_usage_audit_log': task_log} - - def _get_audit_task_logs(self, context, begin=None, end=None, - before=None): - """Returns a full log for all instance usage audit tasks on all - computes. - - :param begin: datetime beginning of audit period to get logs for, - Defaults to the beginning of the most recently completed - audit period prior to the 'before' date. - :param end: datetime ending of audit period to get logs for, - Defaults to the ending of the most recently completed - audit period prior to the 'before' date. - :param before: By default we look for the audit period most recently - completed before this datetime. Has no effect if both begin and end - are specified. - """ - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - defbegin, defend = utils.last_completed_audit_period(before=before) - if begin is None: - begin = defbegin - if end is None: - end = defend - task_logs = self.host_api.task_log_get_all(context, - "instance_usage_audit", - begin, end) - # We do this this way to include disabled compute services, - # which can have instances on them. (mdragon) - filters = {'topic': CONF.compute_topic} - services = self.host_api.service_get_all(context, filters=filters) - hosts = set(serv['host'] for serv in services) - seen_hosts = set() - done_hosts = set() - running_hosts = set() - total_errors = 0 - total_items = 0 - for tlog in task_logs: - seen_hosts.add(tlog['host']) - if tlog['state'] == "DONE": - done_hosts.add(tlog['host']) - if tlog['state'] == "RUNNING": - running_hosts.add(tlog['host']) - total_errors += tlog['errors'] - total_items += tlog['task_items'] - log = {tl['host']: dict(state=tl['state'], - instances=tl['task_items'], - errors=tl['errors'], - message=tl['message']) - for tl in task_logs} - missing_hosts = hosts - seen_hosts - overall_status = "%s hosts done. %s errors." % ( - 'ALL' if len(done_hosts) == len(hosts) - else "%s of %s" % (len(done_hosts), len(hosts)), - total_errors) - return dict(period_beginning=str(begin), - period_ending=str(end), - num_hosts=len(hosts), - num_hosts_done=len(done_hosts), - num_hosts_running=len(running_hosts), - num_hosts_not_run=len(missing_hosts), - hosts_not_run=list(missing_hosts), - total_instances=total_items, - total_errors=total_errors, - overall_status=overall_status, - log=log) - - -class Instance_usage_audit_log(extensions.ExtensionDescriptor): - """Admin-only Task Log Monitoring.""" - name = "OSInstanceUsageAuditLog" - alias = "os-instance_usage_audit_log" - namespace = "http://docs.openstack.org/ext/services/api/v1.1" - updated = "2012-07-06T01:00:00Z" - - def get_resources(self): - ext = extensions.ResourceExtension('os-instance_usage_audit_log', - InstanceUsageAuditLogController()) - return [ext] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/keypairs.py b/nova/api/openstack/compute/legacy_v2/contrib/keypairs.py deleted file mode 100644 index d4a34d035e0..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/keypairs.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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. - -"""Keypair management extension.""" - -import webob -import webob.exc - -from nova.api.openstack.compute.legacy_v2 import servers -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova.compute import api as compute_api -from nova import exception -from nova.i18n import _ - - -authorize = extensions.extension_authorizer('compute', 'keypairs') -soft_authorize = extensions.soft_extension_authorizer('compute', 'keypairs') - - -class KeypairController(object): - - """Keypair API controller for the OpenStack API.""" - def __init__(self): - self.api = compute_api.KeypairAPI() - - def _filter_keypair(self, keypair, **attrs): - clean = { - 'name': keypair.name, - 'public_key': keypair.public_key, - 'fingerprint': keypair.fingerprint, - } - for attr in attrs: - clean[attr] = keypair[attr] - return clean - - def create(self, req, body): - """Create or import keypair. - - Sending name will generate a key and return private_key - and fingerprint. - - You can send a public_key to add an existing ssh key - - params: keypair object with: - name (required) - string - public_key (optional) - string - """ - - context = req.environ['nova.context'] - authorize(context, action='create') - - try: - params = body['keypair'] - name = params['name'] - except KeyError: - msg = _("Invalid request body") - raise webob.exc.HTTPBadRequest(explanation=msg) - - try: - if 'public_key' in params: - keypair = self.api.import_key_pair(context, - context.user_id, name, - params['public_key']) - keypair = self._filter_keypair(keypair, user_id=True) - else: - keypair, private_key = self.api.create_key_pair( - context, context.user_id, name) - keypair = self._filter_keypair(keypair, user_id=True) - keypair['private_key'] = private_key - - return {'keypair': keypair} - - except exception.KeypairLimitExceeded: - msg = _("Quota exceeded, too many key pairs.") - raise webob.exc.HTTPForbidden(explanation=msg) - except exception.InvalidKeypair as exc: - raise webob.exc.HTTPBadRequest(explanation=exc.format_message()) - except exception.KeyPairExists as exc: - raise webob.exc.HTTPConflict(explanation=exc.format_message()) - - def delete(self, req, id): - """Delete a keypair with a given name.""" - context = req.environ['nova.context'] - authorize(context, action='delete') - try: - self.api.delete_key_pair(context, context.user_id, id) - except exception.KeypairNotFound as exc: - raise webob.exc.HTTPNotFound(explanation=exc.format_message()) - return webob.Response(status_int=202) - - def show(self, req, id): - """Return data for the given key name.""" - context = req.environ['nova.context'] - authorize(context, action='show') - - try: - # The return object needs to be a dict in order to pop the 'type' - # field, since it is incompatible with API version <= 2.1. - keypair = self.api.get_key_pair(context, context.user_id, id) - keypair = self._filter_keypair(keypair, created_at=True, - deleted=True, deleted_at=True, - id=True, user_id=True, - updated_at=True) - except exception.KeypairNotFound as exc: - raise webob.exc.HTTPNotFound(explanation=exc.format_message()) - return {'keypair': keypair} - - def index(self, req): - """List of keypairs for a user.""" - context = req.environ['nova.context'] - authorize(context, action='index') - key_pairs = self.api.get_key_pairs(context, context.user_id) - rval = [] - for key_pair in key_pairs: - rval.append({'keypair': self._filter_keypair(key_pair)}) - - return {'keypairs': rval} - - -class Controller(servers.Controller): - - def _add_key_name(self, req, servers): - for server in servers: - db_server = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show'/'detail' methods. - server['key_name'] = db_server['key_name'] - - def _show(self, req, resp_obj): - if 'server' in resp_obj.obj: - server = resp_obj.obj['server'] - self._add_key_name(req, [server]) - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if soft_authorize(context): - self._show(req, resp_obj) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if 'servers' in resp_obj.obj and soft_authorize(context): - servers = resp_obj.obj['servers'] - self._add_key_name(req, servers) - - -class Keypairs(extensions.ExtensionDescriptor): - """Keypair Support.""" - - name = "Keypairs" - alias = "os-keypairs" - namespace = "http://docs.openstack.org/compute/ext/keypairs/api/v1.1" - updated = "2011-08-08T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension( - 'os-keypairs', - KeypairController()) - resources.append(res) - return resources - - def get_controller_extensions(self): - controller = Controller(self.ext_mgr) - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/migrations.py b/nova/api/openstack/compute/legacy_v2/contrib/migrations.py deleted file mode 100644 index 472dbf6dfa5..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/migrations.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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 nova.api.openstack import extensions -from nova import compute -from nova import context as nova_context -from nova.objects import base as obj_base - - -XMLNS = "http://docs.openstack.org/compute/ext/migrations/api/v2.0" -ALIAS = "os-migrations" - - -def authorize(context, action_name): - action = 'migrations:%s' % action_name - extensions.extension_authorizer('compute', action)(context) - - -def output(migrations_obj): - """Returns the desired output of the API from an object. - - From a MigrationsList's object this method returns a list of - primitive objects with the only necessary fields. - """ - detail_keys = ['memory_total', 'memory_processed', 'memory_remaining', - 'disk_total', 'disk_processed', 'disk_remaining'] - # Note(Shaohe Feng): We need to leverage the oslo.versionedobjects. - # Then we can pass the target version to it's obj_to_primitive. - objects = obj_base.obj_to_primitive(migrations_obj) - objects = [x for x in objects if not x['hidden']] - for obj in objects: - del obj['deleted'] - del obj['deleted_at'] - del obj['migration_type'] - del obj['hidden'] - if 'memory_total' in obj: - for key in detail_keys: - del obj[key] - - return objects - - -class MigrationsController(object): - """Controller for accessing migrations in OpenStack API.""" - def __init__(self): - self.compute_api = compute.API() - - def index(self, req): - """Return all migrations in progress.""" - context = req.environ['nova.context'] - authorize(context, "index") - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova_context.require_admin_context(context) - migrations = self.compute_api.get_migrations(context, req.GET) - return {'migrations': output(migrations)} - - -class Migrations(extensions.ExtensionDescriptor): - """Provide data on migrations.""" - name = "Migrations" - alias = ALIAS - namespace = XMLNS - updated = "2013-05-30T00:00:00Z" - - def get_resources(self): - resources = [] - resource = extensions.ResourceExtension('os-migrations', - MigrationsController()) - resources.append(resource) - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/multinic.py b/nova/api/openstack/compute/legacy_v2/contrib/multinic.py deleted file mode 100644 index 6ea2fa9075d..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/multinic.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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. - -"""The multinic extension.""" - -from oslo_log import log as logging -import webob -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova.i18n import _ -from nova.i18n import _LE - - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'multinic') - - -class MultinicController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(MultinicController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - - @wsgi.action('addFixedIp') - def _add_fixed_ip(self, req, id, body): - """Adds an IP on a given network to an instance.""" - context = req.environ['nova.context'] - authorize(context) - - # Validate the input entity - if 'networkId' not in body['addFixedIp']: - msg = _("Missing 'networkId' argument for addFixedIp") - raise exc.HTTPBadRequest(explanation=msg) - - instance = common.get_instance(self.compute_api, context, id) - network_id = body['addFixedIp']['networkId'] - try: - self.compute_api.add_fixed_ip(context, instance, network_id) - except exception.NoMoreFixedIps as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - return webob.Response(status_int=202) - - @wsgi.action('removeFixedIp') - def _remove_fixed_ip(self, req, id, body): - """Removes an IP from an instance.""" - context = req.environ['nova.context'] - authorize(context) - - # Validate the input entity - if 'address' not in body['removeFixedIp']: - msg = _("Missing 'address' argument for removeFixedIp") - raise exc.HTTPBadRequest(explanation=msg) - - instance = common.get_instance(self.compute_api, context, id) - address = body['removeFixedIp']['address'] - - try: - self.compute_api.remove_fixed_ip(context, instance, address) - except exception.FixedIpNotFoundForSpecificInstance: - LOG.exception(_LE("Unable to find address %r"), address, - instance=instance) - raise exc.HTTPBadRequest() - - return webob.Response(status_int=202) - - -# Note: The class name is as it has to be for this to be loaded as an -# extension--only first character capitalized. -class Multinic(extensions.ExtensionDescriptor): - """Multiple network support.""" - - name = "Multinic" - alias = "NMN" - namespace = "http://docs.openstack.org/compute/ext/multinic/api/v1.1" - updated = "2011-06-09T00:00:00Z" - - def get_controller_extensions(self): - controller = MultinicController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/multiple_create.py b/nova/api/openstack/compute/legacy_v2/contrib/multiple_create.py deleted file mode 100644 index 1c0b8d7bb57..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/multiple_create.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 nova.api.openstack import extensions - - -class Multiple_create(extensions.ExtensionDescriptor): - """Allow multiple create in the Create Server v1.1 API.""" - - name = "MultipleCreate" - alias = "os-multiple-create" - namespace = ("http://docs.openstack.org/compute/ext/" - "multiplecreate/api/v1.1") - updated = "2012-08-07T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/networks_associate.py b/nova/api/openstack/compute/legacy_v2/contrib/networks_associate.py deleted file mode 100644 index 506ff1e946e..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/networks_associate.py +++ /dev/null @@ -1,106 +0,0 @@ -# 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 webob -from webob import exc - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import network - -authorize = extensions.extension_authorizer('compute', 'networks_associate') - - -class NetworkAssociateActionController(wsgi.Controller): - """Network Association API Controller.""" - - def __init__(self, network_api=None): - self.network_api = network_api or network.API() - - @wsgi.action("disassociate_host") - def _disassociate_host_only(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. call db API objects.Network.associate - nova_context.require_admin_context(context) - - try: - self.network_api.associate(context, id, host=None) - except exception.NetworkNotFound: - msg = _("Network not found") - raise exc.HTTPNotFound(explanation=msg) - except NotImplementedError: - msg = _('Disassociate host is not implemented by the configured ' - 'Network API') - raise exc.HTTPNotImplemented(explanation=msg) - return webob.Response(status_int=202) - - @wsgi.action("disassociate_project") - def _disassociate_project_only(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. call db API objects.Network.associate - nova_context.require_admin_context(context) - - try: - self.network_api.associate(context, id, project=None) - except exception.NetworkNotFound: - msg = _("Network not found") - raise exc.HTTPNotFound(explanation=msg) - except NotImplementedError: - msg = _('Disassociate project is not implemented by the ' - 'configured Network API') - raise exc.HTTPNotImplemented(explanation=msg) - - return webob.Response(status_int=202) - - @wsgi.action("associate_host") - def _associate_host(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. call db API objects.Network.associate - nova_context.require_admin_context(context) - - try: - self.network_api.associate(context, id, - host=body['associate_host']) - except exception.NetworkNotFound: - msg = _("Network not found") - raise exc.HTTPNotFound(explanation=msg) - except NotImplementedError: - msg = _('Associate host is not implemented by the configured ' - 'Network API') - raise exc.HTTPNotImplemented(explanation=msg) - - return webob.Response(status_int=202) - - -class Networks_associate(extensions.ExtensionDescriptor): - """Network association support.""" - - name = "NetworkAssociationSupport" - alias = "os-networks-associate" - namespace = ("http://docs.openstack.org/compute/ext/" - "networks_associate/api/v2") - updated = "2012-11-19T00:00:00Z" - - def get_controller_extensions(self): - extension = extensions.ControllerExtension( - self, 'os-networks', NetworkAssociateActionController()) - - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/os_networks.py b/nova/api/openstack/compute/legacy_v2/contrib/os_networks.py deleted file mode 100644 index 5cc29054d68..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/os_networks.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright 2011 Grid Dynamics -# Copyright 2011 OpenStack Foundation -# 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 netaddr -import webob -from webob import exc - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import network -from nova.objects import base as base_obj -from nova.objects import fields as obj_fields - -authorize = extensions.extension_authorizer('compute', 'networks') -authorize_view = extensions.extension_authorizer('compute', - 'networks:view') -extended_fields = ('mtu', 'dhcp_server', 'enable_dhcp', 'share_address') - - -def network_dict(context, network, extended): - fields = ('id', 'cidr', 'netmask', 'gateway', 'broadcast', 'dns1', 'dns2', - 'cidr_v6', 'gateway_v6', 'label', 'netmask_v6') - admin_fields = ('created_at', 'updated_at', 'deleted_at', 'deleted', - 'injected', 'bridge', 'vlan', 'vpn_public_address', - 'vpn_public_port', 'vpn_private_address', 'dhcp_start', - 'project_id', 'host', 'bridge_interface', 'multi_host', - 'priority', 'rxtx_base') - if network: - # NOTE(mnaser): We display a limited set of fields so users can know - # what networks are available, extra system-only fields - # are only visible if they are an admin. - if context.is_admin: - fields += admin_fields - if extended: - fields += extended_fields - # TODO(mriedem): Remove the NovaObject type check once the - # network.create API is returning objects. - is_obj = isinstance(network, base_obj.NovaObject) - result = {} - for field in fields: - # NOTE(mriedem): If network is an object, IPAddress fields need to - # be cast to a string so they look the same in the response as - # before the objects conversion. - if is_obj and isinstance(network.fields[field].AUTO_TYPE, - obj_fields.IPAddress): - # NOTE(danms): Here, network should be an object, which could - # have come from neutron and thus be missing most of the - # attributes. Providing a default to get() avoids trying to - # lazy-load missing attributes. - val = network.get(field, None) - if val is not None: - result[field] = str(val) - else: - result[field] = val - else: - # It's either not an object or it's not an IPAddress field. - result[field] = network.get(field, None) - uuid = network.get('uuid') - if uuid: - result['id'] = uuid - return result - else: - return {} - - -class NetworkController(wsgi.Controller): - - def __init__(self, network_api=None, ext_mgr=None): - self.network_api = network_api or network.API() - if ext_mgr: - self.extended = ext_mgr.is_loaded('os-extended-networks') - else: - self.extended = False - - def index(self, req): - context = req.environ['nova.context'] - authorize_view(context) - networks = self.network_api.get_all(context) - result = [network_dict(context, net_ref, self.extended) - for net_ref in networks] - return {'networks': result} - - @wsgi.action("disassociate") - def _disassociate_host_and_project(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. call db API objects.Network.associate - nova_context.require_admin_context(context) - - try: - self.network_api.associate(context, id, host=None, project=None) - except exception.NetworkNotFound: - msg = _("Network not found") - raise exc.HTTPNotFound(explanation=msg) - except NotImplementedError: - msg = _('Disassociate network is not implemented by the ' - 'configured Network API') - raise exc.HTTPNotImplemented(explanation=msg) - return webob.Response(status_int=202) - - def show(self, req, id): - context = req.environ['nova.context'] - authorize_view(context) - - try: - network = self.network_api.get(context, id) - except exception.NetworkNotFound: - msg = _("Network not found") - raise exc.HTTPNotFound(explanation=msg) - return {'network': network_dict(context, network, self.extended)} - - def delete(self, req, id): - context = req.environ['nova.context'] - authorize(context) - try: - self.network_api.delete(context, id) - except exception.NetworkInUse as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.NetworkNotFound: - msg = _("Network not found") - raise exc.HTTPNotFound(explanation=msg) - return webob.Response(status_int=202) - - def create(self, req, body): - context = req.environ['nova.context'] - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. call db API objects.Network.create - nova_context.require_admin_context(context) - - def bad(e): - return exc.HTTPBadRequest(explanation=e) - - if not (body and body.get("network")): - raise bad(_("Missing network in body")) - - params = body["network"] - if not params.get("label"): - raise bad(_("Network label is required")) - - cidr = params.get("cidr") or params.get("cidr_v6") - if not cidr: - raise bad(_("Network cidr or cidr_v6 is required")) - - if params.get("project_id") == "": - params["project_id"] = None - - params["num_networks"] = 1 - try: - params["network_size"] = netaddr.IPNetwork(cidr).size - except netaddr.AddrFormatError: - msg = _('%s is not a valid IP network') % cidr - raise exc.HTTPBadRequest(explanation=msg) - - if not self.extended: - create_params = ('allowed_start', 'allowed_end') - for field in extended_fields + create_params: - if field in params: - del params[field] - - try: - network = self.network_api.create(context, **params)[0] - except (exception.InvalidCidr, - exception.InvalidIntValue, - exception.InvalidAddress, - exception.NetworkNotCreated) as ex: - raise exc.HTTPBadRequest(explanation=ex.format_message) - except exception.CidrConflict as ex: - raise exc.HTTPConflict(explanation=ex.format_message()) - return {"network": network_dict(context, network, self.extended)} - - def add(self, req, body): - context = req.environ['nova.context'] - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. call db API objects.Network.associate - nova_context.require_admin_context(context) - if not body: - raise exc.HTTPUnprocessableEntity() - - network_id = body.get('id', None) - project_id = context.project_id - - try: - self.network_api.add_network_to_project( - context, project_id, network_id) - except NotImplementedError: - msg = (_("VLAN support must be enabled")) - raise exc.HTTPNotImplemented(explanation=msg) - except (exception.NoMoreNetworks, - exception.NetworkNotFoundForUUID) as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - return webob.Response(status_int=202) - - -class Os_networks(extensions.ExtensionDescriptor): - """Admin-only Network Management Extension.""" - - name = "Networks" - alias = "os-networks" - namespace = ("http://docs.openstack.org/compute/" - "ext/os-networks/api/v1.1") - updated = "2011-12-23T00:00:00Z" - - def get_resources(self): - member_actions = {'action': 'POST'} - collection_actions = {'add': 'POST'} - res = extensions.ResourceExtension( - 'os-networks', - NetworkController(ext_mgr=self.ext_mgr), - member_actions=member_actions, - collection_actions=collection_actions) - return [res] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/os_tenant_networks.py b/nova/api/openstack/compute/legacy_v2/contrib/os_tenant_networks.py deleted file mode 100644 index 86925f85c31..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/os_tenant_networks.py +++ /dev/null @@ -1,232 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# 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 netaddr -import netaddr.core as netexc -from oslo_config import cfg -from oslo_log import log as logging -import six -import webob -from webob import exc - -from nova.api.openstack import extensions -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova.i18n import _LE -import nova.network -from nova import quota - - -CONF = cfg.CONF - -os_network_opts = [ - cfg.BoolOpt("enable_network_quota", - default=False, - help='Enables or disables quota checking for tenant ' - 'networks'), - cfg.StrOpt('use_neutron_default_nets', - default="False", - help='Control for checking for default networks'), - cfg.StrOpt('neutron_default_tenant_id', - default="default", - help='Default tenant id when creating neutron ' - 'networks'), - cfg.IntOpt('quota_networks', - default=3, - help='Number of private networks allowed per project'), -] -CONF.register_opts(os_network_opts) - -QUOTAS = quota.QUOTAS -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'os-tenant-networks') - - -def network_dict(network): - # NOTE(danms): Here, network should be an object, which could have come - # from neutron and thus be missing most of the attributes. Providing a - # default to get() avoids trying to lazy-load missing attributes. - return {"id": network.get("uuid", None) or network.get("id", None), - "cidr": str(network.get("cidr", None)), - "label": network.get("label", None)} - - -class NetworkController(object): - def __init__(self, network_api=None): - self.network_api = nova.network.API() - self._default_networks = [] - - def _refresh_default_networks(self): - self._default_networks = [] - if CONF.use_neutron_default_nets == "True": - try: - self._default_networks = self._get_default_networks() - except Exception: - LOG.exception(_LE("Failed to get default networks")) - - def _get_default_networks(self): - project_id = CONF.neutron_default_tenant_id - ctx = nova_context.RequestContext(user_id=None, - project_id=project_id) - networks = {} - for n in self.network_api.get_all(ctx): - networks[n['id']] = n['label'] - return [{'id': k, 'label': v} for k, v in six.iteritems(networks)] - - def index(self, req): - context = req.environ['nova.context'] - authorize(context) - networks = list(self.network_api.get_all(context)) - if not self._default_networks: - self._refresh_default_networks() - networks.extend(self._default_networks) - return {'networks': [network_dict(n) for n in networks]} - - def show(self, req, id): - context = req.environ['nova.context'] - authorize(context) - - try: - network = self.network_api.get(context, id) - except exception.NetworkNotFound: - msg = _("Network not found") - raise exc.HTTPNotFound(explanation=msg) - return {'network': network_dict(network)} - - def delete(self, req, id): - context = req.environ['nova.context'] - authorize(context) - reservation = None - try: - if CONF.enable_network_quota: - reservation = QUOTAS.reserve(context, networks=-1) - except Exception: - reservation = None - LOG.exception(_LE("Failed to update usages deallocating " - "network.")) - - def _rollback_quota(reservation): - if CONF.enable_network_quota and reservation: - QUOTAS.rollback(context, reservation) - - try: - self.network_api.disassociate(context, id) - self.network_api.delete(context, id) - except exception.PolicyNotAuthorized as e: - _rollback_quota(reservation) - raise exc.HTTPForbidden(explanation=six.text_type(e)) - except exception.NetworkInUse as e: - _rollback_quota(reservation) - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.NetworkNotFound: - _rollback_quota(reservation) - msg = _("Network not found") - raise exc.HTTPNotFound(explanation=msg) - - if CONF.enable_network_quota and reservation: - QUOTAS.commit(context, reservation) - response = webob.Response(status_int=202) - - return response - - def create(self, req, body): - if not body: - raise exc.HTTPUnprocessableEntity() - - context = req.environ["nova.context"] - authorize(context) - - network = body["network"] - keys = ["cidr", "cidr_v6", "ipam", "vlan_start", "network_size", - "num_networks"] - kwargs = {k: network.get(k) for k in keys} - - if not network.get("label"): - msg = _("Network label is required") - raise exc.HTTPBadRequest(explanation=msg) - label = network["label"] - - if not (kwargs["cidr"] or kwargs["cidr_v6"]): - msg = _("No CIDR requested") - raise exc.HTTPBadRequest(explanation=msg) - if kwargs["cidr"]: - try: - net = netaddr.IPNetwork(kwargs["cidr"]) - if net.size < 4: - msg = _("Requested network does not contain " - "enough (2+) usable hosts") - raise exc.HTTPBadRequest(explanation=msg) - except netexc.AddrFormatError: - msg = _("CIDR is malformed.") - raise exc.HTTPBadRequest(explanation=msg) - except netexc.AddrConversionError: - msg = _("Address could not be converted.") - raise exc.HTTPBadRequest(explanation=msg) - - networks = [] - try: - if CONF.enable_network_quota: - reservation = QUOTAS.reserve(context, networks=1) - except exception.OverQuota: - msg = _("Quota exceeded, too many networks.") - raise exc.HTTPBadRequest(explanation=msg) - - kwargs['project_id'] = context.project_id - - try: - networks = self.network_api.create(context, - label=label, **kwargs) - if CONF.enable_network_quota: - QUOTAS.commit(context, reservation) - except exception.PolicyNotAuthorized as e: - raise exc.HTTPForbidden(explanation=six.text_type(e)) - except exception.CidrConflict as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except Exception: - if CONF.enable_network_quota: - QUOTAS.rollback(context, reservation) - msg = _("Create networks failed") - LOG.exception(msg, extra=network) - raise exc.HTTPServiceUnavailable(explanation=msg) - return {"network": network_dict(networks[0])} - - -class Os_tenant_networks(extensions.ExtensionDescriptor): - """Tenant-based Network Management Extension.""" - - name = "OSTenantNetworks" - alias = "os-tenant-networks" - namespace = ("http://docs.openstack.org/compute/" - "ext/os-tenant-networks/api/v2") - updated = "2012-03-07T14:46:43Z" - - def get_resources(self): - ext = extensions.ResourceExtension('os-tenant-networks', - NetworkController()) - return [ext] - - -def _sync_networks(context, project_id, session): - ctx = nova_context.RequestContext(user_id=None, project_id=project_id) - ctx = ctx.elevated() - networks = nova.network.api.API().get_all(ctx) - return dict(networks=len(networks)) - - -if CONF.enable_network_quota: - QUOTAS.register_resource(quota.ReservableResource('networks', - _sync_networks, - 'quota_networks')) diff --git a/nova/api/openstack/compute/legacy_v2/contrib/preserve_ephemeral_rebuild.py b/nova/api/openstack/compute/legacy_v2/contrib/preserve_ephemeral_rebuild.py deleted file mode 100644 index bc75d687bfe..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/preserve_ephemeral_rebuild.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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 nova.api.openstack import extensions - - -class Preserve_ephemeral_rebuild(extensions.ExtensionDescriptor): - """Allow preservation of the ephemeral partition on rebuild.""" - - name = "PreserveEphemeralOnRebuild" - alias = "os-preserve-ephemeral-rebuild" - namespace = ("http://docs.openstack.org/compute/ext/" - "preserve_ephemeral_rebuild/api/v2") - updated = "2013-12-17T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/quota_classes.py b/nova/api/openstack/compute/legacy_v2/contrib/quota_classes.py deleted file mode 100644 index 0df81ef9f13..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/quota_classes.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# 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 webob - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -import nova.context -from nova import db -from nova import exception -from nova.i18n import _ -from nova import quota -from nova import utils - - -QUOTAS = quota.QUOTAS -# Quotas that are only enabled by specific extensions -EXTENDED_QUOTAS = {'server_groups': 'os-server-group-quotas', - 'server_group_members': 'os-server-group-quotas'} - - -authorize = extensions.extension_authorizer('compute', 'quota_classes') - - -class QuotaClassSetsController(wsgi.Controller): - - supported_quotas = [] - - def __init__(self, ext_mgr): - self.ext_mgr = ext_mgr - self.supported_quotas = QUOTAS.resources - for resource, extension in EXTENDED_QUOTAS.items(): - if not self.ext_mgr.is_loaded(extension): - self.supported_quotas.remove(resource) - - def _format_quota_set(self, quota_class, quota_set): - """Convert the quota object to a result dict.""" - - if quota_class: - result = dict(id=str(quota_class)) - else: - result = {} - - for resource in self.supported_quotas: - if resource in quota_set: - result[resource] = quota_set[resource] - - return dict(quota_class_set=result) - - def show(self, req, id): - context = req.environ['nova.context'] - authorize(context) - try: - nova.context.authorize_quota_class_context(context, id) - values = QUOTAS.get_class_quotas(context, id) - return self._format_quota_set(id, values) - except exception.Forbidden: - raise webob.exc.HTTPForbidden() - - def update(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - try: - utils.check_string_length(id, 'quota_class_name', - min_length=1, max_length=255) - except exception.InvalidInput as e: - raise webob.exc.HTTPBadRequest( - explanation=e.format_message()) - - quota_class = id - bad_keys = [] - - if not self.is_valid_body(body, 'quota_class_set'): - msg = _("quota_class_set not specified") - raise webob.exc.HTTPBadRequest(explanation=msg) - quota_class_set = body['quota_class_set'] - for key in quota_class_set.keys(): - if key not in self.supported_quotas: - bad_keys.append(key) - continue - try: - body['quota_class_set'][key] = utils.validate_integer( - body['quota_class_set'][key], key, max_value=db.MAX_INT) - except exception.InvalidInput as e: - raise webob.exc.HTTPBadRequest( - explanation=e.format_message()) - - if bad_keys: - msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys) - raise webob.exc.HTTPBadRequest(explanation=msg) - - try: - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova.context.require_admin_context(context) - except exception.AdminRequired: - raise webob.exc.HTTPForbidden() - - for key, value in quota_class_set.items(): - try: - db.quota_class_update(context, quota_class, key, value) - except exception.QuotaClassNotFound: - db.quota_class_create(context, quota_class, key, value) - - values = QUOTAS.get_class_quotas(context, quota_class) - return self._format_quota_set(None, values) - - -class Quota_classes(extensions.ExtensionDescriptor): - """Quota classes management support.""" - - name = "QuotaClasses" - alias = "os-quota-class-sets" - namespace = ("http://docs.openstack.org/compute/ext/" - "quota-classes-sets/api/v1.1") - updated = "2012-03-12T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-quota-class-sets', - QuotaClassSetsController(self.ext_mgr)) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/quotas.py b/nova/api/openstack/compute/legacy_v2/contrib/quotas.py deleted file mode 100644 index 5672258854c..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/quotas.py +++ /dev/null @@ -1,272 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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. - -from oslo_utils import strutils -import six.moves.urllib.parse as urlparse -import webob - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -import nova.context -from nova import db -from nova import exception -from nova.i18n import _ -from nova import objects -from nova import quota -from nova import utils - - -QUOTAS = quota.QUOTAS -NON_QUOTA_KEYS = ['tenant_id', 'id', 'force'] - -# Quotas that are only enabled by specific extensions -EXTENDED_QUOTAS = {'server_groups': 'os-server-group-quotas', - 'server_group_members': 'os-server-group-quotas'} - -authorize_update = extensions.extension_authorizer('compute', 'quotas:update') -authorize_show = extensions.extension_authorizer('compute', 'quotas:show') -authorize_delete = extensions.extension_authorizer('compute', 'quotas:delete') - - -class QuotaSetsController(wsgi.Controller): - - supported_quotas = [] - - def __init__(self, ext_mgr): - self.ext_mgr = ext_mgr - self.supported_quotas = QUOTAS.resources - for resource, extension in EXTENDED_QUOTAS.items(): - if not self.ext_mgr.is_loaded(extension): - self.supported_quotas.remove(resource) - - def _format_quota_set(self, project_id, quota_set): - """Convert the quota object to a result dict.""" - - if project_id: - result = dict(id=str(project_id)) - else: - result = {} - - for resource in self.supported_quotas: - if resource in quota_set: - result[resource] = quota_set[resource] - - return dict(quota_set=result) - - def _validate_quota_limit(self, resource, limit, minimum, maximum): - # NOTE: -1 is a flag value for unlimited, maximum value is limited - # by SQL standard integer type `INT` which is `0x7FFFFFFF`, it's a - # general value for SQL, using a hardcoded value here is not a - # `nice` way, but it seems like the only way for now: - # http://dev.mysql.com/doc/refman/5.0/en/integer-types.html - # http://www.postgresql.org/docs/9.1/static/datatype-numeric.html - if limit < -1 or limit > db.MAX_INT: - msg = (_("Quota limit %(limit)s for %(resource)s " - "must be in the range of -1 and %(max)s.") % - {'limit': limit, 'resource': resource, 'max': db.MAX_INT}) - raise webob.exc.HTTPBadRequest(explanation=msg) - - def conv_inf(value): - return float("inf") if value == -1 else value - - if conv_inf(limit) < conv_inf(minimum): - msg = (_("Quota limit %(limit)s for %(resource)s must " - "be greater than or equal to already used and " - "reserved %(minimum)s.") % - {'limit': limit, 'resource': resource, 'minimum': minimum}) - raise webob.exc.HTTPBadRequest(explanation=msg) - if conv_inf(limit) > conv_inf(maximum): - msg = (_("Quota limit %(limit)s for %(resource)s must be " - "less than or equal to %(maximum)s.") % - {'limit': limit, 'resource': resource, 'maximum': maximum}) - raise webob.exc.HTTPBadRequest(explanation=msg) - - def _get_quotas(self, context, id, user_id=None, usages=False): - if user_id: - values = QUOTAS.get_user_quotas(context, id, user_id, - usages=usages) - else: - values = QUOTAS.get_project_quotas(context, id, usages=usages) - - if usages: - return values - else: - return {k: v['limit'] for k, v in values.items()} - - def show(self, req, id): - context = req.environ['nova.context'] - authorize_show(context) - params = urlparse.parse_qs(req.environ.get('QUERY_STRING', '')) - user_id = None - if self.ext_mgr.is_loaded('os-user-quotas'): - user_id = params.get('user_id', [None])[0] - try: - nova.context.authorize_project_context(context, id) - return self._format_quota_set(id, - self._get_quotas(context, id, user_id=user_id)) - except exception.Forbidden: - raise webob.exc.HTTPForbidden() - - def update(self, req, id, body): - context = req.environ['nova.context'] - authorize_update(context) - try: - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 because - # this version has to be stable even if it means that only admins - # can call this method while the policy could be changed. - nova.context.require_admin_context(context) - except exception.AdminRequired: - raise webob.exc.HTTPForbidden() - - project_id = id - - bad_keys = [] - - # By default, we can force update the quota if the extended - # is not loaded - force_update = True - extended_loaded = False - if self.ext_mgr.is_loaded('os-extended-quotas'): - # force optional has been enabled, the default value of - # force_update need to be changed to False - extended_loaded = True - force_update = False - - user_id = None - if self.ext_mgr.is_loaded('os-user-quotas'): - # Update user quotas only if the extended is loaded - params = urlparse.parse_qs(req.environ.get('QUERY_STRING', '')) - user_id = params.get('user_id', [None])[0] - - try: - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. - nova.context.authorize_project_context(context, id) - settable_quotas = QUOTAS.get_settable_quotas(context, project_id, - user_id=user_id) - except exception.Forbidden: - raise webob.exc.HTTPForbidden() - - if not self.is_valid_body(body, 'quota_set'): - msg = _("quota_set not specified") - raise webob.exc.HTTPBadRequest(explanation=msg) - quota_set = body['quota_set'] - - # NOTE(dims): Pass #1 - In this loop for quota_set.items(), we figure - # out if we have bad keys or if we need to forcibly set quotas or - # if some of the values for the quotas can be converted to integers. - for key, value in quota_set.items(): - if (key not in self.supported_quotas - and key not in NON_QUOTA_KEYS): - bad_keys.append(key) - continue - if key == 'force' and extended_loaded: - # only check the force optional when the extended has - # been loaded - force_update = strutils.bool_from_string(value) - elif key not in NON_QUOTA_KEYS and value: - try: - utils.validate_integer(value, key) - except exception.InvalidInput as e: - raise webob.exc.HTTPBadRequest( - explanation=e.format_message()) - - if bad_keys: - msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys) - raise webob.exc.HTTPBadRequest(explanation=msg) - - # NOTE(dims): Pass #2 - In this loop for quota_set.items(), based on - # force_update flag we validate the quota limit. A loop just for - # the validation of min/max values ensure that we can bail out if - # any of the items in the set is bad. - valid_quotas = {} - for key, value in quota_set.items(): - if key in NON_QUOTA_KEYS or (not value and value != 0): - continue - # validate whether already used and reserved exceeds the new - # quota, this check will be ignored if admin want to force - # update - value = int(value) - if not force_update: - minimum = settable_quotas[key]['minimum'] - maximum = settable_quotas[key]['maximum'] - self._validate_quota_limit(key, value, minimum, maximum) - valid_quotas[key] = value - - # NOTE(dims): Pass #3 - At this point we know that all the keys and - # values are valid and we can iterate and update them all in one - # shot without having to worry about rolling back etc as we have done - # the validation up front in the 2 loops above. - for key, value in valid_quotas.items(): - try: - objects.Quotas.create_limit(context, project_id, - key, value, user_id=user_id) - except exception.QuotaExists: - objects.Quotas.update_limit(context, project_id, - key, value, user_id=user_id) - values = self._get_quotas(context, id, user_id=user_id) - return self._format_quota_set(None, values) - - def defaults(self, req, id): - context = req.environ['nova.context'] - authorize_show(context) - values = QUOTAS.get_defaults(context) - return self._format_quota_set(id, values) - - def delete(self, req, id): - if self.ext_mgr.is_loaded('os-extended-quotas'): - context = req.environ['nova.context'] - authorize_delete(context) - params = urlparse.parse_qs(req.environ.get('QUERY_STRING', '')) - user_id = params.get('user_id', [None])[0] - if user_id and not self.ext_mgr.is_loaded('os-user-quotas'): - raise webob.exc.HTTPNotFound() - try: - nova.context.authorize_project_context(context, id) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks. This has to be left only for API v2.0 - # because this version has to be stable even if it means that - # only admins can call this method while the policy could be - # changed. - nova.context.require_admin_context(context) - if user_id: - QUOTAS.destroy_all_by_project_and_user(context, - id, user_id) - else: - QUOTAS.destroy_all_by_project(context, id) - return webob.Response(status_int=202) - except exception.Forbidden: - raise webob.exc.HTTPForbidden() - raise webob.exc.HTTPNotFound() - - -class Quotas(extensions.ExtensionDescriptor): - """Quotas management support.""" - - name = "Quotas" - alias = "os-quota-sets" - namespace = "http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1" - updated = "2011-08-08T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-quota-sets', - QuotaSetsController(self.ext_mgr), - member_actions={'defaults': 'GET'}) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/rescue.py b/nova/api/openstack/compute/legacy_v2/contrib/rescue.py deleted file mode 100644 index 1b4e91c0cd9..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/rescue.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# 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. - -"""The rescue mode extension.""" - -import webob -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions as exts -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova import utils - - -authorize = exts.extension_authorizer('compute', 'rescue') - - -class RescueController(wsgi.Controller): - def __init__(self, ext_mgr, *args, **kwargs): - super(RescueController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - self.ext_mgr = ext_mgr - - @wsgi.action('rescue') - def _rescue(self, req, id, body): - """Rescue an instance.""" - context = req.environ["nova.context"] - authorize(context) - - if body['rescue'] and 'adminPass' in body['rescue']: - password = body['rescue']['adminPass'] - else: - password = utils.generate_password() - - instance = common.get_instance(self.compute_api, context, id) - try: - rescue_image_ref = None - if self.ext_mgr.is_loaded("os-extended-rescue-with-image"): - if body['rescue'] and 'rescue_image_ref' in body['rescue']: - rescue_image_ref = body['rescue']['rescue_image_ref'] - self.compute_api.rescue(context, instance, - rescue_password=password, rescue_image_ref=rescue_image_ref) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'rescue', id) - except exception.InvalidVolume as volume_error: - raise exc.HTTPConflict(explanation=volume_error.format_message()) - except exception.InstanceNotRescuable as non_rescuable: - raise exc.HTTPBadRequest( - explanation=non_rescuable.format_message()) - - return {'adminPass': password} - - @wsgi.action('unrescue') - def _unrescue(self, req, id, body): - """Unrescue an instance.""" - context = req.environ["nova.context"] - authorize(context) - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.unrescue(context, instance) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'unrescue', - id) - - return webob.Response(status_int=202) - - -class Rescue(exts.ExtensionDescriptor): - """Instance rescue mode.""" - - name = "Rescue" - alias = "os-rescue" - namespace = "http://docs.openstack.org/compute/ext/rescue/api/v1.1" - updated = "2011-08-18T00:00:00Z" - - def get_controller_extensions(self): - controller = RescueController(self.ext_mgr) - extension = exts.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/scheduler_hints.py b/nova/api/openstack/compute/legacy_v2/contrib/scheduler_hints.py deleted file mode 100644 index c1d69413a41..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/scheduler_hints.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# 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 webob.exc - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova.i18n import _ - - -class SchedulerHintsController(wsgi.Controller): - - @staticmethod - def _extract_scheduler_hints(body): - hints = {} - - attr = '%s:scheduler_hints' % Scheduler_hints.alias - try: - if 'os:scheduler_hints' in body: - # NOTE(vish): This is for legacy support - hints.update(body['os:scheduler_hints']) - elif attr in body: - hints.update(body[attr]) - # Fail if non-dict provided - except ValueError: - msg = _("Malformed scheduler_hints attribute") - raise webob.exc.HTTPBadRequest(explanation=msg) - - return hints - - @wsgi.extends - def create(self, req, body): - hints = self._extract_scheduler_hints(body) - - if 'server' in body: - body['server']['scheduler_hints'] = hints - yield - - -class Scheduler_hints(extensions.ExtensionDescriptor): - """Pass arbitrary key/value pairs to the scheduler.""" - - name = "SchedulerHints" - alias = "OS-SCH-HNT" - namespace = ("http://docs.openstack.org/compute/ext/" - "scheduler-hints/api/v2") - updated = "2011-07-19T00:00:00Z" - - def get_controller_extensions(self): - controller = SchedulerHintsController() - ext = extensions.ControllerExtension(self, 'servers', controller) - return [ext] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/security_group_default_rules.py b/nova/api/openstack/compute/legacy_v2/contrib/security_group_default_rules.py deleted file mode 100644 index 486ddabb6c6..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/security_group_default_rules.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright 2013 Metacloud Inc. -# -# 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 six -import webob -from webob import exc - -from nova.api.openstack.compute.legacy_v2.contrib import security_groups as sg -from nova.api.openstack import extensions -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova.network.security_group import openstack_driver - - -authorize = extensions.extension_authorizer('compute', - 'security_group_default_rules') - - -class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase): - - def __init__(self): - self.security_group_api = ( - openstack_driver.get_openstack_security_group_driver()) - - def create(self, req, body): - context = sg._authorize_context(req) - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. - nova_context.require_admin_context(context) - - sg_rule = self._from_body(body, 'security_group_default_rule') - - try: - values = self._rule_args_to_dict(to_port=sg_rule.get('to_port'), - from_port=sg_rule.get('from_port'), - ip_protocol=sg_rule.get('ip_protocol'), - cidr=sg_rule.get('cidr')) - except Exception as exp: - raise exc.HTTPBadRequest(explanation=six.text_type(exp)) - - if values is None: - msg = _('Not enough parameters to build a valid rule.') - raise exc.HTTPBadRequest(explanation=msg) - - if self.security_group_api.default_rule_exists(context, values): - msg = _('This default rule already exists.') - raise exc.HTTPConflict(explanation=msg) - security_group_rule = self.security_group_api.add_default_rules( - context, [values])[0] - fmt_rule = self._format_security_group_default_rule( - security_group_rule) - return {'security_group_default_rule': fmt_rule} - - def _rule_args_to_dict(self, to_port=None, from_port=None, - ip_protocol=None, cidr=None): - cidr = self.security_group_api.parse_cidr(cidr) - return self.security_group_api.new_cidr_ingress_rule( - cidr, ip_protocol, from_port, to_port) - - def show(self, req, id): - context = sg._authorize_context(req) - authorize(context) - - id = self.security_group_api.validate_id(id) - - try: - rule = self.security_group_api.get_default_rule(context, id) - except exception.SecurityGroupDefaultRuleNotFound: - msg = _("security group default rule not found") - raise exc.HTTPNotFound(explanation=msg) - - fmt_rule = self._format_security_group_default_rule(rule) - return {"security_group_default_rule": fmt_rule} - - def delete(self, req, id): - context = sg._authorize_context(req) - authorize(context) - # NOTE(shaohe-feng): back-compatible with db layer hard-code - # admin permission checks. - nova_context.require_admin_context(context) - - try: - id = self.security_group_api.validate_id(id) - except exception.Invalid as ex: - raise exc.HTTPBadRequest(explanation=ex.format_message()) - - try: - rule = self.security_group_api.get_default_rule(context, id) - self.security_group_api.remove_default_rules(context, [rule['id']]) - except exception.SecurityGroupDefaultRuleNotFound as ex: - raise exc.HTTPNotFound(explanation=ex.format_message()) - - return webob.Response(status_int=204) - - def index(self, req): - - context = sg._authorize_context(req) - authorize(context) - - ret = {'security_group_default_rules': []} - try: - for rule in self.security_group_api.get_all_default_rules(context): - rule_fmt = self._format_security_group_default_rule(rule) - ret['security_group_default_rules'].append(rule_fmt) - except exception.SecurityGroupDefaultRuleNotFound as ex: - raise exc.HTTPNotFound(explanation=ex.format_message()) - return ret - - def _format_security_group_default_rule(self, rule): - sg_rule = {} - sg_rule['id'] = rule['id'] - sg_rule['ip_protocol'] = rule['protocol'] - sg_rule['from_port'] = rule['from_port'] - sg_rule['to_port'] = rule['to_port'] - sg_rule['ip_range'] = {} - sg_rule['ip_range'] = {'cidr': rule['cidr']} - return sg_rule - - -class Security_group_default_rules(extensions.ExtensionDescriptor): - """Default rules for security group support.""" - name = "SecurityGroupDefaultRules" - alias = "os-security-group-default-rules" - namespace = ("http://docs.openstack.org/compute/ext/" - "securitygroupdefaultrules/api/v1.1") - updated = "2013-02-05T00:00:00Z" - - def get_resources(self): - resources = [ - extensions.ResourceExtension('os-security-group-default-rules', - SecurityGroupDefaultRulesController(), - collection_actions={'create': 'POST', - 'delete': 'DELETE', - 'index': 'GET'}, - member_actions={'show': 'GET'})] - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/security_groups.py b/nova/api/openstack/compute/legacy_v2/contrib/security_groups.py deleted file mode 100644 index 1c458e127c7..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/security_groups.py +++ /dev/null @@ -1,506 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# Copyright 2012 Justin Santa Barbara -# 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. - -"""The security groups extension.""" - -import contextlib -from xml.dom import minidom - -from oslo_log import log as logging -from oslo_serialization import jsonutils -import six -import webob -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova.i18n import _ -from nova.network.security_group import openstack_driver -from nova.virt import netutils - - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'security_groups') -softauth = extensions.soft_extension_authorizer('compute', 'security_groups') - - -def _authorize_context(req): - context = req.environ['nova.context'] - authorize(context) - return context - - -@contextlib.contextmanager -def translate_exceptions(): - """Translate nova exceptions to http exceptions.""" - try: - yield - except exception.Invalid as exp: - msg = exp.format_message() - raise exc.HTTPBadRequest(explanation=msg) - except exception.SecurityGroupNotFound as exp: - msg = exp.format_message() - raise exc.HTTPNotFound(explanation=msg) - except exception.InstanceNotFound as exp: - msg = exp.format_message() - raise exc.HTTPNotFound(explanation=msg) - except exception.SecurityGroupLimitExceeded as exp: - msg = exp.format_message() - raise exc.HTTPForbidden(explanation=msg) - except exception.NoUniqueMatch as exp: - msg = exp.format_message() - raise exc.HTTPConflict(explanation=msg) - - -class SecurityGroupControllerBase(object): - """Base class for Security Group controllers.""" - - def __init__(self): - self.security_group_api = ( - openstack_driver.get_openstack_security_group_driver()) - self.compute_api = compute.API( - security_group_api=self.security_group_api) - - def _format_security_group_rule(self, context, rule, group_rule_data=None): - """Return a security group rule in desired API response format. - - If group_rule_data is passed in that is used rather than querying - for it. - """ - sg_rule = {} - sg_rule['id'] = rule['id'] - sg_rule['parent_group_id'] = rule['parent_group_id'] - sg_rule['ip_protocol'] = rule['protocol'] - sg_rule['from_port'] = rule['from_port'] - sg_rule['to_port'] = rule['to_port'] - sg_rule['group'] = {} - sg_rule['ip_range'] = {} - if rule['group_id']: - with translate_exceptions(): - try: - source_group = self.security_group_api.get( - context, id=rule['group_id']) - except exception.SecurityGroupNotFound: - # NOTE(arosen): There is a possible race condition that can - # occur here if two api calls occur concurrently: one that - # lists the security groups and another one that deletes a - # security group rule that has a group_id before the - # group_id is fetched. To handle this if - # SecurityGroupNotFound is raised we return None instead - # of the rule and the caller should ignore the rule. - LOG.debug("Security Group ID %s does not exist", - rule['group_id']) - return - sg_rule['group'] = {'name': source_group.get('name'), - 'tenant_id': source_group.get('project_id')} - elif group_rule_data: - sg_rule['group'] = group_rule_data - else: - sg_rule['ip_range'] = {'cidr': rule['cidr']} - return sg_rule - - def _format_security_group(self, context, group): - security_group = {} - security_group['id'] = group['id'] - security_group['description'] = group['description'] - security_group['name'] = group['name'] - security_group['tenant_id'] = group['project_id'] - security_group['rules'] = [] - for rule in group['rules']: - formatted_rule = self._format_security_group_rule(context, rule) - if formatted_rule: - security_group['rules'] += [formatted_rule] - return security_group - - def _from_body(self, body, key): - if not body: - raise exc.HTTPBadRequest( - explanation=_("The request body can't be empty")) - value = body.get(key, None) - if value is None: - raise exc.HTTPBadRequest( - explanation=_("Missing parameter %s") % key) - return value - - -class SecurityGroupController(SecurityGroupControllerBase): - """The Security group API controller for the OpenStack API.""" - - def show(self, req, id): - """Return data about the given security group.""" - context = _authorize_context(req) - - with translate_exceptions(): - id = self.security_group_api.validate_id(id) - security_group = self.security_group_api.get(context, None, id, - map_exception=True) - - return {'security_group': self._format_security_group(context, - security_group)} - - def delete(self, req, id): - """Delete a security group.""" - context = _authorize_context(req) - - with translate_exceptions(): - id = self.security_group_api.validate_id(id) - security_group = self.security_group_api.get(context, None, id, - map_exception=True) - self.security_group_api.destroy(context, security_group) - - return webob.Response(status_int=202) - - def index(self, req): - """Returns a list of security groups.""" - context = _authorize_context(req) - - search_opts = {} - search_opts.update(req.GET) - - with translate_exceptions(): - project_id = context.project_id - raw_groups = self.security_group_api.list(context, - project=project_id, - search_opts=search_opts) - - limited_list = common.limited(raw_groups, req) - result = [self._format_security_group(context, group) - for group in limited_list] - - return {'security_groups': - list(sorted(result, - key=lambda k: (k['tenant_id'], k['name'])))} - - def create(self, req, body): - """Creates a new security group.""" - context = _authorize_context(req) - - security_group = self._from_body(body, 'security_group') - - group_name = security_group.get('name', None) - group_description = security_group.get('description', None) - - with translate_exceptions(): - self.security_group_api.validate_property(group_name, 'name', None) - self.security_group_api.validate_property(group_description, - 'description', None) - group_ref = self.security_group_api.create_security_group( - context, group_name, group_description) - - return {'security_group': self._format_security_group(context, - group_ref)} - - def update(self, req, id, body): - """Update a security group.""" - context = _authorize_context(req) - - with translate_exceptions(): - id = self.security_group_api.validate_id(id) - security_group = self.security_group_api.get(context, None, id, - map_exception=True) - - security_group_data = self._from_body(body, 'security_group') - group_name = security_group_data.get('name', None) - group_description = security_group_data.get('description', None) - - with translate_exceptions(): - self.security_group_api.validate_property(group_name, 'name', None) - self.security_group_api.validate_property(group_description, - 'description', None) - group_ref = self.security_group_api.update_security_group( - context, security_group, group_name, group_description) - - return {'security_group': self._format_security_group(context, - group_ref)} - - -class SecurityGroupRulesController(SecurityGroupControllerBase): - - def create(self, req, body): - context = _authorize_context(req) - - sg_rule = self._from_body(body, 'security_group_rule') - - with translate_exceptions(): - parent_group_id = self.security_group_api.validate_id( - sg_rule.get('parent_group_id', None)) - security_group = self.security_group_api.get(context, None, - parent_group_id, - map_exception=True) - try: - new_rule = self._rule_args_to_dict(context, - to_port=sg_rule.get('to_port'), - from_port=sg_rule.get('from_port'), - ip_protocol=sg_rule.get('ip_protocol'), - cidr=sg_rule.get('cidr'), - group_id=sg_rule.get('group_id')) - except exception.SecurityGroupNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except Exception as exp: - raise exc.HTTPBadRequest(explanation=six.text_type(exp)) - - if new_rule is None: - msg = _("Not enough parameters to build a valid rule.") - raise exc.HTTPBadRequest(explanation=msg) - - new_rule['parent_group_id'] = security_group['id'] - - if 'cidr' in new_rule: - net, prefixlen = netutils.get_net_and_prefixlen(new_rule['cidr']) - if net not in ('0.0.0.0', '::') and prefixlen == '0': - msg = _("Bad prefix for network in cidr %s") % new_rule['cidr'] - raise exc.HTTPBadRequest(explanation=msg) - - group_rule_data = None - with translate_exceptions(): - if sg_rule.get('group_id'): - source_group = self.security_group_api.get( - context, id=sg_rule['group_id']) - group_rule_data = {'name': source_group.get('name'), - 'tenant_id': source_group.get('project_id')} - - security_group_rule = ( - self.security_group_api.create_security_group_rule( - context, security_group, new_rule)) - - formatted_rule = self._format_security_group_rule(context, - security_group_rule, - group_rule_data) - return {"security_group_rule": formatted_rule} - - def _rule_args_to_dict(self, context, to_port=None, from_port=None, - ip_protocol=None, cidr=None, group_id=None): - - if group_id is not None: - group_id = self.security_group_api.validate_id(group_id) - - # check if groupId exists - self.security_group_api.get(context, id=group_id) - return self.security_group_api.new_group_ingress_rule( - group_id, ip_protocol, from_port, to_port) - else: - cidr = self.security_group_api.parse_cidr(cidr) - return self.security_group_api.new_cidr_ingress_rule( - cidr, ip_protocol, from_port, to_port) - - def delete(self, req, id): - context = _authorize_context(req) - - with translate_exceptions(): - id = self.security_group_api.validate_id(id) - rule = self.security_group_api.get_rule(context, id) - group_id = rule['parent_group_id'] - security_group = self.security_group_api.get(context, None, - group_id, - map_exception=True) - self.security_group_api.remove_rules(context, security_group, - [rule['id']]) - - return webob.Response(status_int=202) - - -class ServerSecurityGroupController(SecurityGroupControllerBase): - - def index(self, req, server_id): - """Returns a list of security groups for the given instance.""" - context = _authorize_context(req) - - self.security_group_api.ensure_default(context) - - with translate_exceptions(): - instance = common.get_instance(self.compute_api, context, - server_id) - groups = self.security_group_api.get_instance_security_groups( - context, instance, True) - - result = [self._format_security_group(context, group) - for group in groups] - - return {'security_groups': - list(sorted(result, - key=lambda k: (k['tenant_id'], k['name'])))} - - -class SecurityGroupActionController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(SecurityGroupActionController, self).__init__(*args, **kwargs) - self.security_group_api = ( - openstack_driver.get_openstack_security_group_driver()) - self.compute_api = compute.API( - security_group_api=self.security_group_api) - - def _parse(self, body, action): - try: - body = body[action] - group_name = body['name'] - except TypeError: - msg = _("Missing parameter dict") - raise webob.exc.HTTPBadRequest(explanation=msg) - except KeyError: - msg = _("Security group not specified") - raise webob.exc.HTTPBadRequest(explanation=msg) - - if not group_name or group_name.strip() == '': - msg = _("Security group name cannot be empty") - raise webob.exc.HTTPBadRequest(explanation=msg) - - return group_name - - def _invoke(self, method, context, id, group_name): - with translate_exceptions(): - instance = common.get_instance(self.compute_api, context, id) - method(context, instance, group_name) - - return webob.Response(status_int=202) - - @wsgi.action('addSecurityGroup') - def _addSecurityGroup(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - - group_name = self._parse(body, 'addSecurityGroup') - - return self._invoke(self.security_group_api.add_to_instance, - context, id, group_name) - - @wsgi.action('removeSecurityGroup') - def _removeSecurityGroup(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - - group_name = self._parse(body, 'removeSecurityGroup') - - return self._invoke(self.security_group_api.remove_from_instance, - context, id, group_name) - - -class SecurityGroupsOutputController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(SecurityGroupsOutputController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - self.security_group_api = ( - openstack_driver.get_openstack_security_group_driver()) - - def _extend_servers(self, req, servers): - # TODO(arosen) this function should be refactored to reduce duplicate - # code and use get_instance_security_groups instead of get_db_instance. - if not len(servers): - return - key = "security_groups" - context = _authorize_context(req) - if not openstack_driver.is_neutron_security_groups(): - for server in servers: - instance = req.get_db_instance(server['id']) - groups = instance.get(key) - if groups: - server[key] = [{"name": group["name"]} for group in groups] - else: - # If method is a POST we get the security groups intended for an - # instance from the request. The reason for this is if using - # neutron security groups the requested security groups for the - # instance are not in the db and have not been sent to neutron yet. - if req.method != 'POST': - sg_instance_bindings = ( - self.security_group_api - .get_instances_security_groups_bindings(context, - servers)) - for server in servers: - groups = sg_instance_bindings.get(server['id']) - if groups: - server[key] = groups - - # In this section of code len(servers) == 1 as you can only POST - # one server in an API request. - else: - try: - # try converting to json - req_obj = jsonutils.loads(req.body) - # Add security group to server, if no security group was in - # request add default since that is the group it is part of - servers[0][key] = req_obj['server'].get( - key, [{'name': 'default'}]) - except ValueError: - root = minidom.parseString(req.body) - sg_root = root.getElementsByTagName(key) - groups = [] - if sg_root: - security_groups = sg_root[0].getElementsByTagName( - 'security_group') - for security_group in security_groups: - groups.append( - {'name': security_group.getAttribute('name')}) - if not groups: - groups = [{'name': 'default'}] - - servers[0][key] = groups - - def _show(self, req, resp_obj): - if not softauth(req.environ['nova.context']): - return - if 'server' in resp_obj.obj: - self._extend_servers(req, [resp_obj.obj['server']]) - - @wsgi.extends - def show(self, req, resp_obj, id): - return self._show(req, resp_obj) - - @wsgi.extends - def create(self, req, resp_obj, body): - return self._show(req, resp_obj) - - @wsgi.extends - def detail(self, req, resp_obj): - if not softauth(req.environ['nova.context']): - return - self._extend_servers(req, list(resp_obj.obj['servers'])) - - -class Security_groups(extensions.ExtensionDescriptor): - """Security group support.""" - name = "SecurityGroups" - alias = "os-security-groups" - namespace = "http://docs.openstack.org/compute/ext/securitygroups/api/v1.1" - updated = "2013-05-28T00:00:00Z" - - def get_controller_extensions(self): - controller = SecurityGroupActionController() - actions = extensions.ControllerExtension(self, 'servers', controller) - controller = SecurityGroupsOutputController() - output = extensions.ControllerExtension(self, 'servers', controller) - return [actions, output] - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-security-groups', - controller=SecurityGroupController()) - - resources.append(res) - - res = extensions.ResourceExtension('os-security-group-rules', - controller=SecurityGroupRulesController()) - resources.append(res) - - res = extensions.ResourceExtension( - 'os-security-groups', - controller=ServerSecurityGroupController(), - parent=dict(member_name='server', collection_name='servers')) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_diagnostics.py b/nova/api/openstack/compute/legacy_v2/contrib/server_diagnostics.py deleted file mode 100644 index 3939d220800..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/server_diagnostics.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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 webob.exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova import compute -from nova import exception -from nova.i18n import _ - - -authorize = extensions.extension_authorizer('compute', 'server_diagnostics') - - -class ServerDiagnosticsController(object): - def __init__(self): - self.compute_api = compute.API() - - def index(self, req, server_id): - context = req.environ["nova.context"] - authorize(context) - - instance = common.get_instance(self.compute_api, context, server_id) - - try: - return self.compute_api.get_diagnostics(context, instance) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'get_diagnostics', server_id) - except NotImplementedError: - msg = _("Unable to get diagnostics, functionality not implemented") - raise webob.exc.HTTPNotImplemented(explanation=msg) - - -class Server_diagnostics(extensions.ExtensionDescriptor): - """Allow Admins to view server diagnostics through server action.""" - - name = "ServerDiagnostics" - alias = "os-server-diagnostics" - namespace = ("http://docs.openstack.org/compute/ext/" - "server-diagnostics/api/v1.1") - updated = "2011-12-21T00:00:00Z" - - def get_resources(self): - parent_def = {'member_name': 'server', 'collection_name': 'servers'} - # NOTE(bcwaldon): This should be prefixed with 'os-' - ext = extensions.ResourceExtension('diagnostics', - ServerDiagnosticsController(), - parent=parent_def) - return [ext] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_external_events.py b/nova/api/openstack/compute/legacy_v2/contrib/server_external_events.py deleted file mode 100644 index 65ba052ad73..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/server_external_events.py +++ /dev/null @@ -1,149 +0,0 @@ -# Copyright 2014 Red Hat, Inc. -# -# 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 oslo_log import log as logging -import webob - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova.i18n import _ -from nova.i18n import _LI -from nova import objects -from nova.objects import external_event as external_event_obj - - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', - 'os-server-external-events') - - -class ServerExternalEventsController(wsgi.Controller): - - def __init__(self): - self.compute_api = compute.API() - super(ServerExternalEventsController, self).__init__() - - def create(self, req, body): - """Creates a new instance event.""" - context = req.environ['nova.context'] - authorize(context, action='create') - - response_events = [] - accepted_events = [] - accepted_instances = set() - instances = {} - result = 200 - - body_events = body.get('events', []) - if not isinstance(body_events, list) or not len(body_events): - raise webob.exc.HTTPBadRequest() - - for _event in body_events: - client_event = dict(_event) - event = objects.InstanceExternalEvent(context) - - status = client_event.get('status', 'completed') - if status not in external_event_obj.EVENT_STATUSES: - raise webob.exc.HTTPBadRequest( - _('Invalid event status `%s\'') % status) - - if client_event.get('name') not in external_event_obj.EVENT_NAMES: - raise webob.exc.HTTPBadRequest( - _('Invalid event name %s') % client_event.get('name')) - - try: - event.instance_uuid = client_event.pop('server_uuid') - event.name = client_event.pop('name') - event.status = client_event.pop('status', 'completed') - event.tag = client_event.pop('tag', None) - except KeyError as missing_key: - msg = _('event entity requires key %(key)s') % missing_key - raise webob.exc.HTTPBadRequest(explanation=msg) - - if client_event: - msg = (_('event entity contains unsupported items: %s') % - ', '.join(client_event.keys())) - raise webob.exc.HTTPBadRequest(explanation=msg) - - instance = instances.get(event.instance_uuid) - if not instance: - try: - instance = objects.Instance.get_by_uuid( - context, event.instance_uuid) - instances[event.instance_uuid] = instance - except exception.InstanceNotFound: - LOG.debug('Dropping event %(name)s:%(tag)s for unknown ' - 'instance %(instance_uuid)s', - {'name': event.name, 'tag': event.tag, - 'instance_uuid': event.instance_uuid}) - _event['status'] = 'failed' - _event['code'] = 404 - result = 207 - - # NOTE: before accepting the event, make sure the instance - # for which the event is sent is assigned to a host; otherwise - # it will not be possible to dispatch the event - if instance: - if instance.host: - accepted_events.append(event) - accepted_instances.add(instance) - LOG.info(_LI('Creating event %(name)s:%(tag)s for ' - 'instance %(instance_uuid)s'), - {'name': event.name, 'tag': event.tag, - 'instance_uuid': event.instance_uuid}) - # NOTE: as the event is processed asynchronously verify - # whether 202 is a more suitable response code than 200 - _event['status'] = 'completed' - _event['code'] = 200 - else: - LOG.debug("Unable to find a host for instance " - "%(instance)s. Dropping event %(event)s", - {'instance': event.instance_uuid, - 'event': event.name}) - _event['status'] = 'failed' - _event['code'] = 422 - result = 207 - - response_events.append(_event) - - if accepted_events: - self.compute_api.external_instance_event( - context, accepted_instances, accepted_events) - else: - msg = _('No instances found for any event') - raise webob.exc.HTTPNotFound(explanation=msg) - - # FIXME(cyeoh): This needs some infrastructure support so that - # we have a general way to do this - robj = wsgi.ResponseObject({'events': response_events}) - robj._code = result - return robj - - -class Server_external_events(extensions.ExtensionDescriptor): - """Server External Event Triggers.""" - - name = "ServerExternalEvents" - alias = "os-server-external-events" - namespace = ("http://docs.openstack.org/compute/ext/" - "server-external-events/api/v2") - updated = "2014-02-18T00:00:00Z" - - def get_resources(self): - resource = extensions.ResourceExtension('os-server-external-events', - ServerExternalEventsController()) - - return [resource] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_group_quotas.py b/nova/api/openstack/compute/legacy_v2/contrib/server_group_quotas.py deleted file mode 100644 index e2001b23ac4..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/server_group_quotas.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import quota - -QUOTAS = quota.QUOTAS - - -class ExtendedLimitsController(wsgi.Controller): - - @wsgi.extends - def index(self, req, resp_obj): - - context = req.environ['nova.context'] - quotas = QUOTAS.get_project_quotas(context, context.project_id, - usages=False) - abs = resp_obj.obj.get('limits', {}).get('absolute', {}) - abs['maxServerGroups'] = quotas.get('server_groups').get('limit') - abs['maxServerGroupMembers'] =\ - quotas.get('server_group_members').get('limit') - - -class Server_group_quotas(extensions.ExtensionDescriptor): - """Adds quota support to server groups.""" - - name = "ServerGroupQuotas" - alias = "os-server-group-quotas" - namespace = ("http://docs.openstack.org/compute/ext/" - "server-group-quotas/api/v2") - updated = "2014-07-25T00:00:00Z" - - def get_controller_extensions(self): - extension_list = [extensions.ControllerExtension(self, - 'limits', - ExtendedLimitsController()), - ] - return extension_list diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_groups.py b/nova/api/openstack/compute/legacy_v2/contrib/server_groups.py deleted file mode 100644 index 88bf573d053..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/server_groups.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) 2014 Cisco Systems, 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. - -"""The Server Group API Extension.""" - -from oslo_log import log as logging -import webob -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova.api.validation import parameter_types -import nova.exception -from nova.i18n import _ -from nova.i18n import _LE -from nova import objects -from nova import utils - -LOG = logging.getLogger(__name__) - - -SUPPORTED_POLICIES = ['anti-affinity', 'affinity'] - -authorize = extensions.extension_authorizer('compute', 'server_groups') - - -def _authorize_context(req): - context = req.environ['nova.context'] - authorize(context) - return context - - -class ServerGroupController(wsgi.Controller): - """The Server group API controller for the OpenStack API.""" - - def __init__(self, ext_mgr): - self.ext_mgr = ext_mgr - - def _format_server_group(self, context, group): - # the id field has its value as the uuid of the server group - # There is no 'uuid' key in server_group seen by clients. - # In addition, clients see policies as a ["policy-name"] list; - # and they see members as a ["server-id"] list. - server_group = {} - server_group['id'] = group.uuid - server_group['name'] = group.name - server_group['policies'] = group.policies or [] - # NOTE(danms): This has been exposed to the user, but never used. - # Since we can't remove it, just make sure it's always empty. - server_group['metadata'] = {} - members = [] - if group.members: - # Display the instances that are not deleted. - filters = {'uuid': group.members, 'deleted': False} - instances = objects.InstanceList.get_by_filters( - context, filters=filters) - members = [instance.uuid for instance in instances] - server_group['members'] = members - return server_group - - def _validate_policies(self, policies): - """Validate the policies. - - Validates that there are no contradicting policies, for example - 'anti-affinity' and 'affinity' in the same group. - Validates that the defined policies are supported. - :param policies: the given policies of the server_group - """ - if ('anti-affinity' in policies and - 'affinity' in policies): - msg = _("Conflicting policies configured!") - raise nova.exception.InvalidInput(reason=msg) - not_supported = [policy for policy in policies - if policy not in SUPPORTED_POLICIES] - if not_supported: - msg = _("Invalid policies: %s") % ', '.join(not_supported) - raise nova.exception.InvalidInput(reason=msg) - - # Note(wingwj): It doesn't make sense to store duplicate policies. - if sorted(set(policies)) != sorted(policies): - msg = _("Duplicate policies configured!") - raise nova.exception.InvalidInput(reason=msg) - - def _validate_input_body(self, body, entity_name): - if not self.is_valid_body(body, entity_name): - msg = _("the body is invalid.") - raise nova.exception.InvalidInput(reason=msg) - - subbody = dict(body[entity_name]) - - expected_fields = ['name', 'policies'] - for field in expected_fields: - value = subbody.pop(field, None) - if not value: - msg = _("'%s' is either missing or empty.") % field - raise nova.exception.InvalidInput(reason=msg) - if field == 'name': - utils.check_string_length(value, field, - min_length=1, max_length=255) - if not parameter_types.valid_name_regex_obj.search(value): - msg = _("Invalid format for name: '%s'") % value - raise nova.exception.InvalidInput(reason=msg) - elif field == 'policies': - if isinstance(value, list): - [utils.check_string_length(v, field, - min_length=1, max_length=255) for v in value] - self._validate_policies(value) - else: - msg = _("'%s' is not a list") % value - raise nova.exception.InvalidInput(reason=msg) - - if subbody: - msg = _("unsupported fields: %s") % subbody.keys() - raise nova.exception.InvalidInput(reason=msg) - - def show(self, req, id): - """Return data about the given server group.""" - context = _authorize_context(req) - try: - sg = objects.InstanceGroup.get_by_uuid(context, id) - except nova.exception.InstanceGroupNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - return {'server_group': self._format_server_group(context, sg)} - - def delete(self, req, id): - """Delete an server group.""" - context = _authorize_context(req) - try: - sg = objects.InstanceGroup.get_by_uuid(context, id) - except nova.exception.InstanceGroupNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - - quotas = None - if self.ext_mgr.is_loaded('os-server-group-quotas'): - quotas = objects.Quotas(context=context) - project_id, user_id = objects.quotas.ids_from_server_group(context, - sg) - try: - # We have to add the quota back to the user that created - # the server group - quotas.reserve(project_id=project_id, - user_id=user_id, server_groups=-1) - except Exception: - quotas = None - LOG.exception(_LE("Failed to update usages deallocating " - "server group")) - - try: - sg.destroy() - except nova.exception.InstanceGroupNotFound as e: - if quotas: - quotas.rollback() - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - - if quotas: - quotas.commit() - - return webob.Response(status_int=204) - - def index(self, req): - """Returns a list of server groups.""" - context = _authorize_context(req) - project_id = context.project_id - if 'all_projects' in req.GET and context.is_admin: - sgs = objects.InstanceGroupList.get_all(context) - else: - sgs = objects.InstanceGroupList.get_by_project_id( - context, project_id) - limited_list = common.limited(sgs.objects, req) - result = [self._format_server_group(context, group) - for group in limited_list] - return {'server_groups': result} - - def create(self, req, body): - """Creates a new server group.""" - context = _authorize_context(req) - - try: - self._validate_input_body(body, 'server_group') - except nova.exception.InvalidInput as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - quotas = None - if self.ext_mgr.is_loaded('os-server-group-quotas'): - quotas = objects.Quotas(context=context) - try: - quotas.reserve(project_id=context.project_id, - user_id=context.user_id, server_groups=1) - except nova.exception.OverQuota: - msg = _("Quota exceeded, too many server groups.") - raise exc.HTTPForbidden(explanation=msg) - - vals = body['server_group'] - sg = objects.InstanceGroup(context) - sg.project_id = context.project_id - sg.user_id = context.user_id - try: - sg.name = vals.get('name') - sg.policies = vals.get('policies') - sg.create() - except ValueError as e: - if quotas: - quotas.rollback() - raise exc.HTTPBadRequest(explanation=e) - - if quotas: - quotas.commit() - - return {'server_group': self._format_server_group(context, sg)} - - -class Server_groups(extensions.ExtensionDescriptor): - """Server group support.""" - name = "ServerGroups" - alias = "os-server-groups" - namespace = ("http://docs.openstack.org/compute/ext/" - "servergroups/api/v2") - updated = "2013-06-20T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension( - 'os-server-groups', - controller=ServerGroupController(self.ext_mgr), - member_actions={"action": "POST", }) - - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_list_multi_status.py b/nova/api/openstack/compute/legacy_v2/contrib/server_list_multi_status.py deleted file mode 100644 index bdcb2f883f4..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/server_list_multi_status.py +++ /dev/null @@ -1,25 +0,0 @@ -# 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 nova.api.openstack import extensions - - -class Server_list_multi_status(extensions.ExtensionDescriptor): - """Allow to specify multiple status values concurrently in the servers - list API.. - """ - - name = "ServerListMultiStatus" - alias = "os-server-list-multi-status" - namespace = ("http://docs.openstack.org/compute/ext/" - "os-server-list-multi-status/api/v2") - updated = "2014-05-11T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_password.py b/nova/api/openstack/compute/legacy_v2/contrib/server_password.py deleted file mode 100644 index 6c633e37834..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/server_password.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2012 Nebula, 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. - -"""The server password extension.""" - -from nova.api.metadata import password -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute - - -authorize = extensions.extension_authorizer('compute', 'server_password') - - -class ServerPasswordController(object): - """The Server Password API controller for the OpenStack API.""" - def __init__(self): - self.compute_api = compute.API() - - def index(self, req, server_id): - context = req.environ['nova.context'] - authorize(context) - instance = common.get_instance(self.compute_api, context, server_id) - - passw = password.extract_password(instance) - return {'password': passw or ''} - - @wsgi.response(204) - def delete(self, req, server_id): - context = req.environ['nova.context'] - authorize(context) - instance = common.get_instance(self.compute_api, context, server_id) - meta = password.convert_password(context, None) - instance.system_metadata.update(meta) - instance.save() - - -class Server_password(extensions.ExtensionDescriptor): - """Server password support.""" - - name = "ServerPassword" - alias = "os-server-password" - namespace = ("http://docs.openstack.org/compute/ext/" - "server-password/api/v2") - updated = "2012-11-29T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension( - 'os-server-password', - controller=ServerPasswordController(), - collection_actions={'delete': 'DELETE'}, - parent=dict(member_name='server', collection_name='servers')) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_sort_keys.py b/nova/api/openstack/compute/legacy_v2/contrib/server_sort_keys.py deleted file mode 100644 index 01ce14b7053..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/server_sort_keys.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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 nova.api.openstack import extensions - - -class Server_sort_keys(extensions.ExtensionDescriptor): - """Add sort keys and directions to the Server GET v2 API.""" - - name = "ServerSortKeys" - alias = "os-server-sort-keys" - namespace = ("http://docs.openstack.org/compute/ext/" - "server_sort_keys/api/v2") - updated = "2014-05-22T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_start_stop.py b/nova/api/openstack/compute/legacy_v2/contrib/server_start_stop.py deleted file mode 100644 index f4606aba2e3..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/server_start_stop.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright 2012 Midokura Japan K.K. -# -# 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 webob - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova.i18n import _ -from nova import objects - - -class ServerStartStopActionController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ServerStartStopActionController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - - def _get_instance(self, context, instance_uuid): - try: - attrs = ['system_metadata', 'metadata'] - return objects.Instance.get_by_uuid(context, instance_uuid, - expected_attrs=attrs) - except exception.NotFound: - msg = _("Instance not found") - raise webob.exc.HTTPNotFound(explanation=msg) - - @wsgi.action('os-start') - def _start_server(self, req, id, body): - """Start an instance.""" - context = req.environ['nova.context'] - instance = self._get_instance(context, id) - extensions.check_compute_policy(context, 'start', instance) - - try: - self.compute_api.start(context, instance) - except (exception.InstanceNotReady, exception.InstanceIsLocked) as e: - raise webob.exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'start', id) - return webob.Response(status_int=202) - - @wsgi.action('os-stop') - def _stop_server(self, req, id, body): - """Stop an instance.""" - context = req.environ['nova.context'] - instance = self._get_instance(context, id) - extensions.check_compute_policy(context, 'stop', instance) - - try: - self.compute_api.stop(context, instance) - except (exception.InstanceNotReady, exception.InstanceIsLocked) as e: - raise webob.exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'stop', id) - return webob.Response(status_int=202) - - -class Server_start_stop(extensions.ExtensionDescriptor): - """Start/Stop instance compute API support.""" - - name = "ServerStartStop" - alias = "os-server-start-stop" - namespace = "http://docs.openstack.org/compute/ext/servers/api/v1.1" - updated = "2012-01-23T00:00:00Z" - - def get_controller_extensions(self): - controller = ServerStartStopActionController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_usage.py b/nova/api/openstack/compute/legacy_v2/contrib/server_usage.py deleted file mode 100644 index f09e22db2fb..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/server_usage.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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 nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute - -authorize = extensions.soft_extension_authorizer('compute', 'server_usage') - - -class ServerUsageController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ServerUsageController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - - def _extend_server(self, server, instance): - for k in ['launched_at', 'terminated_at']: - key = "%s:%s" % (Server_usage.alias, k) - # NOTE(danms): Historically, this timestamp has been generated - # merely by grabbing str(datetime) of a TZ-naive object. The - # only way we can keep that with instance objects is to strip - # the tzinfo from the stamp and str() it. - server[key] = (instance[k].replace(tzinfo=None) - if instance[k] else None) - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if authorize(context): - server = resp_obj.obj['server'] - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show' method. - self._extend_server(server, db_instance) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if authorize(context): - servers = list(resp_obj.obj['servers']) - for server in servers: - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'detail' method. - self._extend_server(server, db_instance) - - -class Server_usage(extensions.ExtensionDescriptor): - """Adds launched_at and terminated_at on Servers.""" - - name = "ServerUsage" - alias = "OS-SRV-USG" - namespace = ("http://docs.openstack.org/compute/ext/" - "server_usage/api/v1.1") - updated = "2013-04-29T00:00:00Z" - - def get_controller_extensions(self): - controller = ServerUsageController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/services.py b/nova/api/openstack/compute/legacy_v2/contrib/services.py deleted file mode 100644 index 625a10157dc..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/services.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright 2012 IBM Corp. -# -# 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 webob.exc - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import context as nova_context -from nova import exception -from nova.i18n import _ -from nova import servicegroup -from nova import utils - -authorize = extensions.extension_authorizer('compute', 'services') - - -class ServiceController(object): - - def __init__(self, ext_mgr=None, *args, **kwargs): - self.host_api = compute.HostAPI() - self.servicegroup_api = servicegroup.API() - self.ext_mgr = ext_mgr - - def _get_services(self, req): - api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata') - - context = req.environ['nova.context'] - authorize(context) - - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks - nova_context.require_admin_context(context) - - services = [ - s - for s in self.host_api.service_get_all(context, set_zones=True) - if s['binary'] not in api_services - ] - - host = '' - if 'host' in req.GET: - host = req.GET['host'] - binary = '' - if 'binary' in req.GET: - binary = req.GET['binary'] - if host: - services = [s for s in services if s['host'] == host] - if binary: - services = [s for s in services if s['binary'] == binary] - - return services - - def _get_service_detail(self, svc, detailed): - alive = self.servicegroup_api.service_is_up(svc) - state = (alive and "up") or "down" - active = 'enabled' - if svc['disabled']: - active = 'disabled' - service_detail = {'binary': svc['binary'], 'host': svc['host'], - 'zone': svc['availability_zone'], - 'status': active, 'state': state, - 'updated_at': svc['updated_at']} - if self.ext_mgr.is_loaded('os-extended-services-delete'): - service_detail['id'] = svc['id'] - if detailed: - service_detail['disabled_reason'] = svc['disabled_reason'] - - return service_detail - - def _get_services_list(self, req, detailed): - services = self._get_services(req) - svcs = [] - for svc in services: - svcs.append(self._get_service_detail(svc, detailed)) - - return svcs - - def _is_valid_as_reason(self, reason): - try: - utils.check_string_length(reason.strip(), 'Disabled reason', - min_length=1, max_length=255) - except exception.InvalidInput: - return False - - return True - - @wsgi.response(204) - def delete(self, req, id): - """Deletes the specified service.""" - if not self.ext_mgr.is_loaded('os-extended-services-delete'): - raise webob.exc.HTTPMethodNotAllowed() - - context = req.environ['nova.context'] - authorize(context) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks - nova_context.require_admin_context(context) - - try: - utils.validate_integer(id, 'id') - except exception.InvalidInput as exc: - raise webob.exc.HTTPBadRequest(explanation=exc.format_message()) - - try: - self.host_api.service_delete(context, id) - except exception.ServiceNotFound: - explanation = _("Service %s not found.") % id - raise webob.exc.HTTPNotFound(explanation=explanation) - - def index(self, req): - """Return a list of all running services.""" - detailed = self.ext_mgr.is_loaded('os-extended-services') - services = self._get_services_list(req, detailed) - - return {'services': services} - - def update(self, req, id, body): - """Enable/Disable scheduling for a service.""" - context = req.environ['nova.context'] - authorize(context) - # NOTE(alex_xu): back-compatible with db layer hard-code admin - # permission checks - nova_context.require_admin_context(context) - ext_loaded = self.ext_mgr.is_loaded('os-extended-services') - if id == "enable": - disabled = False - status = "enabled" - elif (id == "disable" or - (id == "disable-log-reason" and ext_loaded)): - disabled = True - status = "disabled" - else: - msg = _("Unknown action") - raise webob.exc.HTTPNotFound(explanation=msg) - try: - host = body['host'] - binary = body['binary'] - ret_value = { - 'service': { - 'host': host, - 'binary': binary, - 'status': status, - }, - } - status_detail = { - 'disabled': disabled, - 'disabled_reason': None, - } - if id == "disable-log-reason": - reason = body['disabled_reason'] - if not self._is_valid_as_reason(reason): - msg = _('The string containing the reason for disabling ' - 'the service contains invalid characters or is ' - 'too long.') - raise webob.exc.HTTPBadRequest(explanation=msg) - - status_detail['disabled_reason'] = reason - ret_value['service']['disabled_reason'] = reason - except (TypeError, KeyError): - msg = _('Invalid attribute in the request') - if 'host' in body and 'binary' in body: - msg = _('Missing disabled reason field') - raise webob.exc.HTTPBadRequest(explanation=msg) - - try: - self.host_api.service_update(context, host, binary, status_detail) - except exception.HostBinaryNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - - return ret_value - - -class Services(extensions.ExtensionDescriptor): - """Services support.""" - - name = "Services" - alias = "os-services" - namespace = "http://docs.openstack.org/compute/ext/services/api/v2" - updated = "2012-10-28T00:00:00Z" - - def get_resources(self): - resources = [] - resource = extensions.ResourceExtension('os-services', - ServiceController(self.ext_mgr)) - - resources.append(resource) - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/shelve.py b/nova/api/openstack/compute/legacy_v2/contrib/shelve.py deleted file mode 100644 index b457e91b5ca..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/shelve.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2013 Rackspace Hosting -# -# 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. - -"""The shelved mode extension.""" - -import webob -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions as exts -from nova.api.openstack import wsgi -from nova import compute -from nova import exception - - -auth_shelve = exts.extension_authorizer('compute', 'shelve') -auth_shelve_offload = exts.extension_authorizer('compute', 'shelveOffload') -auth_unshelve = exts.extension_authorizer('compute', 'unshelve') - - -class ShelveController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ShelveController, self).__init__(*args, **kwargs) - self.compute_api = compute.API() - - @wsgi.action('shelve') - def _shelve(self, req, id, body): - """Move an instance into shelved mode.""" - context = req.environ["nova.context"] - auth_shelve(context) - - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.shelve(context, instance) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'shelve', id) - - return webob.Response(status_int=202) - - @wsgi.action('shelveOffload') - def _shelve_offload(self, req, id, body): - """Force removal of a shelved instance from the compute node.""" - context = req.environ["nova.context"] - auth_shelve_offload(context) - - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.shelve_offload(context, instance) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'shelveOffload', - id) - - return webob.Response(status_int=202) - - @wsgi.action('unshelve') - def _unshelve(self, req, id, body): - """Restore an instance from shelved mode.""" - context = req.environ["nova.context"] - auth_unshelve(context) - instance = common.get_instance(self.compute_api, context, id) - try: - self.compute_api.unshelve(context, instance) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'unshelve', - id) - return webob.Response(status_int=202) - - -class Shelve(exts.ExtensionDescriptor): - """Instance shelve mode.""" - - name = "Shelve" - alias = "os-shelve" - namespace = "http://docs.openstack.org/compute/ext/shelve/api/v1.1" - updated = "2013-04-06T00:00:00Z" - - def get_controller_extensions(self): - controller = ShelveController() - extension = exts.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/simple_tenant_usage.py b/nova/api/openstack/compute/legacy_v2/contrib/simple_tenant_usage.py deleted file mode 100644 index 75d1e381164..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/simple_tenant_usage.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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 iso8601 -from oslo_utils import timeutils -import six -import six.moves.urllib.parse as urlparse -from webob import exc - -from nova.api.openstack import extensions -from nova import exception -from nova.i18n import _ -from nova import objects - -authorize_show = extensions.extension_authorizer('compute', - 'simple_tenant_usage:show') -authorize_list = extensions.extension_authorizer('compute', - 'simple_tenant_usage:list') - - -def parse_strtime(dstr, fmt): - try: - return timeutils.parse_strtime(dstr, fmt) - except (TypeError, ValueError) as e: - raise exception.InvalidStrTime(reason=six.text_type(e)) - - -class SimpleTenantUsageController(object): - def _hours_for(self, instance, period_start, period_stop): - launched_at = instance.launched_at - terminated_at = instance.terminated_at - if terminated_at is not None: - if not isinstance(terminated_at, datetime.datetime): - # NOTE(mriedem): Instance object DateTime fields are - # timezone-aware so convert using isotime. - terminated_at = timeutils.parse_isotime(terminated_at) - - if launched_at is not None: - if not isinstance(launched_at, datetime.datetime): - launched_at = timeutils.parse_isotime(launched_at) - - if terminated_at and terminated_at < period_start: - return 0 - # nothing if it started after the usage report ended - if launched_at and launched_at > period_stop: - return 0 - if launched_at: - # if instance launched after period_started, don't charge for first - start = max(launched_at, period_start) - if terminated_at: - # if instance stopped before period_stop, don't charge after - stop = min(period_stop, terminated_at) - else: - # instance is still running, so charge them up to current time - stop = period_stop - dt = stop - start - return dt.total_seconds() / 3600.0 - else: - # instance hasn't launched, so no charge - return 0 - - def _get_flavor(self, context, instance, flavors_cache): - """Get flavor information from the instance object, - allowing a fallback to lookup by-id for deleted instances only. - """ - try: - return instance.get_flavor() - except exception.NotFound: - if not instance.deleted: - # Only support the fallback mechanism for deleted instances - # that would have been skipped by migration #153 - raise - - flavor_type = instance.instance_type_id - if flavor_type in flavors_cache: - return flavors_cache[flavor_type] - - try: - flavor_ref = objects.Flavor.get_by_id(context, flavor_type) - flavors_cache[flavor_type] = flavor_ref - except exception.FlavorNotFound: - # can't bill if there is no flavor - flavor_ref = None - - return flavor_ref - - def _tenant_usages_for_period(self, context, period_start, - period_stop, tenant_id=None, detailed=True): - - instances = objects.InstanceList.get_active_by_window_joined( - context, period_start, period_stop, tenant_id, - expected_attrs=['flavor']) - rval = {} - flavors = {} - - for instance in instances: - info = {} - info['hours'] = self._hours_for(instance, - period_start, - period_stop) - flavor = self._get_flavor(context, instance, flavors) - if not flavor: - info['flavor'] = '' - else: - info['flavor'] = flavor.name - - info['instance_id'] = instance.uuid - info['name'] = instance.display_name - - info['memory_mb'] = instance.memory_mb - info['local_gb'] = instance.root_gb + instance.ephemeral_gb - info['vcpus'] = instance.vcpus - - info['tenant_id'] = instance.project_id - - # NOTE(mriedem): We need to normalize the start/end times back - # to timezone-naive so the response doesn't change after the - # conversion to objects. - info['started_at'] = timeutils.normalize_time(instance.launched_at) - - info['ended_at'] = ( - timeutils.normalize_time(instance.terminated_at) if - instance.terminated_at else None) - - if info['ended_at']: - info['state'] = 'terminated' - else: - info['state'] = instance.vm_state - - now = timeutils.utcnow() - - if info['state'] == 'terminated': - delta = info['ended_at'] - info['started_at'] - else: - delta = now - info['started_at'] - - info['uptime'] = int(delta.total_seconds()) - - if info['tenant_id'] not in rval: - summary = {} - summary['tenant_id'] = info['tenant_id'] - if detailed: - summary['server_usages'] = [] - summary['total_local_gb_usage'] = 0 - summary['total_vcpus_usage'] = 0 - summary['total_memory_mb_usage'] = 0 - summary['total_hours'] = 0 - summary['start'] = timeutils.normalize_time(period_start) - summary['stop'] = timeutils.normalize_time(period_stop) - rval[info['tenant_id']] = summary - - summary = rval[info['tenant_id']] - summary['total_local_gb_usage'] += info['local_gb'] * info['hours'] - summary['total_vcpus_usage'] += info['vcpus'] * info['hours'] - summary['total_memory_mb_usage'] += (info['memory_mb'] * - info['hours']) - - summary['total_hours'] += info['hours'] - if detailed: - summary['server_usages'].append(info) - - return rval.values() - - def _parse_datetime(self, dtstr): - if not dtstr: - value = timeutils.utcnow() - elif isinstance(dtstr, datetime.datetime): - value = dtstr - else: - for fmt in ["%Y-%m-%dT%H:%M:%S", - "%Y-%m-%dT%H:%M:%S.%f", - "%Y-%m-%d %H:%M:%S.%f"]: - try: - value = parse_strtime(dtstr, fmt) - break - except exception.InvalidStrTime: - pass - else: - msg = _("Datetime is in invalid format") - raise exception.InvalidStrTime(reason=msg) - - # NOTE(mriedem): Instance object DateTime fields are timezone-aware - # so we have to force UTC timezone for comparing this datetime against - # instance object fields and still maintain backwards compatibility - # in the API. - if value.utcoffset() is None: - value = value.replace(tzinfo=iso8601.iso8601.Utc()) - return value - - def _get_datetime_range(self, req): - qs = req.environ.get('QUERY_STRING', '') - env = urlparse.parse_qs(qs) - # NOTE(lzyeval): env.get() always returns a list - period_start = self._parse_datetime(env.get('start', [None])[0]) - period_stop = self._parse_datetime(env.get('end', [None])[0]) - - if not period_start < period_stop: - msg = _("Invalid start time. The start time cannot occur after " - "the end time.") - raise exc.HTTPBadRequest(explanation=msg) - - detailed = env.get('detailed', ['0'])[0] == '1' - return (period_start, period_stop, detailed) - - def index(self, req): - """Retrieve tenant_usage for all tenants.""" - context = req.environ['nova.context'] - - authorize_list(context) - - try: - (period_start, period_stop, detailed) = self._get_datetime_range( - req) - except exception.InvalidStrTime as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - now = timeutils.parse_isotime(timeutils.utcnow().isoformat()) - if period_stop > now: - period_stop = now - usages = self._tenant_usages_for_period(context, - period_start, - period_stop, - detailed=detailed) - return {'tenant_usages': usages} - - def show(self, req, id): - """Retrieve tenant_usage for a specified tenant.""" - tenant_id = id - context = req.environ['nova.context'] - - authorize_show(context, {'project_id': tenant_id}) - - try: - (period_start, period_stop, ignore) = self._get_datetime_range( - req) - except exception.InvalidStrTime as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - now = timeutils.parse_isotime(timeutils.utcnow().isoformat()) - if period_stop > now: - period_stop = now - usage = self._tenant_usages_for_period(context, - period_start, - period_stop, - tenant_id=tenant_id, - detailed=True) - if len(usage): - usage = list(usage)[0] - else: - usage = {} - return {'tenant_usage': usage} - - -class Simple_tenant_usage(extensions.ExtensionDescriptor): - """Simple tenant usage extension.""" - - name = "SimpleTenantUsage" - alias = "os-simple-tenant-usage" - namespace = ("http://docs.openstack.org/compute/ext/" - "os-simple-tenant-usage/api/v1.1") - updated = "2011-08-19T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-simple-tenant-usage', - SimpleTenantUsageController()) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/used_limits.py b/nova/api/openstack/compute/legacy_v2/contrib/used_limits.py deleted file mode 100644 index 81c4e79dc7b..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/used_limits.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 six - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import quota - - -QUOTAS = quota.QUOTAS - - -XMLNS = "http://docs.openstack.org/compute/ext/used_limits/api/v1.1" -ALIAS = "os-used-limits" -authorize = extensions.soft_extension_authorizer('compute', 'used_limits') -authorize_for_admin = extensions.extension_authorizer('compute', - 'used_limits_for_admin') - - -class UsedLimitsController(wsgi.Controller): - - def __init__(self, ext_mgr): - self.ext_mgr = ext_mgr - - @staticmethod - def _reserved(req): - try: - return int(req.GET['reserved']) - except (ValueError, KeyError): - return False - - @wsgi.extends - def index(self, req, resp_obj): - context = req.environ['nova.context'] - project_id = self._project_id(context, req) - quotas = QUOTAS.get_project_quotas(context, project_id, usages=True) - quota_map = { - 'totalRAMUsed': 'ram', - 'totalCoresUsed': 'cores', - 'totalInstancesUsed': 'instances', - 'totalFloatingIpsUsed': 'floating_ips', - 'totalSecurityGroupsUsed': 'security_groups', - } - if self.ext_mgr.is_loaded('os-server-group-quotas'): - quota_map['totalServerGroupsUsed'] = 'server_groups' - - used_limits = {} - for display_name, key in six.iteritems(quota_map): - if key in quotas: - reserved = (quotas[key]['reserved'] - if self._reserved(req) else 0) - used_limits[display_name] = quotas[key]['in_use'] + reserved - - resp_obj.obj['limits']['absolute'].update(used_limits) - - def _project_id(self, context, req): - if self.ext_mgr.is_loaded('os-used-limits-for-admin'): - if 'tenant_id' in req.GET: - tenant_id = req.GET.get('tenant_id') - target = { - 'project_id': tenant_id, - 'user_id': context.user_id - } - authorize_for_admin(context, target=target) - return tenant_id - return context.project_id - - -class Used_limits(extensions.ExtensionDescriptor): - """Provide data on limited resources that are being used.""" - - name = "UsedLimits" - alias = ALIAS - namespace = XMLNS - updated = "2012-07-13T00:00:00Z" - - def get_controller_extensions(self): - controller = UsedLimitsController(self.ext_mgr) - limits_ext = extensions.ControllerExtension(self, 'limits', - controller=controller) - return [limits_ext] diff --git a/nova/api/openstack/compute/legacy_v2/contrib/used_limits_for_admin.py b/nova/api/openstack/compute/legacy_v2/contrib/used_limits_for_admin.py deleted file mode 100644 index f731a7aa0c3..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/used_limits_for_admin.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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 nova.api.openstack import extensions - - -class Used_limits_for_admin(extensions.ExtensionDescriptor): - """Provide data to admin on limited resources used by other tenants.""" - - name = "UsedLimitsForAdmin" - alias = "os-used-limits-for-admin" - namespace = ("http://docs.openstack.org/compute/ext/used_limits_for_admin" - "/api/v1.1") - updated = "2013-05-02T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/user_data.py b/nova/api/openstack/compute/legacy_v2/contrib/user_data.py deleted file mode 100644 index b7e559c1101..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/user_data.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 nova.api.openstack import extensions - - -class User_data(extensions.ExtensionDescriptor): - """Add user_data to the Create Server v1.1 API.""" - - name = "UserData" - alias = "os-user-data" - namespace = ("http://docs.openstack.org/compute/ext/" - "userdata/api/v1.1") - updated = "2012-08-07T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/user_quotas.py b/nova/api/openstack/compute/legacy_v2/contrib/user_quotas.py deleted file mode 100644 index 344166e3cfd..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/user_quotas.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# 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. - -from nova.api.openstack import extensions - - -class User_quotas(extensions.ExtensionDescriptor): - """Project user quota support.""" - - name = "UserQuotas" - alias = "os-user-quotas" - namespace = ("http://docs.openstack.org/compute/ext/user_quotas" - "/api/v1.1") - updated = "2013-07-18T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/virtual_interfaces.py b/nova/api/openstack/compute/legacy_v2/contrib/virtual_interfaces.py deleted file mode 100644 index faaabbc6a28..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/virtual_interfaces.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) 2011 Midokura KK -# 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. - -"""The virtual interfaces extension.""" - -import webob - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova import compute -from nova.i18n import _ -from nova import network - - -authorize = extensions.extension_authorizer('compute', 'virtual_interfaces') - - -def _translate_vif_summary_view(vif): - """Maps keys for VIF summary view.""" - d = {} - d['id'] = vif.uuid - d['mac_address'] = vif.address - return d - - -class ServerVirtualInterfaceController(object): - """The instance VIF API controller for the OpenStack API. - """ - - def __init__(self): - self.compute_api = compute.API() - self.network_api = network.API() - super(ServerVirtualInterfaceController, self).__init__() - - def _items(self, req, server_id, entity_maker): - """Returns a list of VIFs, transformed through entity_maker.""" - context = req.environ['nova.context'] - instance = common.get_instance(self.compute_api, context, server_id) - - try: - vifs = self.network_api.get_vifs_by_instance(context, instance) - except NotImplementedError: - msg = _('Listing virtual interfaces is not supported by this ' - 'cloud.') - raise webob.exc.HTTPBadRequest(explanation=msg) - limited_list = common.limited(vifs, req) - res = [entity_maker(vif) for vif in limited_list] - return {'virtual_interfaces': res} - - def index(self, req, server_id): - """Returns the list of VIFs for a given instance.""" - authorize(req.environ['nova.context']) - return self._items(req, server_id, - entity_maker=_translate_vif_summary_view) - - -class Virtual_interfaces(extensions.ExtensionDescriptor): - """Virtual interface support.""" - - name = "VirtualInterfaces" - alias = "os-virtual-interfaces" - namespace = ("http://docs.openstack.org/compute/ext/" - "virtual_interfaces/api/v1.1") - updated = "2011-08-17T00:00:00Z" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension( - 'os-virtual-interfaces', - controller=ServerVirtualInterfaceController(), - parent=dict(member_name='server', collection_name='servers')) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/contrib/volume_attachment_update.py b/nova/api/openstack/compute/legacy_v2/contrib/volume_attachment_update.py deleted file mode 100644 index 32d9cbdce45..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/volume_attachment_update.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2013 Nebula, 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. - -from nova.api.openstack import extensions - - -class Volume_attachment_update(extensions.ExtensionDescriptor): - """Support for updating a volume attachment.""" - - name = "VolumeAttachmentUpdate" - alias = "os-volume-attachment-update" - namespace = ("http://docs.openstack.org/compute/ext/" - "os-volume-attachment-update/api/v2") - updated = "2013-06-20T00:00:00Z" diff --git a/nova/api/openstack/compute/legacy_v2/contrib/volumes.py b/nova/api/openstack/compute/legacy_v2/contrib/volumes.py deleted file mode 100644 index 6b0e9966e2b..00000000000 --- a/nova/api/openstack/compute/legacy_v2/contrib/volumes.py +++ /dev/null @@ -1,613 +0,0 @@ -# Copyright 2011 Justin Santa Barbara -# 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. - -"""The volumes extension.""" - -from oslo_log import log as logging -from oslo_utils import strutils -from oslo_utils import uuidutils -import webob -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova.i18n import _ -from nova.i18n import _LI -from nova import objects -from nova import volume - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'volumes') - -authorize_attach = extensions.extension_authorizer('compute', - 'volume_attachments') - - -def _translate_volume_detail_view(context, vol): - """Maps keys for volumes details view.""" - - d = _translate_volume_summary_view(context, vol) - - # No additional data / lookups at the moment - - return d - - -def _translate_volume_summary_view(context, vol): - """Maps keys for volumes summary view.""" - d = {} - - d['id'] = vol['id'] - d['status'] = vol['status'] - d['size'] = vol['size'] - d['availabilityZone'] = vol['availability_zone'] - d['createdAt'] = vol['created_at'] - - if vol['attach_status'] == 'attached': - # NOTE(ildikov): The attachments field in the volume info that - # Cinder sends is converted to an OrderedDict with the - # instance_uuid as key to make it easier for the multiattach - # feature to check the required information. Multiattach will - # be enable in the Nova API in Newton. - # The format looks like the following: - # attachments = {'instance_uuid': { - # 'attachment_id': 'attachment_uuid', - # 'mountpoint': '/dev/sda/ - # } - # } - attachment = vol['attachments'].items()[0] - d['attachments'] = [_translate_attachment_detail_view(vol['id'], - attachment[0], - attachment[1].get('mountpoint'))] - else: - d['attachments'] = [{}] - - d['displayName'] = vol['display_name'] - d['displayDescription'] = vol['display_description'] - - if vol['volume_type_id'] and vol.get('volume_type'): - d['volumeType'] = vol['volume_type']['name'] - else: - d['volumeType'] = vol['volume_type_id'] - - d['snapshotId'] = vol['snapshot_id'] - LOG.info(_LI("vol=%s"), vol, context=context) - - if vol.get('volume_metadata'): - d['metadata'] = vol.get('volume_metadata') - else: - d['metadata'] = {} - - return d - - -class VolumeController(wsgi.Controller): - """The Volumes API controller for the OpenStack API.""" - - def __init__(self): - self.volume_api = volume.API() - super(VolumeController, self).__init__() - - def show(self, req, id): - """Return data about the given volume.""" - context = req.environ['nova.context'] - authorize(context) - - try: - vol = self.volume_api.get(context, id) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - - return {'volume': _translate_volume_detail_view(context, vol)} - - def delete(self, req, id): - """Delete a volume.""" - context = req.environ['nova.context'] - authorize(context) - - LOG.info(_LI("Delete volume with id: %s"), id, context=context) - - try: - self.volume_api.delete(context, id) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - return webob.Response(status_int=202) - - def index(self, req): - """Returns a summary list of volumes.""" - return self._items(req, entity_maker=_translate_volume_summary_view) - - def detail(self, req): - """Returns a detailed list of volumes.""" - return self._items(req, entity_maker=_translate_volume_detail_view) - - def _items(self, req, entity_maker): - """Returns a list of volumes, transformed through entity_maker.""" - context = req.environ['nova.context'] - authorize(context) - - volumes = self.volume_api.get_all(context) - limited_list = common.limited(volumes, req) - res = [entity_maker(context, vol) for vol in limited_list] - return {'volumes': res} - - def create(self, req, body): - """Creates a new volume.""" - context = req.environ['nova.context'] - authorize(context) - - if not self.is_valid_body(body, 'volume'): - msg = _("volume not specified") - raise exc.HTTPBadRequest(explanation=msg) - - vol = body['volume'] - - vol_type = vol.get('volume_type', None) - - metadata = vol.get('metadata', None) - - snapshot_id = vol.get('snapshot_id') - - if snapshot_id is not None: - try: - snapshot = self.volume_api.get_snapshot(context, snapshot_id) - except exception.SnapshotNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - else: - snapshot = None - - size = vol.get('size', None) - if size is None and snapshot is not None: - size = snapshot['volume_size'] - - LOG.info(_LI("Create volume of %s GB"), size, context=context) - - availability_zone = vol.get('availability_zone', None) - - try: - new_volume = self.volume_api.create( - context, - size, - vol.get('display_name'), - vol.get('display_description'), - snapshot=snapshot, - volume_type=vol_type, - metadata=metadata, - availability_zone=availability_zone - ) - except exception.InvalidInput as err: - raise exc.HTTPBadRequest(explanation=err.format_message()) - except exception.OverQuota as err: - raise exc.HTTPForbidden(explanation=err.format_message()) - # TODO(vish): Instance should be None at db layer instead of - # trying to lazy load, but for now we turn it into - # a dict to avoid an error. - retval = _translate_volume_detail_view(context, dict(new_volume)) - result = {'volume': retval} - - location = '%s/%s' % (req.url, new_volume['id']) - - return wsgi.ResponseObject(result, headers=dict(location=location)) - - -def _translate_attachment_detail_view(volume_id, instance_uuid, mountpoint): - """Maps keys for attachment details view.""" - - d = _translate_attachment_summary_view(volume_id, - instance_uuid, - mountpoint) - - # No additional data / lookups at the moment - return d - - -def _translate_attachment_summary_view(volume_id, instance_uuid, mountpoint): - """Maps keys for attachment summary view.""" - d = {} - - # NOTE(justinsb): We use the volume id as the id of the attachment object - d['id'] = volume_id - - d['volumeId'] = volume_id - - d['serverId'] = instance_uuid - if mountpoint: - d['device'] = mountpoint - - return d - - -class VolumeAttachmentController(wsgi.Controller): - """The volume attachment API controller for the OpenStack API. - - A child resource of the server. Note that we use the volume id - as the ID of the attachment (though this is not guaranteed externally) - - """ - - def __init__(self, ext_mgr=None): - self.compute_api = compute.API() - self.volume_api = volume.API() - self.ext_mgr = ext_mgr - super(VolumeAttachmentController, self).__init__() - - def index(self, req, server_id): - """Returns the list of volume attachments for a given instance.""" - context = req.environ['nova.context'] - authorize_attach(context, action='index') - return self._items(req, server_id, - entity_maker=_translate_attachment_summary_view) - - def show(self, req, server_id, id): - """Return data about the given volume attachment.""" - context = req.environ['nova.context'] - authorize(context) - authorize_attach(context, action='show') - - volume_id = id - instance = common.get_instance(self.compute_api, context, server_id) - bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( - context, instance.uuid) - - if not bdms: - msg = _("Instance %s is not attached.") % server_id - raise exc.HTTPNotFound(explanation=msg) - - assigned_mountpoint = None - - for bdm in bdms: - if bdm.volume_id == volume_id: - assigned_mountpoint = bdm.device_name - break - - if assigned_mountpoint is None: - msg = _("volume_id not found: %s") % volume_id - raise exc.HTTPNotFound(explanation=msg) - - return {'volumeAttachment': _translate_attachment_detail_view( - volume_id, - instance.uuid, - assigned_mountpoint)} - - def _validate_volume_id(self, volume_id): - if not uuidutils.is_uuid_like(volume_id): - msg = _("Bad volumeId format: volumeId is " - "not in proper format (%s)") % volume_id - raise exc.HTTPBadRequest(explanation=msg) - - def create(self, req, server_id, body): - """Attach a volume to an instance.""" - context = req.environ['nova.context'] - authorize(context) - authorize_attach(context, action='create') - - if not self.is_valid_body(body, 'volumeAttachment'): - msg = _("volumeAttachment not specified") - raise exc.HTTPBadRequest(explanation=msg) - try: - volume_id = body['volumeAttachment']['volumeId'] - except KeyError: - msg = _("volumeId must be specified.") - raise exc.HTTPBadRequest(explanation=msg) - device = body['volumeAttachment'].get('device') - - self._validate_volume_id(volume_id) - - LOG.info(_LI("Attach volume %(volume_id)s to instance %(server_id)s " - "at %(device)s"), - {'volume_id': volume_id, - 'device': device, - 'server_id': server_id}, - context=context) - - instance = common.get_instance(self.compute_api, context, server_id) - try: - device = self.compute_api.attach_volume(context, instance, - volume_id, device) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'attach_volume', server_id) - - # The attach is async - attachment = {} - attachment['id'] = volume_id - attachment['serverId'] = server_id - attachment['volumeId'] = volume_id - attachment['device'] = device - - # NOTE(justinsb): And now, we have a problem... - # The attach is async, so there's a window in which we don't see - # the attachment (until the attachment completes). We could also - # get problems with concurrent requests. I think we need an - # attachment state, and to write to the DB here, but that's a bigger - # change. - # For now, we'll probably have to rely on libraries being smart - - # TODO(justinsb): How do I return "accepted" here? - return {'volumeAttachment': attachment} - - def update(self, req, server_id, id, body): - if (not self.ext_mgr or - not self.ext_mgr.is_loaded('os-volume-attachment-update')): - raise exc.HTTPBadRequest() - context = req.environ['nova.context'] - authorize(context) - authorize_attach(context, action='update') - - if not self.is_valid_body(body, 'volumeAttachment'): - msg = _("volumeAttachment not specified") - raise exc.HTTPBadRequest(explanation=msg) - - old_volume_id = id - old_volume = self.volume_api.get(context, old_volume_id) - - try: - new_volume_id = body['volumeAttachment']['volumeId'] - except KeyError: - msg = _("volumeId must be specified.") - raise exc.HTTPBadRequest(explanation=msg) - self._validate_volume_id(new_volume_id) - new_volume = self.volume_api.get(context, new_volume_id) - - instance = common.get_instance(self.compute_api, context, server_id) - - bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( - context, instance.uuid) - found = False - try: - for bdm in bdms: - if bdm.volume_id != old_volume_id: - continue - try: - self.compute_api.swap_volume(context, instance, old_volume, - new_volume) - found = True - break - except exception.VolumeUnattached: - # The volume is not attached. Treat it as NotFound - # by falling through. - pass - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'swap_volume', server_id) - - if not found: - msg = _("volume_id not found: %s") % old_volume_id - raise exc.HTTPNotFound(explanation=msg) - else: - return webob.Response(status_int=202) - - def delete(self, req, server_id, id): - """Detach a volume from an instance.""" - context = req.environ['nova.context'] - authorize(context) - authorize_attach(context, action='delete') - - volume_id = id - LOG.info(_LI("Detach volume %s"), volume_id, context=context) - - instance = common.get_instance(self.compute_api, context, server_id) - - volume = self.volume_api.get(context, volume_id) - - bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( - context, instance.uuid) - if not bdms: - msg = _("Instance %s is not attached.") % server_id - raise exc.HTTPNotFound(explanation=msg) - - found = False - try: - for bdm in bdms: - if bdm.volume_id != volume_id: - continue - if bdm.is_root: - msg = _("Can't detach root device volume") - raise exc.HTTPForbidden(explanation=msg) - try: - self.compute_api.detach_volume(context, instance, volume) - found = True - break - except exception.VolumeUnattached: - # The volume is not attached. Treat it as NotFound - # by falling through. - pass - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'detach_volume', server_id) - - if not found: - msg = _("volume_id not found: %s") % volume_id - raise exc.HTTPNotFound(explanation=msg) - else: - return webob.Response(status_int=202) - - def _items(self, req, server_id, entity_maker): - """Returns a list of attachments, transformed through entity_maker.""" - context = req.environ['nova.context'] - authorize(context) - - instance = common.get_instance(self.compute_api, context, server_id) - - bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( - context, instance.uuid) - limited_list = common.limited(bdms, req) - results = [] - - for bdm in limited_list: - if bdm.volume_id: - results.append(entity_maker(bdm.volume_id, - bdm.instance_uuid, - bdm.device_name)) - - return {'volumeAttachments': results} - - -def _translate_snapshot_detail_view(context, vol): - """Maps keys for snapshots details view.""" - - d = _translate_snapshot_summary_view(context, vol) - - # NOTE(gagupta): No additional data / lookups at the moment - return d - - -def _translate_snapshot_summary_view(context, vol): - """Maps keys for snapshots summary view.""" - d = {} - - d['id'] = vol['id'] - d['volumeId'] = vol['volume_id'] - d['status'] = vol['status'] - # NOTE(gagupta): We map volume_size as the snapshot size - d['size'] = vol['volume_size'] - d['createdAt'] = vol['created_at'] - d['displayName'] = vol['display_name'] - d['displayDescription'] = vol['display_description'] - return d - - -class SnapshotController(wsgi.Controller): - """The Snapshots API controller for the OpenStack API.""" - - def __init__(self): - self.volume_api = volume.API() - super(SnapshotController, self).__init__() - - def show(self, req, id): - """Return data about the given snapshot.""" - context = req.environ['nova.context'] - authorize(context) - - try: - vol = self.volume_api.get_snapshot(context, id) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - - return {'snapshot': _translate_snapshot_detail_view(context, vol)} - - def delete(self, req, id): - """Delete a snapshot.""" - context = req.environ['nova.context'] - authorize(context) - - LOG.info(_LI("Delete snapshot with id: %s"), id, context=context) - - try: - self.volume_api.delete_snapshot(context, id) - except exception.NotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - return webob.Response(status_int=202) - - def index(self, req): - """Returns a summary list of snapshots.""" - return self._items(req, entity_maker=_translate_snapshot_summary_view) - - def detail(self, req): - """Returns a detailed list of snapshots.""" - return self._items(req, entity_maker=_translate_snapshot_detail_view) - - def _items(self, req, entity_maker): - """Returns a list of snapshots, transformed through entity_maker.""" - context = req.environ['nova.context'] - authorize(context) - - snapshots = self.volume_api.get_all_snapshots(context) - limited_list = common.limited(snapshots, req) - res = [entity_maker(context, snapshot) for snapshot in limited_list] - return {'snapshots': res} - - def create(self, req, body): - """Creates a new snapshot.""" - context = req.environ['nova.context'] - authorize(context) - - if not self.is_valid_body(body, 'snapshot'): - msg = _("snapshot not specified") - raise exc.HTTPBadRequest(explanation=msg) - - snapshot = body['snapshot'] - volume_id = snapshot['volume_id'] - - LOG.info(_LI("Create snapshot from volume %s"), volume_id, - context=context) - - force = snapshot.get('force', False) - try: - force = strutils.bool_from_string(force, strict=True) - except ValueError: - msg = _("Invalid value '%s' for force.") % force - raise exc.HTTPBadRequest(explanation=msg) - - if force: - create_func = self.volume_api.create_snapshot_force - else: - create_func = self.volume_api.create_snapshot - - new_snapshot = create_func(context, volume_id, - snapshot.get('display_name'), - snapshot.get('display_description')) - - retval = _translate_snapshot_detail_view(context, new_snapshot) - return {'snapshot': retval} - - -class Volumes(extensions.ExtensionDescriptor): - """Volumes support.""" - - name = "Volumes" - alias = "os-volumes" - namespace = "http://docs.openstack.org/compute/ext/volumes/api/v1.1" - updated = "2011-03-25T00:00:00Z" - - def get_resources(self): - resources = [] - - # NOTE(justinsb): No way to provide singular name ('volume') - # Does this matter? - res = extensions.ResourceExtension('os-volumes', - VolumeController(), - collection_actions={'detail': 'GET'}) - resources.append(res) - - attachment_controller = VolumeAttachmentController(self.ext_mgr) - res = extensions.ResourceExtension('os-volume_attachments', - attachment_controller, - parent=dict( - member_name='server', - collection_name='servers')) - resources.append(res) - - res = extensions.ResourceExtension('os-volumes_boot', - inherits='servers') - resources.append(res) - - res = extensions.ResourceExtension('os-snapshots', - SnapshotController(), - collection_actions={'detail': 'GET'}) - resources.append(res) - - return resources diff --git a/nova/api/openstack/compute/legacy_v2/extensions.py b/nova/api/openstack/compute/legacy_v2/extensions.py deleted file mode 100644 index af030a0307f..00000000000 --- a/nova/api/openstack/compute/legacy_v2/extensions.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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. - -from oslo_config import cfg -from oslo_log import log as logging - -from nova.api.openstack import extensions as base_extensions -from nova.i18n import _LW - -STANDARD_EXTENSIONS = ('nova.api.openstack.compute.legacy_v2.contrib.' + - 'standard_extensions') - -ext_opts = [ - cfg.MultiStrOpt( - 'osapi_compute_extension', - default=[STANDARD_EXTENSIONS], - help='osapi compute extension to load. ' - 'This option will be removed in the near future. ' - 'After that point you have to run all of the API.', - deprecated_for_removal=True), -] -CONF = cfg.CONF -CONF.register_opts(ext_opts) - -LOG = logging.getLogger(__name__) - - -class ExtensionManager(base_extensions.ExtensionManager): - def __init__(self): - self.cls_list = CONF.osapi_compute_extension - if (len(self.cls_list) > 0 and - self.cls_list[0] != STANDARD_EXTENSIONS): - LOG.warning(_LW('The extension configure options are deprecated. ' - 'In the near future you must run all of the API.')) - self.extensions = {} - self.sorted_ext_list = [] - self._load_extensions() diff --git a/nova/api/openstack/compute/legacy_v2/flavors.py b/nova/api/openstack/compute/legacy_v2/flavors.py deleted file mode 100644 index ed47389f4dd..00000000000 --- a/nova/api/openstack/compute/legacy_v2/flavors.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2010 OpenStack Foundation -# 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. - -from oslo_utils import strutils -import webob - -from nova.api.openstack import common -from nova.api.openstack.compute.views import flavors as flavors_view -from nova.api.openstack import wsgi -from nova.compute import flavors -from nova import exception -from nova.i18n import _ -from nova import utils - - -class Controller(wsgi.Controller): - """Flavor controller for the OpenStack API.""" - - _view_builder_class = flavors_view.ViewBuilder - - def index(self, req): - """Return all flavors in brief.""" - limited_flavors = self._get_flavors(req) - return self._view_builder.index(req, limited_flavors) - - def detail(self, req): - """Return all flavors in detail.""" - limited_flavors = self._get_flavors(req) - req.cache_db_flavors(limited_flavors) - return self._view_builder.detail(req, limited_flavors) - - def show(self, req, id): - """Return data about the given flavor id.""" - try: - context = req.environ['nova.context'] - flavor = flavors.get_flavor_by_flavor_id(id, ctxt=context) - req.cache_db_flavor(flavor) - except exception.NotFound: - raise webob.exc.HTTPNotFound() - - return self._view_builder.show(req, flavor) - - def _parse_is_public(self, is_public): - """Parse is_public into something usable.""" - - if is_public is None: - # preserve default value of showing only public flavors - return True - elif utils.is_none_string(is_public): - return None - else: - try: - return strutils.bool_from_string(is_public, strict=True) - except ValueError: - msg = _('Invalid is_public filter [%s]') % is_public - raise webob.exc.HTTPBadRequest(explanation=msg) - - def _get_flavors(self, req): - """Helper function that returns a list of flavor dicts.""" - filters = {} - sort_key = req.params.get('sort_key') or 'flavorid' - sort_dir = req.params.get('sort_dir') or 'asc' - limit, marker = common.get_limit_and_marker(req) - - context = req.environ['nova.context'] - if context.is_admin: - # Only admin has query access to all flavor types - filters['is_public'] = self._parse_is_public( - req.params.get('is_public', None)) - else: - filters['is_public'] = True - filters['disabled'] = False - - if 'minRam' in req.params: - try: - filters['min_memory_mb'] = int(req.params['minRam']) - except ValueError: - msg = _('Invalid minRam filter [%s]') % req.params['minRam'] - raise webob.exc.HTTPBadRequest(explanation=msg) - - if 'minDisk' in req.params: - try: - filters['min_root_gb'] = int(req.params['minDisk']) - except ValueError: - msg = _('Invalid minDisk filter [%s]') % req.params['minDisk'] - raise webob.exc.HTTPBadRequest(explanation=msg) - - try: - limited_flavors = flavors.get_all_flavors_sorted_list(context, - filters=filters, sort_key=sort_key, sort_dir=sort_dir, - limit=limit, marker=marker) - except exception.MarkerNotFound: - msg = _('marker [%s] not found') % marker - raise webob.exc.HTTPBadRequest(explanation=msg) - - return limited_flavors - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/nova/api/openstack/compute/legacy_v2/image_metadata.py b/nova/api/openstack/compute/legacy_v2/image_metadata.py deleted file mode 100644 index 77fdd59c69c..00000000000 --- a/nova/api/openstack/compute/legacy_v2/image_metadata.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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 six -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import wsgi -from nova import exception -from nova.i18n import _ -import nova.image - - -class Controller(object): - """The image metadata API controller for the OpenStack API.""" - - def __init__(self): - self.image_api = nova.image.API() - - def _get_image(self, context, image_id): - try: - return self.image_api.get(context, image_id) - except exception.ImageNotAuthorized as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - except exception.ImageNotFound: - msg = _("Image not found.") - raise exc.HTTPNotFound(explanation=msg) - - def index(self, req, image_id): - """Returns the list of metadata for a given instance.""" - context = req.environ['nova.context'] - metadata = self._get_image(context, image_id)['properties'] - return dict(metadata=metadata) - - def show(self, req, image_id, id): - context = req.environ['nova.context'] - metadata = self._get_image(context, image_id)['properties'] - if id in metadata: - return {'meta': {id: metadata[id]}} - else: - raise exc.HTTPNotFound() - - def create(self, req, image_id, body): - context = req.environ['nova.context'] - image = self._get_image(context, image_id) - if 'metadata' in body: - for key, value in six.iteritems(body['metadata']): - image['properties'][key] = value - common.check_img_metadata_properties_quota(context, - image['properties']) - try: - image = self.image_api.update(context, image_id, image, data=None, - purge_props=True) - except exception.ImageNotAuthorized as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - return dict(metadata=image['properties']) - - def update(self, req, image_id, id, body): - context = req.environ['nova.context'] - - try: - meta = body['meta'] - except KeyError: - expl = _('Incorrect request body format') - raise exc.HTTPBadRequest(explanation=expl) - - if id not in meta: - expl = _('Request body and URI mismatch') - raise exc.HTTPBadRequest(explanation=expl) - if len(meta) > 1: - expl = _('Request body contains too many items') - raise exc.HTTPBadRequest(explanation=expl) - - image = self._get_image(context, image_id) - image['properties'][id] = meta[id] - common.check_img_metadata_properties_quota(context, - image['properties']) - try: - self.image_api.update(context, image_id, image, data=None, - purge_props=True) - except exception.ImageNotAuthorized as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - return dict(meta=meta) - - def update_all(self, req, image_id, body): - context = req.environ['nova.context'] - image = self._get_image(context, image_id) - metadata = body.get('metadata', {}) - common.check_img_metadata_properties_quota(context, metadata) - image['properties'] = metadata - try: - self.image_api.update(context, image_id, image, data=None, - purge_props=True) - except exception.ImageNotAuthorized as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - return dict(metadata=metadata) - - @wsgi.response(204) - def delete(self, req, image_id, id): - context = req.environ['nova.context'] - image = self._get_image(context, image_id) - if id not in image['properties']: - msg = _("Invalid metadata key") - raise exc.HTTPNotFound(explanation=msg) - image['properties'].pop(id) - try: - self.image_api.update(context, image_id, image, data=None, - purge_props=True) - except exception.ImageNotAuthorized as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/nova/api/openstack/compute/legacy_v2/images.py b/nova/api/openstack/compute/legacy_v2/images.py deleted file mode 100644 index c2172c19b0e..00000000000 --- a/nova/api/openstack/compute/legacy_v2/images.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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 webob.exc - -from nova.api.openstack import common -from nova.api.openstack.compute.views import images as views_images -from nova.api.openstack import wsgi -from nova import exception -from nova.i18n import _ -import nova.image -import nova.utils - - -SUPPORTED_FILTERS = { - 'name': 'name', - 'status': 'status', - 'changes-since': 'changes-since', - 'server': 'property-instance_uuid', - 'type': 'property-image_type', - 'minRam': 'min_ram', - 'minDisk': 'min_disk', -} - - -class Controller(wsgi.Controller): - """Base controller for retrieving/displaying images.""" - - _view_builder_class = views_images.ViewBuilder - - def __init__(self, **kwargs): - super(Controller, self).__init__(**kwargs) - self._image_api = nova.image.API() - - def _get_filters(self, req): - """Return a dictionary of query param filters from the request. - - :param req: the Request object coming from the wsgi layer - :retval a dict of key/value filters - """ - filters = {} - for param in req.params: - if param in SUPPORTED_FILTERS or param.startswith('property-'): - # map filter name or carry through if property-* - filter_name = SUPPORTED_FILTERS.get(param, param) - filters[filter_name] = req.params.get(param) - - # ensure server filter is the instance uuid - filter_name = 'property-instance_uuid' - try: - filters[filter_name] = filters[filter_name].rsplit('/', 1)[1] - except (AttributeError, IndexError, KeyError): - pass - - filter_name = 'status' - if filter_name in filters: - # The Image API expects us to use lowercase strings for status - filters[filter_name] = filters[filter_name].lower() - - return filters - - def show(self, req, id): - """Return detailed information about a specific image. - - :param req: `wsgi.Request` object - :param id: Image identifier - """ - context = req.environ['nova.context'] - - try: - image = self._image_api.get(context, id) - except (exception.NotFound, exception.InvalidImageRef): - explanation = _("Image not found.") - raise webob.exc.HTTPNotFound(explanation=explanation) - - req.cache_db_items('images', [image], 'id') - return self._view_builder.show(req, image) - - def delete(self, req, id): - """Delete an image, if allowed. - - :param req: `wsgi.Request` object - :param id: Image identifier (integer) - """ - context = req.environ['nova.context'] - try: - self._image_api.delete(context, id) - except exception.ImageNotFound: - explanation = _("Image not found.") - raise webob.exc.HTTPNotFound(explanation=explanation) - except exception.ImageNotAuthorized: - # The image service raises this exception on delete if glanceclient - # raises HTTPForbidden. - explanation = _("You are not allowed to delete the image.") - raise webob.exc.HTTPForbidden(explanation=explanation) - return webob.exc.HTTPNoContent() - - def index(self, req): - """Return an index listing of images available to the request. - - :param req: `wsgi.Request` object - - """ - context = req.environ['nova.context'] - filters = self._get_filters(req) - page_params = common.get_pagination_params(req) - - try: - images = self._image_api.get_all(context, filters=filters, - **page_params) - except exception.Invalid as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - return self._view_builder.index(req, images) - - def detail(self, req): - """Return a detailed index listing of images available to the request. - - :param req: `wsgi.Request` object. - - """ - context = req.environ['nova.context'] - filters = self._get_filters(req) - page_params = common.get_pagination_params(req) - try: - images = self._image_api.get_all(context, filters=filters, - **page_params) - except exception.Invalid as e: - raise webob.exc.HTTPBadRequest(explanation=e.format_message()) - - req.cache_db_items('images', images, 'id') - return self._view_builder.detail(req, images) - - def create(self, *args, **kwargs): - raise webob.exc.HTTPMethodNotAllowed() - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/nova/api/openstack/compute/legacy_v2/ips.py b/nova/api/openstack/compute/legacy_v2/ips.py deleted file mode 100644 index 328c7c11729..00000000000 --- a/nova/api/openstack/compute/legacy_v2/ips.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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. - -from webob import exc - -import nova -from nova.api.openstack import common -from nova.api.openstack.compute.views import addresses as view_addresses -from nova.api.openstack import wsgi -from nova.i18n import _ - - -class Controller(wsgi.Controller): - """The servers addresses API controller for the OpenStack API.""" - - _view_builder_class = view_addresses.ViewBuilder - - def __init__(self, **kwargs): - super(Controller, self).__init__(**kwargs) - self._compute_api = nova.compute.API() - - def index(self, req, server_id): - context = req.environ["nova.context"] - instance = common.get_instance(self._compute_api, context, server_id) - networks = common.get_networks_for_instance(context, instance) - return self._view_builder.index(networks) - - def show(self, req, server_id, id): - context = req.environ["nova.context"] - instance = common.get_instance(self._compute_api, context, server_id) - networks = common.get_networks_for_instance(context, instance) - if id not in networks: - msg = _("Instance is not a member of specified network") - raise exc.HTTPNotFound(explanation=msg) - - return self._view_builder.show(networks[id], id) - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/nova/api/openstack/compute/legacy_v2/limits.py b/nova/api/openstack/compute/legacy_v2/limits.py deleted file mode 100644 index c1bdc5cbad7..00000000000 --- a/nova/api/openstack/compute/legacy_v2/limits.py +++ /dev/null @@ -1,457 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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. - -""" -Module dedicated functions/classes dealing with rate limiting requests. - -This module handles rate liming at a per-user level, so it should not be used -to prevent intentional Denial of Service attacks, as we can assume a DOS can -easily come through multiple user accounts. DOS protection should be done at a -different layer. Instead this module should be used to protect against -unintentional user actions. With that in mind the limits set here should be -high enough as to not rate-limit any intentional actions. - -To find good rate-limit values, check how long requests are taking (see logs) -in your environment to assess your capabilities and multiply out to get -figures. - -NOTE: As the rate-limiting here is done in memory, this only works per -process (each process will have its own rate limiting counter). -""" - -import collections -import copy -import math -import re -import time - -from oslo_serialization import jsonutils -from oslo_utils import importutils -from six.moves import http_client as httplib -import webob.dec -import webob.exc - -from nova.api.openstack.compute.views import limits as limits_views -from nova.api.openstack import wsgi -from nova.i18n import _ -from nova import quota -from nova import utils -from nova import wsgi as base_wsgi - - -QUOTAS = quota.QUOTAS -LIMITS_PREFIX = "limits." - - -class LimitsController(object): - """Controller for accessing limits in the OpenStack API.""" - - def index(self, req): - """Return all global and rate limit information.""" - context = req.environ['nova.context'] - project_id = req.params.get('tenant_id', context.project_id) - quotas = QUOTAS.get_project_quotas(context, project_id, - usages=False) - abs_limits = {k: v['limit'] for k, v in quotas.items()} - rate_limits = req.environ.get("nova.limits", []) - - builder = self._get_view_builder(req) - return builder.build(rate_limits, abs_limits) - - def create(self, req, body): - """Create a new limit.""" - raise webob.exc.HTTPNotImplemented() - - def delete(self, req, id): - """Delete the limit.""" - raise webob.exc.HTTPNotImplemented() - - def show(self, req, id): - """Show limit information.""" - raise webob.exc.HTTPNotImplemented() - - def update(self, req, id, body): - """Update existing limit.""" - raise webob.exc.HTTPNotImplemented() - - def _get_view_builder(self, req): - return limits_views.ViewBuilder() - - -def create_resource(): - return wsgi.Resource(LimitsController()) - - -class Limit(object): - """Stores information about a limit for HTTP requests.""" - - UNITS = {v: k for k, v in utils.TIME_UNITS.items()} - - def __init__(self, verb, uri, regex, value, unit): - """Initialize a new `Limit`. - - @param verb: HTTP verb (POST, PUT, etc.) - @param uri: Human-readable URI - @param regex: Regular expression format for this limit - @param value: Integer number of requests which can be made - @param unit: Unit of measure for the value parameter - """ - self.verb = verb - self.uri = uri - self.regex = regex - self.value = int(value) - self.unit = unit - self.unit_string = self.display_unit().lower() - self.remaining = int(value) - - if value <= 0: - raise ValueError("Limit value must be > 0") - - self.last_request = None - self.next_request = None - - self.water_level = 0 - self.capacity = self.unit - self.request_value = float(self.capacity) / float(self.value) - msg = (_("Only %(value)s %(verb)s request(s) can be " - "made to %(uri)s every %(unit_string)s.") % - {'value': self.value, 'verb': self.verb, 'uri': self.uri, - 'unit_string': self.unit_string}) - self.error_message = msg - - def __call__(self, verb, url): - """Represents a call to this limit from a relevant request. - - @param verb: string http verb (POST, GET, etc.) - @param url: string URL - """ - if self.verb != verb or not re.match(self.regex, url): - return - - now = self._get_time() - - if self.last_request is None: - self.last_request = now - - leak_value = now - self.last_request - - self.water_level -= leak_value - self.water_level = max(self.water_level, 0) - self.water_level += self.request_value - - difference = self.water_level - self.capacity - - self.last_request = now - - if difference > 0: - self.water_level -= self.request_value - self.next_request = now + difference - return difference - - cap = self.capacity - water = self.water_level - val = self.value - - self.remaining = math.floor(((cap - water) / cap) * val) - self.next_request = now - - def _get_time(self): - """Retrieve the current time. Broken out for testability.""" - return time.time() - - def display_unit(self): - """Display the string name of the unit.""" - return self.UNITS.get(self.unit, "UNKNOWN") - - def display(self): - """Return a useful representation of this class.""" - return { - "verb": self.verb, - "URI": self.uri, - "regex": self.regex, - "value": self.value, - "remaining": int(self.remaining), - "unit": self.display_unit(), - "resetTime": int(self.next_request or self._get_time()), - } - -# "Limit" format is a dictionary with the HTTP verb, human-readable URI, -# a regular-expression to match, value and unit of measure (PER_DAY, etc.) - -DEFAULT_LIMITS = [ - Limit("POST", "*", ".*", 120, utils.TIME_UNITS['MINUTE']), - Limit("POST", "*/servers", "^/servers", 120, utils.TIME_UNITS['MINUTE']), - Limit("PUT", "*", ".*", 120, utils.TIME_UNITS['MINUTE']), - Limit("GET", "*changes-since*", ".*changes-since.*", 120, - utils.TIME_UNITS['MINUTE']), - Limit("DELETE", "*", ".*", 120, utils.TIME_UNITS['MINUTE']), - Limit("GET", "*/os-fping", "^/os-fping", 12, utils.TIME_UNITS['MINUTE']), -] - - -class RateLimitingMiddleware(base_wsgi.Middleware): - """Rate-limits requests passing through this middleware. All limit - information is stored in memory for this implementation. - """ - - def __init__(self, application, limits=None, limiter=None, **kwargs): - """Initialize new `RateLimitingMiddleware`. - - It wraps the given WSGI application and sets up the given limits. - - @param application: WSGI application to wrap - @param limits: String describing limits - @param limiter: String identifying class for representing limits - - Other parameters are passed to the constructor for the limiter. - """ - base_wsgi.Middleware.__init__(self, application) - - # Select the limiter class - if limiter is None: - limiter = Limiter - else: - limiter = importutils.import_class(limiter) - - # Parse the limits, if any are provided - if limits is not None: - limits = limiter.parse_limits(limits) - - self._limiter = limiter(limits or DEFAULT_LIMITS, **kwargs) - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - """Represents a single call through this middleware. - - We should record the request if we have a limit relevant to it. - If no limit is relevant to the request, ignore it. - - If the request should be rate limited, return a fault telling the user - they are over the limit and need to retry later. - """ - verb = req.method - url = req.url - context = req.environ.get("nova.context") - - if context: - username = context.user_id - else: - username = None - - delay, error = self._limiter.check_for_delay(verb, url, username) - - if delay: - msg = _("This request was rate-limited.") - retry = time.time() + delay - return wsgi.RateLimitFault(msg, error, retry) - - req.environ["nova.limits"] = self._limiter.get_limits(username) - - return self.application - - -class Limiter(object): - """Rate-limit checking class which handles limits in memory.""" - - def __init__(self, limits, **kwargs): - """Initialize the new `Limiter`. - - @param limits: List of `Limit` objects - """ - self.limits = copy.deepcopy(limits) - self.levels = collections.defaultdict(lambda: copy.deepcopy(limits)) - - # Pick up any per-user limit information - for key, value in kwargs.items(): - if key.startswith(LIMITS_PREFIX): - username = key[len(LIMITS_PREFIX):] - self.levels[username] = self.parse_limits(value) - - def get_limits(self, username=None): - """Return the limits for a given user.""" - return [limit.display() for limit in self.levels[username]] - - def check_for_delay(self, verb, url, username=None): - """Check the given verb/user/user triplet for limit. - - @return: Tuple of delay (in seconds) and error message (or None, None) - """ - delays = [] - - for limit in self.levels[username]: - delay = limit(verb, url) - if delay: - delays.append((delay, limit.error_message)) - - if delays: - delays.sort() - return delays[0] - - return None, None - - # Note: This method gets called before the class is instantiated, - # so this must be either a static method or a class method. It is - # used to develop a list of limits to feed to the constructor. We - # put this in the class so that subclasses can override the - # default limit parsing. - @staticmethod - def parse_limits(limits): - """Convert a string into a list of Limit instances. This - implementation expects a semicolon-separated sequence of - parenthesized groups, where each group contains a - comma-separated sequence consisting of HTTP method, - user-readable URI, a URI reg-exp, an integer number of - requests which can be made, and a unit of measure. Valid - values for the latter are "SECOND", "MINUTE", "HOUR", and - "DAY". - - @return: List of Limit instances. - """ - - # Handle empty limit strings - limits = limits.strip() - if not limits: - return [] - - # Split up the limits by semicolon - result = [] - for group in limits.split(';'): - group = group.strip() - if not group.startswith('(') or not group.endswith(')'): - raise ValueError("Limit rules must be surrounded by " - "parentheses") - group = group[1:-1] - - # Extract the Limit arguments - args = [a.strip() for a in group.split(',')] - if len(args) != 5: - raise ValueError("Limit rules must contain the following " - "arguments: verb, uri, regex, value, unit") - - # Pull out the arguments - verb, uri, regex, value, unit = args - - # Upper-case the verb - verb = verb.upper() - - # Convert value--raises ValueError if it's not integer - value = int(value) - - # Convert unit - unit = unit.upper() - if unit not in utils.TIME_UNITS: - raise ValueError("Invalid units specified") - unit = utils.TIME_UNITS[unit] - - # Build a limit - result.append(Limit(verb, uri, regex, value, unit)) - - return result - - -class WsgiLimiter(object): - """Rate-limit checking from a WSGI application. Uses an in-memory - `Limiter`. - - To use, POST ``/`` with JSON data such as:: - - { - "verb" : GET, - "path" : "/servers" - } - - and receive a 204 No Content, or a 403 Forbidden with an X-Wait-Seconds - header containing the number of seconds to wait before the action would - succeed. - """ - - def __init__(self, limits=None): - """Initialize the new `WsgiLimiter`. - - @param limits: List of `Limit` objects - """ - self._limiter = Limiter(limits or DEFAULT_LIMITS) - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, request): - """Handles a call to this application. - - Returns 204 if the request is acceptable to the limiter, else a 403 - is returned with a relevant header indicating when the request *will* - succeed. - """ - if request.method != "POST": - raise webob.exc.HTTPMethodNotAllowed() - - try: - info = dict(jsonutils.loads(request.body)) - except ValueError: - raise webob.exc.HTTPBadRequest() - - username = request.path_info_pop() - verb = info.get("verb") - path = info.get("path") - - delay, error = self._limiter.check_for_delay(verb, path, username) - - if delay: - headers = {"X-Wait-Seconds": "%.2f" % delay} - return webob.exc.HTTPForbidden(headers=headers, explanation=error) - else: - return webob.exc.HTTPNoContent() - - -class WsgiLimiterProxy(object): - """Rate-limit requests based on answers from a remote source.""" - - def __init__(self, limiter_address): - """Initialize the new `WsgiLimiterProxy`. - - @param limiter_address: IP/port combination of where to request limit - """ - self.limiter_address = limiter_address - - def check_for_delay(self, verb, path, username=None): - body = jsonutils.dumps({"verb": verb, "path": path}) - headers = {"Content-Type": "application/json"} - - conn = httplib.HTTPConnection(self.limiter_address) - - if username: - conn.request("POST", "/%s" % (username), body, headers) - else: - conn.request("POST", "/", body, headers) - - resp = conn.getresponse() - - if 200 >= resp.status < 300: - return None, None - - return resp.getheader("X-Wait-Seconds"), resp.read() or None - - # Note: This method gets called before the class is instantiated, - # so this must be either a static method or a class method. It is - # used to develop a list of limits to feed to the constructor. - # This implementation returns an empty list, since all limit - # decisions are made by a remote server. - @staticmethod - def parse_limits(limits): - """Ignore a limits string--simply doesn't apply for the limit - proxy. - - @return: Empty list. - """ - - return [] diff --git a/nova/api/openstack/compute/legacy_v2/server_metadata.py b/nova/api/openstack/compute/legacy_v2/server_metadata.py deleted file mode 100644 index a23dc9609cd..00000000000 --- a/nova/api/openstack/compute/legacy_v2/server_metadata.py +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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 six -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova.i18n import _ - - -class Controller(object): - """The server metadata API controller for the OpenStack API.""" - - def __init__(self): - self.compute_api = compute.API() - super(Controller, self).__init__() - - def _get_metadata(self, context, server_id): - try: - server = common.get_instance(self.compute_api, context, server_id) - meta = self.compute_api.get_instance_metadata(context, server) - except exception.InstanceNotFound: - msg = _('Server does not exist') - raise exc.HTTPNotFound(explanation=msg) - - meta_dict = {} - for key, value in six.iteritems(meta): - meta_dict[key] = value - return meta_dict - - def index(self, req, server_id): - """Returns the list of metadata for a given instance.""" - context = req.environ['nova.context'] - return {'metadata': self._get_metadata(context, server_id)} - - def create(self, req, server_id, body): - try: - metadata = body['metadata'] - except (KeyError, TypeError): - msg = _("Malformed request body") - raise exc.HTTPBadRequest(explanation=msg) - if not isinstance(metadata, dict): - msg = _("Malformed request body. metadata must be object") - raise exc.HTTPBadRequest(explanation=msg) - - context = req.environ['nova.context'] - - new_metadata = self._update_instance_metadata(context, - server_id, - metadata, - delete=False) - - return {'metadata': new_metadata} - - def update(self, req, server_id, id, body): - try: - meta_item = body['meta'] - except (TypeError, KeyError): - expl = _('Malformed request body') - raise exc.HTTPBadRequest(explanation=expl) - - if not isinstance(meta_item, dict): - msg = _("Malformed request body. meta item must be object") - raise exc.HTTPBadRequest(explanation=msg) - - if id not in meta_item: - expl = _('Request body and URI mismatch') - raise exc.HTTPBadRequest(explanation=expl) - - if len(meta_item) > 1: - expl = _('Request body contains too many items') - raise exc.HTTPBadRequest(explanation=expl) - - context = req.environ['nova.context'] - self._update_instance_metadata(context, - server_id, - meta_item, - delete=False) - - return {'meta': meta_item} - - def update_all(self, req, server_id, body): - try: - metadata = body['metadata'] - except (TypeError, KeyError): - expl = _('Malformed request body') - raise exc.HTTPBadRequest(explanation=expl) - - if not isinstance(metadata, dict): - msg = _("Malformed request body. metadata must be object") - raise exc.HTTPBadRequest(explanation=msg) - - context = req.environ['nova.context'] - new_metadata = self._update_instance_metadata(context, - server_id, - metadata, - delete=True) - - return {'metadata': new_metadata} - - def _update_instance_metadata(self, context, server_id, metadata, - delete=False): - try: - server = common.get_instance(self.compute_api, context, server_id) - return self.compute_api.update_instance_metadata(context, - server, - metadata, - delete) - - except exception.InstanceNotFound: - msg = _('Server does not exist') - raise exc.HTTPNotFound(explanation=msg) - - except (ValueError, AttributeError): - msg = _("Malformed request body") - raise exc.HTTPBadRequest(explanation=msg) - - except exception.InvalidMetadata as error: - raise exc.HTTPBadRequest(explanation=error.format_message()) - - except exception.InvalidMetadataSize as error: - raise exc.HTTPRequestEntityTooLarge( - explanation=error.format_message()) - - except exception.QuotaError as error: - raise exc.HTTPForbidden(explanation=error.format_message()) - - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'update metadata', server_id) - - def show(self, req, server_id, id): - """Return a single metadata item.""" - context = req.environ['nova.context'] - data = self._get_metadata(context, server_id) - - try: - return {'meta': {id: data[id]}} - except KeyError: - msg = _("Metadata item was not found") - raise exc.HTTPNotFound(explanation=msg) - - @wsgi.response(204) - def delete(self, req, server_id, id): - """Deletes an existing metadata.""" - context = req.environ['nova.context'] - - metadata = self._get_metadata(context, server_id) - - if id not in metadata: - msg = _("Metadata item was not found") - raise exc.HTTPNotFound(explanation=msg) - - server = common.get_instance(self.compute_api, context, server_id) - try: - self.compute_api.delete_instance_metadata(context, server, id) - - except exception.InstanceNotFound: - msg = _('Server does not exist') - raise exc.HTTPNotFound(explanation=msg) - - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'delete metadata', server_id) - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/nova/api/openstack/compute/legacy_v2/servers.py b/nova/api/openstack/compute/legacy_v2/servers.py deleted file mode 100644 index 47a6db362af..00000000000 --- a/nova/api/openstack/compute/legacy_v2/servers.py +++ /dev/null @@ -1,1171 +0,0 @@ -# Copyright 2010 OpenStack Foundation -# Copyright 2011 Piston Cloud Computing, 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 base64 -import re -import sys - -from oslo_config import cfg -from oslo_log import log as logging -import oslo_messaging as messaging -from oslo_utils import netutils -from oslo_utils import strutils -from oslo_utils import timeutils -from oslo_utils import uuidutils -import six -import webob -from webob import exc - -from nova.api.openstack import common -from nova.api.openstack.compute.views import servers as views_servers -from nova.api.openstack import wsgi -from nova import block_device -from nova import compute -from nova.compute import flavors -from nova import exception -from nova.i18n import _ -from nova import objects -from nova import policy -from nova import utils - - -server_opts = [ - cfg.BoolOpt('enable_instance_password', - default=True, - help='Enables returning of the instance password by the' - ' relevant server API calls such as create, rebuild' - ' or rescue, If the hypervisor does not support' - ' password injection then the password returned will' - ' not be correct'), -] -CONF = cfg.CONF -CONF.register_opts(server_opts) -CONF.import_opt('reclaim_instance_interval', 'nova.compute.manager') - -LOG = logging.getLogger(__name__) - -CREATE_EXCEPTIONS = { - exception.InvalidMetadataSize: exc.HTTPRequestEntityTooLarge, - exception.ImageNotFound: exc.HTTPBadRequest, - exception.FlavorNotFound: exc.HTTPBadRequest, - exception.KeypairNotFound: exc.HTTPBadRequest, - exception.ConfigDriveInvalidValue: exc.HTTPBadRequest, - exception.ImageNotActive: exc.HTTPBadRequest, - exception.FlavorDiskTooSmall: exc.HTTPBadRequest, - exception.FlavorMemoryTooSmall: exc.HTTPBadRequest, - exception.NetworkNotFound: exc.HTTPBadRequest, - exception.PortNotFound: exc.HTTPBadRequest, - exception.FixedIpAlreadyInUse: exc.HTTPBadRequest, - exception.SecurityGroupNotFound: exc.HTTPBadRequest, - exception.InstanceUserDataTooLarge: exc.HTTPBadRequest, - exception.InstanceUserDataMalformed: exc.HTTPBadRequest, - exception.ImageNUMATopologyIncomplete: exc.HTTPBadRequest, - exception.ImageNUMATopologyForbidden: exc.HTTPBadRequest, - exception.ImageNUMATopologyAsymmetric: exc.HTTPBadRequest, - exception.ImageNUMATopologyCPUOutOfRange: exc.HTTPBadRequest, - exception.ImageNUMATopologyCPUDuplicates: exc.HTTPBadRequest, - exception.ImageNUMATopologyCPUsUnassigned: exc.HTTPBadRequest, - exception.ImageNUMATopologyMemoryOutOfRange: exc.HTTPBadRequest, - exception.PortInUse: exc.HTTPConflict, - exception.InstanceExists: exc.HTTPConflict, - exception.NoUniqueMatch: exc.HTTPConflict, - exception.Invalid: exc.HTTPBadRequest, - exception.InstanceGroupNotFound: exc.HTTPBadRequest, -} - -CREATE_EXCEPTIONS_MSGS = { - exception.ImageNotFound: _("Can not find requested image"), - exception.FlavorNotFound: _("Invalid flavorRef provided."), - exception.KeypairNotFound: _("Invalid key_name provided."), - exception.ConfigDriveInvalidValue: _("Invalid config_drive provided."), -} - - -class Controller(wsgi.Controller): - """The Server API base controller class for the OpenStack API.""" - - _view_builder_class = views_servers.ViewBuilder - - @staticmethod - def _add_location(robj): - # Just in case... - if 'server' not in robj.obj: - return robj - - link = [l for l in robj.obj['server']['links'] if l['rel'] == 'self'] - if link: - robj['Location'] = utils.utf8(link[0]['href']) - - # Convenience return - return robj - - def __init__(self, ext_mgr=None, **kwargs): - super(Controller, self).__init__(**kwargs) - self.compute_api = compute.API() - self.ext_mgr = ext_mgr - - def index(self, req): - """Returns a list of server names and ids for a given user.""" - try: - servers = self._get_servers(req, is_detail=False) - except exception.Invalid as err: - raise exc.HTTPBadRequest(explanation=err.format_message()) - return servers - - def detail(self, req): - """Returns a list of server details for a given user.""" - try: - servers = self._get_servers(req, is_detail=True) - except exception.Invalid as err: - raise exc.HTTPBadRequest(explanation=err.format_message()) - return servers - - def _get_servers(self, req, is_detail): - """Returns a list of servers, based on any search options specified.""" - - search_opts = {} - search_opts.update(req.GET) - - context = req.environ['nova.context'] - remove_invalid_options(context, search_opts, - self._get_server_search_options()) - - # Verify search by 'status' contains a valid status. - # Convert it to filter by vm_state or task_state for compute_api. - search_opts.pop('status', None) - if 'status' in req.GET.keys(): - statuses = req.GET.getall('status') - states = common.task_and_vm_state_from_status(statuses) - vm_state, task_state = states - if not vm_state and not task_state: - return {'servers': []} - search_opts['vm_state'] = vm_state - # When we search by vm state, task state will return 'default'. - # So we don't need task_state search_opt. - if 'default' not in task_state: - search_opts['task_state'] = task_state - - if 'changes-since' in search_opts: - try: - parsed = timeutils.parse_isotime(search_opts['changes-since']) - except ValueError: - msg = _('Invalid changes-since value') - raise exc.HTTPBadRequest(explanation=msg) - search_opts['changes-since'] = parsed - - # By default, compute's get_all() will return deleted instances. - # If an admin hasn't specified a 'deleted' search option, we need - # to filter out deleted instances by setting the filter ourselves. - # ... Unless 'changes-since' is specified, because 'changes-since' - # should return recently deleted images according to the API spec. - - if 'deleted' not in search_opts: - if 'changes-since' not in search_opts: - # No 'changes-since', so we only want non-deleted servers - search_opts['deleted'] = False - else: - # Convert deleted filter value to a valid boolean. - # Return non-deleted servers if an invalid value - # is passed with deleted filter. - search_opts['deleted'] = strutils.bool_from_string( - search_opts['deleted'], default=False) - - if search_opts.get("vm_state") == ['deleted']: - if context.is_admin: - search_opts['deleted'] = True - else: - msg = _("Only administrators may list deleted instances") - raise exc.HTTPForbidden(explanation=msg) - - all_tenants = common.is_all_tenants(search_opts) - # use the boolean from here on out so remove the entry from search_opts - # if it's present - search_opts.pop('all_tenants', None) - - elevated = None - if all_tenants: - policy.enforce(context, 'compute:get_all_tenants', - {'project_id': context.project_id, - 'user_id': context.user_id}) - elevated = context.elevated() - else: - if context.project_id: - search_opts['project_id'] = context.project_id - else: - search_opts['user_id'] = context.user_id - - limit, marker = common.get_limit_and_marker(req) - # Sorting by multiple keys and directions is conditionally enabled - sort_keys, sort_dirs = None, None - if self.ext_mgr.is_loaded('os-server-sort-keys'): - sort_keys, sort_dirs = common.get_sort_params(req.params) - - expected_attrs = None - if is_detail: - # merge our expected attrs with what the view builder needs for - # showing details - expected_attrs = self._view_builder.get_show_expected_attrs( - expected_attrs) - - try: - instance_list = self.compute_api.get_all(elevated or context, - search_opts=search_opts, limit=limit, marker=marker, - want_objects=True, expected_attrs=expected_attrs, - sort_keys=sort_keys, sort_dirs=sort_dirs) - except exception.MarkerNotFound: - msg = _('marker [%s] not found') % marker - raise exc.HTTPBadRequest(explanation=msg) - except exception.FlavorNotFound: - LOG.debug("Flavor '%s' could not be found", search_opts['flavor']) - instance_list = objects.InstanceList() - - if is_detail: - instance_list._context = context - instance_list.fill_faults() - response = self._view_builder.detail(req, instance_list) - else: - response = self._view_builder.index(req, instance_list) - req.cache_db_instances(instance_list) - return response - - def _get_server(self, context, req, instance_uuid, is_detail=False): - """Utility function for looking up an instance by uuid. - - :param context: request context for auth - :param req: HTTP request. The instance is cached in this request. - :param instance_uuid: UUID of the server instance to get - :param is_detail: True if you plan on showing the details of the - instance in the response, False otherwise. - """ - expected_attrs = ['flavor'] - if is_detail: - expected_attrs = self._view_builder.get_show_expected_attrs( - expected_attrs) - instance = common.get_instance(self.compute_api, context, - instance_uuid, - expected_attrs=expected_attrs) - req.cache_db_instance(instance) - return instance - - def _check_string_length(self, value, name, max_length=None): - try: - if isinstance(value, six.string_types): - value = value.strip() - utils.check_string_length(value, name, min_length=1, - max_length=max_length) - except exception.InvalidInput as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - def _validate_server_name(self, value): - self._check_string_length(value, 'Server name', max_length=255) - - def _get_injected_files(self, personality): - """Create a list of injected files from the personality attribute. - - At this time, injected_files must be formatted as a list of - (file_path, file_content) pairs for compatibility with the - underlying compute service. - """ - injected_files = [] - - for item in personality: - try: - path = item['path'] - contents = item['contents'] - except KeyError as key: - expl = _('Bad personality format: missing %s') % key - raise exc.HTTPBadRequest(explanation=expl) - except TypeError: - expl = _('Bad personality format') - raise exc.HTTPBadRequest(explanation=expl) - if self._decode_base64(contents) is None: - expl = _('Personality content for %s cannot be decoded') % path - raise exc.HTTPBadRequest(explanation=expl) - injected_files.append((path, contents)) - return injected_files - - def _get_requested_networks(self, requested_networks): - """Create a list of requested networks from the networks attribute.""" - networks = [] - network_uuids = [] - for network in requested_networks: - request = objects.NetworkRequest() - try: - try: - request.port_id = network.get('port', None) - except ValueError: - msg = _("Bad port format: port uuid is " - "not in proper format " - "(%s)") % network.get('port') - raise exc.HTTPBadRequest(explanation=msg) - if request.port_id: - request.network_id = None - if not utils.is_neutron(): - # port parameter is only for neutron v2.0 - msg = _("Unknown argument : port") - raise exc.HTTPBadRequest(explanation=msg) - else: - request.network_id = network['uuid'] - - if (not request.port_id and not - uuidutils.is_uuid_like(request.network_id)): - br_uuid = request.network_id.split('-', 1)[-1] - if not uuidutils.is_uuid_like(br_uuid): - msg = _("Bad networks format: network uuid is " - "not in proper format " - "(%s)") % request.network_id - raise exc.HTTPBadRequest(explanation=msg) - - # fixed IP address is optional - # if the fixed IP address is not provided then - # it will use one of the available IP address from the network - try: - request.address = network.get('fixed_ip', None) - except ValueError: - msg = (_("Invalid fixed IP address (%s)") % - network.get('fixed_ip')) - raise exc.HTTPBadRequest(explanation=msg) - - # duplicate networks are allowed only for neutron v2.0 - if (not utils.is_neutron() and request.network_id and - request.network_id in network_uuids): - expl = (_("Duplicate networks" - " (%s) are not allowed") % - request.network_id) - raise exc.HTTPBadRequest(explanation=expl) - network_uuids.append(request.network_id) - networks.append(request) - except KeyError as key: - expl = _('Bad network format: missing %s') % key - raise exc.HTTPBadRequest(explanation=expl) - except TypeError: - expl = _('Bad networks format') - raise exc.HTTPBadRequest(explanation=expl) - - return objects.NetworkRequestList(objects=networks) - - # NOTE(vish): Without this regex, b64decode will happily - # ignore illegal bytes in the base64 encoded - # data. - B64_REGEX = re.compile('^(?:[A-Za-z0-9+\/]{4})*' - '(?:[A-Za-z0-9+\/]{2}==' - '|[A-Za-z0-9+\/]{3}=)?$') - - def _decode_base64(self, data): - if isinstance(data, six.binary_type) and hasattr(data, "decode"): - try: - data = data.decode("utf-8") - except UnicodeDecodeError: - return None - data = re.sub(r'\s', '', data) - if not self.B64_REGEX.match(data): - return None - try: - return base64.b64decode(data) - except TypeError: - return None - - def _validate_access_ipv4(self, address): - if not netutils.is_valid_ipv4(address): - expl = _('accessIPv4 is not proper IPv4 format') - raise exc.HTTPBadRequest(explanation=expl) - - def _validate_access_ipv6(self, address): - if not netutils.is_valid_ipv6(address): - expl = _('accessIPv6 is not proper IPv6 format') - raise exc.HTTPBadRequest(explanation=expl) - - def show(self, req, id): - """Returns server details by server id.""" - context = req.environ['nova.context'] - instance = self._get_server(context, req, id, is_detail=True) - return self._view_builder.show(req, instance) - - def _extract(self, server_dict, ext_name, key): - if self.ext_mgr.is_loaded(ext_name): - return server_dict.get(key) - return None - - def _validate_user_data(self, user_data): - if user_data and self._decode_base64(user_data) is None: - expl = _('Userdata content cannot be decoded') - raise exc.HTTPBadRequest(explanation=expl) - return user_data - - def _extract_bdm(self, server_dict, image_uuid_specified): - legacy_bdm = True - block_device_mapping_v2 = None - if not self.ext_mgr.is_loaded('os-volumes'): - return legacy_bdm, None - block_device_mapping = server_dict.get('block_device_mapping', []) - if not isinstance(block_device_mapping, list): - msg = _('block_device_mapping must be a list') - raise exc.HTTPBadRequest(explanation=msg) - for bdm in block_device_mapping: - try: - block_device.validate_device_name(bdm.get("device_name")) - block_device.validate_and_default_volume_size(bdm) - except exception.InvalidBDMFormat as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - if 'delete_on_termination' in bdm: - bdm['delete_on_termination'] = strutils.bool_from_string( - bdm['delete_on_termination']) - - if self.ext_mgr.is_loaded('os-block-device-mapping-v2-boot'): - # Consider the new data format for block device mapping - block_device_mapping_v2 = server_dict.get( - 'block_device_mapping_v2', []) - # NOTE (ndipanov): Disable usage of both legacy and new - # block device format in the same request - if block_device_mapping and block_device_mapping_v2: - expl = _('Using different block_device_mapping syntaxes ' - 'is not allowed in the same request.') - raise exc.HTTPBadRequest(explanation=expl) - - if not isinstance(block_device_mapping_v2, list): - msg = _('block_device_mapping_v2 must be a list') - raise exc.HTTPBadRequest(explanation=msg) - - # Assume legacy format - legacy_bdm = not bool(block_device_mapping_v2) - - try: - block_device_mapping_v2 = [ - block_device.BlockDeviceDict.from_api(bdm_dict, - image_uuid_specified) - for bdm_dict in block_device_mapping_v2] - except exception.InvalidBDMFormat as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - bdm = (block_device_mapping or block_device_mapping_v2) - return legacy_bdm, bdm - - @staticmethod - def _resolve_exception(matches): - """We want the most specific exception class.""" - while len(matches) > 1: - first = matches[0] - second = matches[1] - if issubclass(first, second): - del matches[1] - else: - del matches[0] - return matches[0] - - @staticmethod - def _handle_create_exception(*exc_info): - """The `CREATE_EXCEPTIONS` dict containing the relationships between - the nova exceptions and the webob exception classes to be raised is - defined at the top of this file. - """ - error = exc_info[1] - err_cls = error.__class__ - cls_to_raise = CREATE_EXCEPTIONS.get(err_cls) - if cls_to_raise is None: - # The error is a subclass of one of the dict keys - to_raise = [val for key, val in CREATE_EXCEPTIONS.items() - if isinstance(error, key)] - if len(to_raise) > 1: - cls_to_raise = Controller._resolve_exception(to_raise) - elif not to_raise: - # Not any of the expected exceptions, so re-raise - six.reraise(*exc_info) - else: - cls_to_raise = to_raise[0] - - for key, val in CREATE_EXCEPTIONS_MSGS.items(): - if isinstance(error, key): - raise cls_to_raise(explanation=CREATE_EXCEPTIONS_MSGS[key]) - raise cls_to_raise(explanation=error.format_message()) - - def _determine_requested_networks(self, server_dict): - requested_networks = None - if (self.ext_mgr.is_loaded('os-networks') - or utils.is_neutron()): - requested_networks = server_dict.get('networks') - - if requested_networks is not None: - if not isinstance(requested_networks, list): - expl = _('Bad networks format') - raise exc.HTTPBadRequest(explanation=expl) - requested_networks = self._get_requested_networks( - requested_networks) - return requested_networks - - @wsgi.response(202) - def create(self, req, body): - """Creates a new server for a given user.""" - if not self.is_valid_body(body, 'server'): - raise exc.HTTPUnprocessableEntity() - - context = req.environ['nova.context'] - server_dict = body['server'] - password = self._get_server_admin_password(server_dict) - - if 'name' not in server_dict: - msg = _("Server name is not defined") - raise exc.HTTPBadRequest(explanation=msg) - - name = server_dict['name'] - self._validate_server_name(name) - name = name.strip() - - image_uuid = self._image_from_req_data(body) - - personality = server_dict.get('personality') - config_drive = None - if self.ext_mgr.is_loaded('os-config-drive'): - config_drive = server_dict.get('config_drive') - - injected_files = [] - if personality: - injected_files = self._get_injected_files(personality) - - sg_names = [] - if self.ext_mgr.is_loaded('os-security-groups'): - security_groups = server_dict.get('security_groups') - if security_groups is not None: - try: - sg_names = [sg['name'] for sg in security_groups - if sg.get('name')] - except AttributeError: - msg = _("Invalid input for field/attribute %(path)s." - " Value: %(value)s. %(message)s") % { - 'path': 'security_groups', - 'value': security_groups, - 'message': '' - } - raise exc.HTTPBadRequest(explanation=msg) - if not sg_names: - sg_names.append('default') - - sg_names = list(set(sg_names)) - - requested_networks = self._determine_requested_networks(server_dict) - - (access_ip_v4, ) = server_dict.get('accessIPv4'), - if access_ip_v4 is not None: - self._validate_access_ipv4(access_ip_v4) - - (access_ip_v6, ) = server_dict.get('accessIPv6'), - if access_ip_v6 is not None: - self._validate_access_ipv6(access_ip_v6) - - flavor_id = self._flavor_id_from_req_data(body) - - # optional openstack extensions: - key_name = self._extract(server_dict, 'os-keypairs', 'key_name') - availability_zone = self._extract(server_dict, 'os-availability-zone', - 'availability_zone') - user_data = self._extract(server_dict, 'os-user-data', 'user_data') - self._validate_user_data(user_data) - - image_uuid_specified = bool(image_uuid) - legacy_bdm, block_device_mapping = self._extract_bdm(server_dict, - image_uuid_specified) - - ret_resv_id = False - # min_count and max_count are optional. If they exist, they may come - # in as strings. Verify that they are valid integers and > 0. - # Also, we want to default 'min_count' to 1, and default - # 'max_count' to be 'min_count'. - min_count = 1 - max_count = 1 - if self.ext_mgr.is_loaded('os-multiple-create'): - ret_resv_id = server_dict.get('return_reservation_id', False) - min_count = server_dict.get('min_count', 1) - max_count = server_dict.get('max_count', min_count) - - try: - min_count = utils.validate_integer( - min_count, "min_count", min_value=1) - max_count = utils.validate_integer( - max_count, "max_count", min_value=1) - except exception.InvalidInput as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - - if min_count > max_count: - msg = _('min_count must be <= max_count') - raise exc.HTTPBadRequest(explanation=msg) - - auto_disk_config = False - if self.ext_mgr.is_loaded('OS-DCF'): - auto_disk_config = server_dict.get('auto_disk_config') - - scheduler_hints = {} - if self.ext_mgr.is_loaded('OS-SCH-HNT'): - scheduler_hints = server_dict.get('scheduler_hints', {}) - parse_az = self.compute_api.parse_availability_zone - availability_zone, host, node = parse_az(context, availability_zone) - - check_server_group_quota = self.ext_mgr.is_loaded( - 'os-server-group-quotas') - try: - _get_inst_type = flavors.get_flavor_by_flavor_id - inst_type = _get_inst_type(flavor_id, ctxt=context, - read_deleted="no") - - (instances, resv_id) = self.compute_api.create(context, - inst_type, - image_uuid, - display_name=name, - display_description=name, - key_name=key_name, - metadata=server_dict.get('metadata', {}), - access_ip_v4=access_ip_v4, - access_ip_v6=access_ip_v6, - injected_files=injected_files, - admin_password=password, - min_count=min_count, - max_count=max_count, - requested_networks=requested_networks, - security_group=sg_names, - user_data=user_data, - availability_zone=availability_zone, - forced_host=host, forced_node=node, - config_drive=config_drive, - block_device_mapping=block_device_mapping, - auto_disk_config=auto_disk_config, - scheduler_hints=scheduler_hints, - legacy_bdm=legacy_bdm, - check_server_group_quota=check_server_group_quota) - except (exception.QuotaError, - exception.PortLimitExceeded) as error: - raise exc.HTTPForbidden( - explanation=error.format_message()) - except messaging.RemoteError as err: - msg = "%(err_type)s: %(err_msg)s" % {'err_type': err.exc_type, - 'err_msg': err.value} - raise exc.HTTPBadRequest(explanation=msg) - except UnicodeDecodeError as error: - msg = "UnicodeError: %s" % error - raise exc.HTTPBadRequest(explanation=msg) - except Exception: - # The remaining cases can be handled in a standard fashion. - self._handle_create_exception(*sys.exc_info()) - - # If the caller wanted a reservation_id, return it - if ret_resv_id: - return wsgi.ResponseObject({'reservation_id': resv_id}) - - req.cache_db_instances(instances) - server = self._view_builder.create(req, instances[0]) - - if CONF.enable_instance_password: - server['server']['adminPass'] = password - - robj = wsgi.ResponseObject(server) - - return self._add_location(robj) - - def _delete(self, context, req, instance_uuid): - instance = self._get_server(context, req, instance_uuid) - if CONF.reclaim_instance_interval: - try: - self.compute_api.soft_delete(context, instance) - except exception.InstanceInvalidState: - # Note(yufang521247): instance which has never been active - # is not allowed to be soft_deleted. Thus we have to call - # delete() to clean up the instance. - self.compute_api.delete(context, instance) - else: - self.compute_api.delete(context, instance) - - def update(self, req, id, body): - """Update server then pass on to version-specific controller.""" - if not self.is_valid_body(body, 'server'): - raise exc.HTTPUnprocessableEntity() - - ctxt = req.environ['nova.context'] - update_dict = {} - - if 'name' in body['server']: - name = body['server']['name'] - self._validate_server_name(name) - update_dict['display_name'] = name.strip() - - if 'accessIPv4' in body['server']: - access_ipv4 = body['server']['accessIPv4'] - if access_ipv4: - self._validate_access_ipv4(access_ipv4) - update_dict['access_ip_v4'] = ( - access_ipv4 and access_ipv4.strip() or None) - - if 'accessIPv6' in body['server']: - access_ipv6 = body['server']['accessIPv6'] - if access_ipv6: - self._validate_access_ipv6(access_ipv6) - update_dict['access_ip_v6'] = ( - access_ipv6 and access_ipv6.strip() or None) - - if 'auto_disk_config' in body['server']: - auto_disk_config = strutils.bool_from_string( - body['server']['auto_disk_config']) - update_dict['auto_disk_config'] = auto_disk_config - - if 'hostId' in body['server']: - msg = _("HostId cannot be updated.") - raise exc.HTTPBadRequest(explanation=msg) - - if 'personality' in body['server']: - msg = _("Personality cannot be updated.") - raise exc.HTTPBadRequest(explanation=msg) - - instance = self._get_server(ctxt, req, id, is_detail=True) - try: - policy.enforce(ctxt, 'compute:update', instance) - instance.update(update_dict) - # Note instance.save can throw a NotFound exception - instance.save() - except exception.NotFound: - msg = _("Instance could not be found") - raise exc.HTTPNotFound(explanation=msg) - - return self._view_builder.show(req, instance) - - @wsgi.response(204) - @wsgi.action('confirmResize') - def _action_confirm_resize(self, req, id, body): - context = req.environ['nova.context'] - instance = self._get_server(context, req, id) - try: - self.compute_api.confirm_resize(context, instance) - except exception.MigrationNotFound: - msg = _("Instance has not been resized.") - raise exc.HTTPBadRequest(explanation=msg) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'confirmResize', id) - - @wsgi.response(202) - @wsgi.action('revertResize') - def _action_revert_resize(self, req, id, body): - context = req.environ['nova.context'] - instance = self._get_server(context, req, id) - try: - self.compute_api.revert_resize(context, instance) - except exception.MigrationNotFound: - msg = _("Instance has not been resized.") - raise exc.HTTPBadRequest(explanation=msg) - except exception.FlavorNotFound: - msg = _("Flavor used by the instance could not be found.") - raise exc.HTTPBadRequest(explanation=msg) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'revertResize', id) - return webob.Response(status_int=202) - - @wsgi.response(202) - @wsgi.action('reboot') - def _action_reboot(self, req, id, body): - if 'reboot' in body and 'type' in body['reboot']: - if not isinstance(body['reboot']['type'], six.string_types): - msg = _("Argument 'type' for reboot must be a string") - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - valid_reboot_types = ['HARD', 'SOFT'] - reboot_type = body['reboot']['type'].upper() - if not valid_reboot_types.count(reboot_type): - msg = _("Argument 'type' for reboot is not HARD or SOFT") - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - else: - msg = _("Missing argument 'type' for reboot") - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - context = req.environ['nova.context'] - instance = self._get_server(context, req, id) - - try: - self.compute_api.reboot(context, instance, reboot_type) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'reboot', id) - return webob.Response(status_int=202) - - def _resize(self, req, instance_id, flavor_id, **kwargs): - """Begin the resize process with given instance/flavor.""" - context = req.environ["nova.context"] - instance = self._get_server(context, req, instance_id) - try: - self.compute_api.resize(context, instance, flavor_id, **kwargs) - except exception.QuotaError as error: - raise exc.HTTPForbidden( - explanation=error.format_message()) - except exception.FlavorNotFound: - msg = _("Unable to locate requested flavor.") - raise exc.HTTPBadRequest(explanation=msg) - except exception.CannotResizeToSameFlavor: - msg = _("Resize requires a flavor change.") - raise exc.HTTPBadRequest(explanation=msg) - except exception.CannotResizeDisk as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'resize', instance_id) - except exception.ImageNotAuthorized: - msg = _("You are not authorized to access the image " - "the instance was started with.") - raise exc.HTTPUnauthorized(explanation=msg) - except exception.ImageNotFound: - msg = _("Image that the instance was started " - "with could not be found.") - raise exc.HTTPBadRequest(explanation=msg) - except (exception.NoValidHost, - exception.AutoDiskConfigDisabledByImage) as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) - except exception.Invalid: - msg = _("Invalid instance image.") - raise exc.HTTPBadRequest(explanation=msg) - - return webob.Response(status_int=202) - - @wsgi.response(204) - def delete(self, req, id): - """Destroys a server.""" - try: - self._delete(req.environ['nova.context'], req, id) - except exception.NotFound: - msg = _("Instance could not be found") - raise exc.HTTPNotFound(explanation=msg) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'delete', id) - - def _image_ref_from_req_data(self, data): - try: - return six.text_type(data['server']['imageRef']) - except (TypeError, KeyError): - msg = _("Missing imageRef attribute") - raise exc.HTTPBadRequest(explanation=msg) - - def _image_uuid_from_href(self, image_href): - if not image_href: - msg = _("Invalid imageRef provided.") - raise exc.HTTPBadRequest(explanation=msg) - - # If the image href was generated by nova api, strip image_href - # down to an id and use the default glance connection params - image_uuid = image_href.split('/').pop() - - if not uuidutils.is_uuid_like(image_uuid): - msg = _("Invalid imageRef provided.") - raise exc.HTTPBadRequest(explanation=msg) - - return image_uuid - - def _image_from_req_data(self, data): - """Get image data from the request or raise appropriate - exceptions - - If no image is supplied - checks to see if there is - block devices set and proper extesions loaded. - """ - image_ref = data['server'].get('imageRef') - bdm = data['server'].get('block_device_mapping') - bdm_v2 = data['server'].get('block_device_mapping_v2') - - if (not image_ref and ( - (bdm and self.ext_mgr.is_loaded('os-volumes')) or - (bdm_v2 and - self.ext_mgr.is_loaded('os-block-device-mapping-v2-boot')))): - return '' - else: - image_href = self._image_ref_from_req_data(data) - image_uuid = self._image_uuid_from_href(image_href) - return image_uuid - - def _flavor_id_from_req_data(self, data): - try: - flavor_ref = data['server']['flavorRef'] - except (TypeError, KeyError): - msg = _("Missing flavorRef attribute") - raise exc.HTTPBadRequest(explanation=msg) - try: - return common.get_id_from_href(flavor_ref) - except ValueError: - msg = _("Invalid flavorRef provided.") - raise exc.HTTPBadRequest(explanation=msg) - - @wsgi.response(202) - @wsgi.action('changePassword') - def _action_change_password(self, req, id, body): - context = req.environ['nova.context'] - if (not body.get('changePassword') - or 'adminPass' not in body['changePassword']): - msg = _("No adminPass was specified") - raise exc.HTTPBadRequest(explanation=msg) - password = self._get_server_admin_password(body['changePassword']) - - server = self._get_server(context, req, id) - try: - self.compute_api.set_admin_password(context, server, password) - except exception.InstancePasswordSetFailed as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as e: - raise common.raise_http_conflict_for_instance_invalid_state( - e, 'changePassword', id) - except NotImplementedError: - msg = _("Unable to set password on instance") - raise exc.HTTPNotImplemented(explanation=msg) - return webob.Response(status_int=202) - - def _validate_metadata(self, metadata): - """Ensure that we can work with the metadata given.""" - try: - six.iteritems(metadata) - except AttributeError: - msg = _("Unable to parse metadata key/value pairs.") - LOG.debug(msg) - raise exc.HTTPBadRequest(explanation=msg) - - @wsgi.response(202) - @wsgi.action('resize') - def _action_resize(self, req, id, body): - """Resizes a given instance to the flavor size requested.""" - try: - flavor_ref = str(body["resize"]["flavorRef"]) - if not flavor_ref: - msg = _("Resize request has invalid 'flavorRef' attribute.") - raise exc.HTTPBadRequest(explanation=msg) - except (KeyError, TypeError): - msg = _("Resize requests require 'flavorRef' attribute.") - raise exc.HTTPBadRequest(explanation=msg) - - kwargs = {} - if 'auto_disk_config' in body['resize']: - kwargs['auto_disk_config'] = body['resize']['auto_disk_config'] - - return self._resize(req, id, flavor_ref, **kwargs) - - @wsgi.response(202) - @wsgi.action('rebuild') - def _action_rebuild(self, req, id, body): - """Rebuild an instance with the given attributes.""" - body = body['rebuild'] - - try: - image_href = body["imageRef"] - except (KeyError, TypeError): - msg = _("Could not parse imageRef from request.") - raise exc.HTTPBadRequest(explanation=msg) - - image_href = self._image_uuid_from_href(image_href) - - password = self._get_server_admin_password(body) - - context = req.environ['nova.context'] - instance = self._get_server(context, req, id) - - attr_map = { - 'personality': 'files_to_inject', - 'name': 'display_name', - 'accessIPv4': 'access_ip_v4', - 'accessIPv6': 'access_ip_v6', - 'metadata': 'metadata', - 'auto_disk_config': 'auto_disk_config', - } - - kwargs = {} - - # take the preserve_ephemeral value into account only when the - # corresponding extension is active - if (self.ext_mgr.is_loaded('os-preserve-ephemeral-rebuild') - and 'preserve_ephemeral' in body): - kwargs['preserve_ephemeral'] = strutils.bool_from_string( - body['preserve_ephemeral'], strict=True) - - if 'accessIPv4' in body: - self._validate_access_ipv4(body['accessIPv4']) - - if 'accessIPv6' in body: - self._validate_access_ipv6(body['accessIPv6']) - - if 'name' in body: - self._validate_server_name(body['name']) - - for request_attribute, instance_attribute in attr_map.items(): - try: - kwargs[instance_attribute] = body[request_attribute] - except (KeyError, TypeError): - pass - - self._validate_metadata(kwargs.get('metadata', {})) - - if 'files_to_inject' in kwargs: - personality = kwargs.pop('files_to_inject') - files_to_inject = self._get_injected_files(personality) - else: - files_to_inject = None - - try: - self.compute_api.rebuild(context, - instance, - image_href, - password, - files_to_inject=files_to_inject, - **kwargs) - except exception.InstanceIsLocked as e: - raise exc.HTTPConflict(explanation=e.format_message()) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'rebuild', id) - except exception.InstanceNotFound: - msg = _("Instance could not be found") - raise exc.HTTPNotFound(explanation=msg) - except exception.InvalidMetadataSize as error: - raise exc.HTTPRequestEntityTooLarge( - explanation=error.format_message()) - except exception.ImageNotFound: - msg = _("Cannot find image for rebuild") - raise exc.HTTPBadRequest(explanation=msg) - except exception.QuotaError as error: - raise exc.HTTPForbidden(explanation=error.format_message()) - except (exception.ImageNotActive, - exception.FlavorDiskTooSmall, - exception.FlavorMemoryTooSmall, - exception.InvalidMetadata, - exception.AutoDiskConfigDisabledByImage) as error: - raise exc.HTTPBadRequest(explanation=error.format_message()) - - instance = self._get_server(context, req, id, is_detail=True) - - view = self._view_builder.show(req, instance) - - # Add on the adminPass attribute since the view doesn't do it - # unless instance passwords are disabled - if CONF.enable_instance_password: - view['server']['adminPass'] = password - - robj = wsgi.ResponseObject(view) - return self._add_location(robj) - - @wsgi.response(202) - @wsgi.action('createImage') - @common.check_snapshots_enabled - def _action_create_image(self, req, id, body): - """Snapshot a server instance.""" - context = req.environ['nova.context'] - entity = body.get("createImage", {}) - - image_name = entity.get("name") - - if not image_name: - msg = _("createImage entity requires name attribute") - raise exc.HTTPBadRequest(explanation=msg) - - props = {} - metadata = entity.get('metadata', {}) - common.check_img_metadata_properties_quota(context, metadata) - try: - props.update(metadata) - except ValueError: - msg = _("Invalid metadata") - raise exc.HTTPBadRequest(explanation=msg) - - instance = self._get_server(context, req, id) - - bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( - context, instance.uuid) - - try: - if self.compute_api.is_volume_backed_instance(context, instance, - bdms): - policy.enforce(context, - 'compute:snapshot_volume_backed', - {'project_id': context.project_id, - 'user_id': context.user_id}) - image = self.compute_api.snapshot_volume_backed( - context, - instance, - image_name, - extra_properties=props) - else: - image = self.compute_api.snapshot(context, - instance, - image_name, - extra_properties=props) - except exception.InstanceInvalidState as state_error: - common.raise_http_conflict_for_instance_invalid_state(state_error, - 'createImage', id) - except exception.Invalid as err: - raise exc.HTTPBadRequest(explanation=err.format_message()) - - # build location of newly-created image entity - image_id = str(image['id']) - url_prefix = self._view_builder._update_glance_link_prefix( - req.application_url) - image_ref = common.url_join(url_prefix, - context.project_id, - 'images', - image_id) - - resp = webob.Response(status_int=202) - resp.headers['Location'] = image_ref - return resp - - def _get_server_admin_password(self, server): - """Determine the admin password for a server on creation.""" - try: - password = server['adminPass'] - self._validate_admin_password(password) - except KeyError: - password = utils.generate_password() - except ValueError: - raise exc.HTTPBadRequest(explanation=_("Invalid adminPass")) - - return password - - def _validate_admin_password(self, password): - if not isinstance(password, six.string_types): - raise ValueError() - - def _get_server_search_options(self): - """Return server search options allowed by non-admin.""" - return ('reservation_id', 'name', 'status', 'image', 'flavor', - 'ip', 'changes-since', 'all_tenants') - - -def create_resource(ext_mgr): - return wsgi.Resource(Controller(ext_mgr)) - - -def remove_invalid_options(context, search_options, allowed_search_options): - """Remove search options that are not valid for non-admin API/context.""" - if context.is_admin: - # Only remove parameters for sorting and pagination - for key in ('sort_key', 'sort_dir', 'limit', 'marker'): - search_options.pop(key, None) - return - # Otherwise, strip out all unknown options - unknown_options = [opt for opt in search_options - if opt not in allowed_search_options] - LOG.debug("Removing options '%s' from query", - ", ".join(unknown_options)) - for opt in unknown_options: - search_options.pop(opt, None) diff --git a/nova/api/openstack/compute/legacy_v2/versions.py b/nova/api/openstack/compute/legacy_v2/versions.py deleted file mode 100644 index 6c36ea03764..00000000000 --- a/nova/api/openstack/compute/legacy_v2/versions.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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. - -from nova.api.openstack.compute import versions -from nova.api.openstack.compute.views import versions as views_versions -from nova.api.openstack import wsgi - - -class VersionV2(object): - def show(self, req): - builder = views_versions.get_view_builder(req) - return builder.build_version(versions.VERSIONS['v2.0']) - - -def create_resource(): - return wsgi.Resource(VersionV2()) diff --git a/nova/api/openstack/compute/limits.py b/nova/api/openstack/compute/limits.py index 9a9569afd27..49e5a98febd 100644 --- a/nova/api/openstack/compute/limits.py +++ b/nova/api/openstack/compute/limits.py @@ -13,55 +13,53 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.api.openstack.compute.legacy_v2 import limits +from nova.api.openstack.api_version_request \ + import MAX_IMAGE_META_PROXY_API_VERSION +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION +from nova.api.openstack.api_version_request \ + import MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION +from nova.api.openstack.api_version_request \ + import MIN_WITHOUT_PROXY_API_SUPPORT_VERSION from nova.api.openstack.compute.views import limits as limits_views from nova.api.openstack import extensions from nova.api.openstack import wsgi +from nova.policies import limits as limits_policies from nova import quota -# NOTE(alex_xu): This is just for keeping backward compatible with v2 endpoint -# in api-paste.ini. This will be removed after v2 API code deprecated in the -# future. -RateLimitingMiddleware = limits.RateLimitingMiddleware - QUOTAS = quota.QUOTAS -ALIAS = 'limits' -authorize = extensions.os_compute_authorizer(ALIAS) class LimitsController(wsgi.Controller): """Controller for accessing limits in the OpenStack API.""" + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) + @extensions.expected_errors(()) + def index(self, req): + return self._index(req) + + @wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, # noqa + MAX_IMAGE_META_PROXY_API_VERSION) # noqa + @extensions.expected_errors(()) + def index(self, req): + return self._index(req, filter_result=True) + + @wsgi.Controller.api_version( # noqa + MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION) # noqa @extensions.expected_errors(()) def index(self, req): - """Return all global and rate limit information.""" + return self._index(req, filter_result=True, max_image_meta=False) + + def _index(self, req, filter_result=False, max_image_meta=True): + """Return all global limit information.""" context = req.environ['nova.context'] - authorize(context) + context.can(limits_policies.BASE_POLICY_NAME) project_id = req.params.get('tenant_id', context.project_id) quotas = QUOTAS.get_project_quotas(context, project_id, usages=False) abs_limits = {k: v['limit'] for k, v in quotas.items()} - rate_limits = req.environ.get("nova.limits", []) - - builder = self._get_view_builder(req) - return builder.build(rate_limits, abs_limits) - - def _get_view_builder(self, req): - return limits_views.ViewBuilderV21() - - -class Limits(extensions.V21APIExtensionBase): - """Limits support.""" - - name = "Limits" - alias = ALIAS - version = 1 - - def get_resources(self): - resource = [extensions.ResourceExtension(ALIAS, - LimitsController())] - return resource - def get_controller_extensions(self): - return [] + builder = limits_views.ViewBuilder() + return builder.build(abs_limits, filter_result=filter_result, + max_image_meta=max_image_meta) diff --git a/nova/api/openstack/compute/lock_server.py b/nova/api/openstack/compute/lock_server.py index 2c29add9e9a..ed616a596f1 100644 --- a/nova/api/openstack/compute/lock_server.py +++ b/nova/api/openstack/compute/lock_server.py @@ -17,16 +17,13 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute - -ALIAS = "os-lock-server" - -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import lock_server as ls_policies class LockServerController(wsgi.Controller): def __init__(self, *args, **kwargs): super(LockServerController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() @wsgi.response(202) @extensions.expected_errors(404) @@ -34,8 +31,10 @@ def __init__(self, *args, **kwargs): def _lock(self, req, id, body): """Lock a server instance.""" context = req.environ['nova.context'] - authorize(context, action='lock') instance = common.get_instance(self.compute_api, context, id) + context.can(ls_policies.POLICY_ROOT % 'lock', + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) self.compute_api.lock(context, instance) @wsgi.response(202) @@ -44,26 +43,10 @@ def _lock(self, req, id, body): def _unlock(self, req, id, body): """Unlock a server instance.""" context = req.environ['nova.context'] - authorize(context, action='unlock') + context.can(ls_policies.POLICY_ROOT % 'unlock') instance = common.get_instance(self.compute_api, context, id) if not self.compute_api.is_expected_locked_by(context, instance): - authorize(context, target=instance, - action='unlock:unlock_override') + context.can(ls_policies.POLICY_ROOT % 'unlock:unlock_override', + instance) self.compute_api.unlock(context, instance) - - -class LockServer(extensions.V21APIExtensionBase): - """Enable lock/unlock server actions.""" - - name = "LockServer" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = LockServerController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/migrate_server.py b/nova/api/openstack/compute/migrate_server.py index b1f4a5e7029..bd6aae2bc22 100644 --- a/nova/api/openstack/compute/migrate_server.py +++ b/nova/api/openstack/compute/migrate_server.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import log as logging +from oslo_utils import excutils from oslo_utils import strutils from webob import exc @@ -24,17 +26,16 @@ from nova.api import validation from nova import compute from nova import exception +from nova.i18n import _ +from nova.policies import migrate_server as ms_policies -ALIAS = "os-migrate-server" - - -authorize = extensions.os_compute_authorizer(ALIAS) +LOG = logging.getLogger(__name__) class MigrateServerController(wsgi.Controller): def __init__(self, *args, **kwargs): super(MigrateServerController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() @wsgi.response(202) @extensions.expected_errors((400, 403, 404, 409)) @@ -42,7 +43,7 @@ def __init__(self, *args, **kwargs): def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host.""" context = req.environ['nova.context'] - authorize(context, action='migrate') + context.can(ms_policies.POLICY_ROOT % 'migrate') instance = common.get_instance(self.compute_api, context, id) try: @@ -62,16 +63,20 @@ def _migrate(self, req, id, body): @wsgi.response(202) @extensions.expected_errors((400, 404, 409)) @wsgi.action('os-migrateLive') - @validation.schema(migrate_server.migrate_live, "2.1", "2.24") - @validation.schema(migrate_server.migrate_live_v2_25, "2.25") + @validation.schema(migrate_server.migrate_live, "2.0", "2.24") + @validation.schema(migrate_server.migrate_live_v2_25, "2.25", "2.29") + @validation.schema(migrate_server.migrate_live_v2_30, "2.30") def _migrate_live(self, req, id, body): """Permit admins to (live) migrate a server to a new host.""" context = req.environ["nova.context"] - authorize(context, action='migrate_live') + context.can(ms_policies.POLICY_ROOT % 'migrate_live') host = body["os-migrateLive"]["host"] block_migration = body["os-migrateLive"]["block_migration"] - + force = None + async = api_version_request.is_supported(req, min_version='2.34') + if api_version_request.is_supported(req, min_version='2.30'): + force = self._get_force_param_for_live_migration(body, host) if api_version_request.is_supported(req, min_version='2.25'): if block_migration == 'auto': block_migration = None @@ -87,10 +92,10 @@ def _migrate_live(self, req, id, body): disk_over_commit = strutils.bool_from_string(disk_over_commit, strict=True) + instance = common.get_instance(self.compute_api, context, id) try: - instance = common.get_instance(self.compute_api, context, id) self.compute_api.live_migrate(context, instance, block_migration, - disk_over_commit, host) + disk_over_commit, host, force, async) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) except (exception.NoValidHost, @@ -103,27 +108,26 @@ def _migrate_live(self, req, id, body): exception.InvalidSharedStorage, exception.HypervisorUnavailable, exception.MigrationPreCheckError, - exception.LiveMigrationWithOldNovaNotSafe, exception.LiveMigrationWithOldNovaNotSupported) as ex: - raise exc.HTTPBadRequest(explanation=ex.format_message()) + if async: + with excutils.save_and_reraise_exception(): + LOG.error("Unexpected exception received from " + "conductor during pre-live-migration checks " + "'%(ex)s'", {'ex': ex}) + else: + raise exc.HTTPBadRequest(explanation=ex.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) + except exception.ComputeHostNotFound as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'os-migrateLive', id) - -class MigrateServer(extensions.V21APIExtensionBase): - """Enable migrate and live-migrate server actions.""" - - name = "MigrateServer" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = MigrateServerController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] + def _get_force_param_for_live_migration(self, body, host): + force = body["os-migrateLive"].get("force", False) + force = strutils.bool_from_string(force, strict=True) + if force is True and not host: + message = _("Can't force to a non-provided destination") + raise exc.HTTPBadRequest(explanation=message) + return force diff --git a/nova/api/openstack/compute/migrations.py b/nova/api/openstack/compute/migrations.py index 29a9de66a92..bdb4fae2a8a 100644 --- a/nova/api/openstack/compute/migrations.py +++ b/nova/api/openstack/compute/migrations.py @@ -16,13 +16,7 @@ from nova.api.openstack import wsgi from nova import compute from nova.objects import base as obj_base - - -ALIAS = "os-migrations" - - -def authorize(context, action_name): - extensions.os_compute_authorizer(ALIAS)(context, action=action_name) +from nova.policies import migrations as migrations_policies class MigrationsController(wsgi.Controller): @@ -76,27 +70,10 @@ def _output(self, req, migrations_obj, add_link=False): def index(self, req): """Return all migrations in progress.""" context = req.environ['nova.context'] - authorize(context, "index") + context.can(migrations_policies.POLICY_ROOT % 'index') migrations = self.compute_api.get_migrations(context, req.GET) if api_version_request.is_supported(req, min_version='2.23'): return {'migrations': self._output(req, migrations, True)} return {'migrations': self._output(req, migrations)} - - -class Migrations(extensions.V21APIExtensionBase): - """Provide data on migrations.""" - name = "Migrations" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [] - resource = extensions.ResourceExtension(ALIAS, - MigrationsController()) - resources.append(resource) - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/multinic.py b/nova/api/openstack/compute/multinic.py index 75ea342915a..41c1c3b96e2 100644 --- a/nova/api/openstack/compute/multinic.py +++ b/nova/api/openstack/compute/multinic.py @@ -24,17 +24,17 @@ from nova.api import validation from nova import compute from nova import exception - - -ALIAS = "os-multinic" -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import multinic as multinic_policies class MultinicController(wsgi.Controller): + """This API is deprecated from Microversion '2.44'.""" + def __init__(self, *args, **kwargs): super(MultinicController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() + @wsgi.Controller.api_version("2.1", "2.43") @wsgi.response(202) @wsgi.action('addFixedIp') @extensions.expected_errors((400, 404)) @@ -42,7 +42,7 @@ def __init__(self, *args, **kwargs): def _add_fixed_ip(self, req, id, body): """Adds an IP on a given network to an instance.""" context = req.environ['nova.context'] - authorize(context) + context.can(multinic_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, id) network_id = body['addFixedIp']['networkId'] @@ -53,6 +53,7 @@ def _add_fixed_ip(self, req, id, body): except exception.NoMoreFixedIps as e: raise exc.HTTPBadRequest(explanation=e.format_message()) + @wsgi.Controller.api_version("2.1", "2.43") @wsgi.response(202) @wsgi.action('removeFixedIp') @extensions.expected_errors((400, 404)) @@ -60,7 +61,7 @@ def _add_fixed_ip(self, req, id, body): def _remove_fixed_ip(self, req, id, body): """Removes an IP from an instance.""" context = req.environ['nova.context'] - authorize(context) + context.can(multinic_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, id) address = body['removeFixedIp']['address'] @@ -71,21 +72,3 @@ def _remove_fixed_ip(self, req, id, body): raise exc.HTTPNotFound(explanation=e.format_message()) except exception.FixedIpNotFoundForSpecificInstance as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - - -# Note: The class name is as it has to be for this to be loaded as an -# extension--only first character capitalized. -class Multinic(extensions.V21APIExtensionBase): - """Multiple network support.""" - - name = "Multinic" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = MultinicController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/multiple_create.py b/nova/api/openstack/compute/multiple_create.py index fc19e0faef0..1fb61804b78 100644 --- a/nova/api/openstack/compute/multiple_create.py +++ b/nova/api/openstack/compute/multiple_create.py @@ -17,48 +17,32 @@ from nova.api.openstack.compute.schemas import multiple_create as \ schema_multiple_create -from nova.api.openstack import extensions from nova.i18n import _ -ALIAS = "os-multiple-create" MIN_ATTRIBUTE_NAME = "min_count" MAX_ATTRIBUTE_NAME = "max_count" RRID_ATTRIBUTE_NAME = "return_reservation_id" -class MultipleCreate(extensions.V21APIExtensionBase): - """Allow multiple create in the Create Server v2.1 API.""" +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + # min_count and max_count are optional. If they exist, they may come + # in as strings. Verify that they are valid integers and > 0. + # Also, we want to default 'min_count' to 1, and default + # 'max_count' to be 'min_count'. + min_count = int(server_dict.get(MIN_ATTRIBUTE_NAME, 1)) + max_count = int(server_dict.get(MAX_ATTRIBUTE_NAME, min_count)) + return_id = server_dict.get(RRID_ATTRIBUTE_NAME, False) - name = "MultipleCreate" - alias = ALIAS - version = 1 + if min_count > max_count: + msg = _('min_count must be <= max_count') + raise exc.HTTPBadRequest(explanation=msg) - def get_resources(self): - return [] + create_kwargs['min_count'] = min_count + create_kwargs['max_count'] = max_count + create_kwargs['return_reservation_id'] = return_id - def get_controller_extensions(self): - return [] - # use nova.api.extensions.server.extensions entry point to modify - # server create kwargs - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - # min_count and max_count are optional. If they exist, they may come - # in as strings. Verify that they are valid integers and > 0. - # Also, we want to default 'min_count' to 1, and default - # 'max_count' to be 'min_count'. - min_count = int(server_dict.get(MIN_ATTRIBUTE_NAME, 1)) - max_count = int(server_dict.get(MAX_ATTRIBUTE_NAME, min_count)) - return_id = server_dict.get(RRID_ATTRIBUTE_NAME, False) - - if min_count > max_count: - msg = _('min_count must be <= max_count') - raise exc.HTTPBadRequest(explanation=msg) - - create_kwargs['min_count'] = min_count - create_kwargs['max_count'] = max_count - create_kwargs['return_reservation_id'] = return_id - - def get_server_create_schema(self, version): - return schema_multiple_create.server_create +def get_server_create_schema(version): + return schema_multiple_create.server_create diff --git a/nova/api/openstack/compute/networks.py b/nova/api/openstack/compute/networks.py index 83f643367f4..e1621e8a4db 100644 --- a/nova/api/openstack/compute/networks.py +++ b/nova/api/openstack/compute/networks.py @@ -17,6 +17,8 @@ import netaddr from webob import exc +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import common from nova.api.openstack.compute.schemas import networks as schema from nova.api.openstack import extensions @@ -27,9 +29,7 @@ from nova import network from nova.objects import base as base_obj from nova.objects import fields as obj_fields - -ALIAS = 'os-networks' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import networks as net_policies def network_dict(context, network): @@ -80,22 +80,24 @@ def network_dict(context, network): class NetworkController(wsgi.Controller): def __init__(self, network_api=None): - self.network_api = network_api or network.API(skip_policy_check=True) + self.network_api = network_api or network.API() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(()) def index(self, req): context = req.environ['nova.context'] - authorize(context, action='view') + context.can(net_policies.POLICY_ROOT % 'view') networks = self.network_api.get_all(context) result = [network_dict(context, net_ref) for net_ref in networks] return {'networks': result} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.response(202) @extensions.expected_errors((404, 501)) @wsgi.action("disassociate") def _disassociate_host_and_project(self, req, id, body): context = req.environ['nova.context'] - authorize(context) + context.can(net_policies.BASE_POLICY_NAME) try: self.network_api.associate(context, id, host=None, project=None) @@ -105,10 +107,11 @@ def _disassociate_host_and_project(self, req, id, body): except NotImplementedError: common.raise_feature_not_supported() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(404) def show(self, req, id): context = req.environ['nova.context'] - authorize(context, action='view') + context.can(net_policies.POLICY_ROOT % 'view') try: network = self.network_api.get(context, id) @@ -117,11 +120,12 @@ def show(self, req, id): raise exc.HTTPNotFound(explanation=msg) return {'network': network_dict(context, network)} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.response(202) @extensions.expected_errors((404, 409)) def delete(self, req, id): context = req.environ['nova.context'] - authorize(context) + context.can(net_policies.BASE_POLICY_NAME) try: self.network_api.delete(context, id) @@ -131,11 +135,12 @@ def delete(self, req, id): msg = _("Network not found") raise exc.HTTPNotFound(explanation=msg) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 409, 501)) @validation.schema(schema.create) def create(self, req, body): context = req.environ['nova.context'] - authorize(context) + context.can(net_policies.BASE_POLICY_NAME) params = body["network"] @@ -151,16 +156,18 @@ def create(self, req, body): exception.InvalidAddress, exception.NetworkNotCreated) as ex: raise exc.HTTPBadRequest(explanation=ex.format_message) - except exception.CidrConflict as ex: + except (exception.CidrConflict, + exception.DuplicateVlan) as ex: raise exc.HTTPConflict(explanation=ex.format_message()) return {"network": network_dict(context, network)} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.response(202) @extensions.expected_errors((400, 501)) @validation.schema(schema.add_network_to_project) def add(self, req, body): context = req.environ['nova.context'] - authorize(context) + context.can(net_policies.BASE_POLICY_NAME) network_id = body['id'] project_id = context.project_id @@ -173,23 +180,3 @@ def add(self, req, body): except (exception.NoMoreNetworks, exception.NetworkNotFoundForUUID) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - - -class Networks(extensions.V21APIExtensionBase): - """Admin-only Network Management Extension.""" - - name = "Networks" - alias = ALIAS - version = 1 - - def get_resources(self): - member_actions = {'action': 'POST'} - collection_actions = {'add': 'POST'} - res = extensions.ResourceExtension( - ALIAS, NetworkController(), - member_actions=member_actions, - collection_actions=collection_actions) - return [res] - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/networks_associate.py b/nova/api/openstack/compute/networks_associate.py index 8f9ace0dc09..530c4caff4f 100644 --- a/nova/api/openstack/compute/networks_associate.py +++ b/nova/api/openstack/compute/networks_associate.py @@ -12,6 +12,8 @@ from webob import exc +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import common from nova.api.openstack.compute.schemas import networks_associate from nova.api.openstack import extensions @@ -20,24 +22,22 @@ from nova import exception from nova.i18n import _ from nova import network - -ALIAS = "os-networks-associate" - -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import networks_associate as na_policies class NetworkAssociateActionController(wsgi.Controller): """Network Association API Controller.""" def __init__(self, network_api=None): - self.network_api = network_api or network.API(skip_policy_check=True) + self.network_api = network_api or network.API() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.action("disassociate_host") @wsgi.response(202) @extensions.expected_errors((404, 501)) def _disassociate_host_only(self, req, id, body): context = req.environ['nova.context'] - authorize(context) + context.can(na_policies.BASE_POLICY_NAME) try: self.network_api.associate(context, id, host=None) except exception.NetworkNotFound: @@ -46,12 +46,13 @@ def _disassociate_host_only(self, req, id, body): except NotImplementedError: common.raise_feature_not_supported() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.action("disassociate_project") @wsgi.response(202) @extensions.expected_errors((404, 501)) def _disassociate_project_only(self, req, id, body): context = req.environ['nova.context'] - authorize(context) + context.can(na_policies.BASE_POLICY_NAME) try: self.network_api.associate(context, id, project=None) except exception.NetworkNotFound: @@ -60,13 +61,14 @@ def _disassociate_project_only(self, req, id, body): except NotImplementedError: common.raise_feature_not_supported() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.action("associate_host") @wsgi.response(202) @extensions.expected_errors((404, 501)) @validation.schema(networks_associate.associate_host) def _associate_host(self, req, id, body): context = req.environ['nova.context'] - authorize(context) + context.can(na_policies.BASE_POLICY_NAME) try: self.network_api.associate(context, id, @@ -76,20 +78,3 @@ def _associate_host(self, req, id, body): raise exc.HTTPNotFound(explanation=msg) except NotImplementedError: common.raise_feature_not_supported() - - -class NetworksAssociate(extensions.V21APIExtensionBase): - """Network association support.""" - - name = "NetworkAssociationSupport" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - extension = extensions.ControllerExtension( - self, 'os-networks', NetworkAssociateActionController()) - - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/pause_server.py b/nova/api/openstack/compute/pause_server.py index 042cd2c16b5..14b687ab83d 100644 --- a/nova/api/openstack/compute/pause_server.py +++ b/nova/api/openstack/compute/pause_server.py @@ -20,16 +20,13 @@ from nova.api.openstack import wsgi from nova import compute from nova import exception - -ALIAS = "os-pause-server" - -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import pause_server as ps_policies class PauseServerController(wsgi.Controller): def __init__(self, *args, **kwargs): super(PauseServerController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() @wsgi.response(202) @extensions.expected_errors((404, 409, 501)) @@ -37,8 +34,10 @@ def __init__(self, *args, **kwargs): def _pause(self, req, id, body): """Permit Admins to pause the server.""" ctxt = req.environ['nova.context'] - authorize(ctxt, action='pause') server = common.get_instance(self.compute_api, ctxt, id) + ctxt.can(ps_policies.POLICY_ROOT % 'pause', + target={'user_id': server.user_id, + 'project_id': server.project_id}) try: self.compute_api.pause(ctxt, server) except exception.InstanceIsLocked as e: @@ -58,7 +57,7 @@ def _pause(self, req, id, body): def _unpause(self, req, id, body): """Permit Admins to unpause the server.""" ctxt = req.environ['nova.context'] - authorize(ctxt, action='unpause') + ctxt.can(ps_policies.POLICY_ROOT % 'unpause') server = common.get_instance(self.compute_api, ctxt, id) try: self.compute_api.unpause(ctxt, server) @@ -72,19 +71,3 @@ def _unpause(self, req, id, body): raise exc.HTTPNotFound(explanation=e.format_message()) except NotImplementedError: common.raise_feature_not_supported() - - -class PauseServer(extensions.V21APIExtensionBase): - """Enable pause/unpause server actions.""" - - name = "PauseServer" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = PauseServerController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/pci.py b/nova/api/openstack/compute/pci.py deleted file mode 100644 index 698cb4bbc3f..00000000000 --- a/nova/api/openstack/compute/pci.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright 2013 Intel Corporation -# 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 webob.exc - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import compute -from nova import exception -from nova import objects - - -ALIAS = 'os-pci' -soft_authorize = extensions.os_compute_soft_authorizer(ALIAS + ':pci_servers') -authorize = extensions.os_compute_authorizer(ALIAS) - -PCI_ADMIN_KEYS = ['id', 'address', 'vendor_id', 'product_id', 'status', - 'compute_node_id'] -PCI_DETAIL_KEYS = ['dev_type', 'label', 'instance_uuid', 'dev_id', - 'extra_info'] - - -class PciServerController(wsgi.Controller): - def _extend_server(self, server, instance): - dev_id = [] - for dev in instance.pci_devices: - dev_id.append({'id': dev.id}) - server['%s:pci_devices' % Pci.alias] = dev_id - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - if soft_authorize(context): - server = resp_obj.obj['server'] - instance = req.get_db_instance(server['id']) - self._extend_server(server, instance) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - if soft_authorize(context): - servers = list(resp_obj.obj['servers']) - for server in servers: - instance = req.get_db_instance(server['id']) - self._extend_server(server, instance) - - -class PciHypervisorController(wsgi.Controller): - def _extend_hypervisor(self, hypervisor, compute_node): - if compute_node.pci_device_pools is not None: - pci_pools = [pci_pool.to_dict() - for pci_pool in compute_node.pci_device_pools] - else: - pci_pools = [] - hypervisor['%s:pci_stats' % Pci.alias] = pci_pools - - @wsgi.extends - def show(self, req, resp_obj, id): - hypervisor = resp_obj.obj['hypervisor'] - compute_node = req.get_db_compute_node(hypervisor['id']) - self._extend_hypervisor(hypervisor, compute_node) - - @wsgi.extends - def detail(self, req, resp_obj): - hypervisors = list(resp_obj.obj['hypervisors']) - for hypervisor in hypervisors: - compute_node = req.get_db_compute_node(hypervisor['id']) - self._extend_hypervisor(hypervisor, compute_node) - - -class PciController(wsgi.Controller): - - def __init__(self): - self.host_api = compute.HostAPI() - - def _view_pcidevice(self, device, detail=False): - dev_dict = {} - for key in PCI_ADMIN_KEYS: - dev_dict[key] = getattr(device, key) - if detail: - for field in PCI_DETAIL_KEYS: - if field == 'instance_uuid': - dev_dict['server_uuid'] = getattr(device, field) - else: - dev_dict[field] = getattr(device, field) - return dev_dict - - def _get_all_nodes_pci_devices(self, req, detail, action): - context = req.environ['nova.context'] - authorize(context, action=action) - compute_nodes = self.host_api.compute_node_get_all(context) - results = [] - for node in compute_nodes: - pci_devs = objects.PciDeviceList.get_by_compute_node( - context, node['id']) - results.extend([self._view_pcidevice(dev, detail) - for dev in pci_devs]) - return results - - @extensions.expected_errors(()) - def detail(self, req): - results = self._get_all_nodes_pci_devices(req, True, 'detail') - return dict(pci_devices=results) - - @extensions.expected_errors(404) - def show(self, req, id): - context = req.environ['nova.context'] - authorize(context, action='show') - try: - pci_dev = objects.PciDevice.get_by_dev_id(context, id) - except exception.PciDeviceNotFoundById as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - result = self._view_pcidevice(pci_dev, True) - return dict(pci_device=result) - - @extensions.expected_errors(()) - def index(self, req): - results = self._get_all_nodes_pci_devices(req, False, 'index') - return dict(pci_devices=results) - - -class Pci(extensions.V21APIExtensionBase): - """Pci access support.""" - name = "PciAccess" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [extensions.ResourceExtension(ALIAS, - PciController(), - collection_actions={'detail': 'GET'})] - return resources - - def get_controller_extensions(self): - server_extension = extensions.ControllerExtension( - self, 'servers', PciServerController()) - compute_extension = extensions.ControllerExtension( - self, 'os-hypervisors', PciHypervisorController()) - return [server_extension, compute_extension] diff --git a/nova/api/openstack/compute/personality.py b/nova/api/openstack/compute/personality.py deleted file mode 100644 index 4d15fb851dc..00000000000 --- a/nova/api/openstack/compute/personality.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright 2014 NEC Corporation. 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. - -from nova.api.openstack.compute.schemas import personality -from nova.api.openstack import extensions - -ALIAS = "os-personality" - - -class Personality(extensions.V21APIExtensionBase): - """Personality support.""" - - name = "Personality" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - return [] - - def get_resources(self): - return [] - - def _get_injected_files(self, personality): - """Create a list of injected files from the personality attribute. - - At this time, injected_files must be formatted as a list of - (file_path, file_content) pairs for compatibility with the - underlying compute service. - """ - injected_files = [] - for item in personality: - injected_files.append((item['path'], item['contents'])) - return injected_files - - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - # making 'body_deprecated_param' as optional to avoid changes for - # server_update & server_rebuild - def server_create(self, server_dict, create_kwargs, - body_deprecated_param=None): - create_kwargs['injected_files'] = self._get_injected_files( - server_dict.get('personality', [])) - - def server_rebuild(self, server_dict, create_kwargs, - body_deprecated_param=None): - if 'personality' in server_dict: - create_kwargs['files_to_inject'] = self._get_injected_files( - server_dict['personality']) - - def get_server_create_schema(self, version): - return personality.server_create - - get_server_rebuild_schema = get_server_create_schema diff --git a/nova/api/openstack/compute/preserve_ephemeral_rebuild.py b/nova/api/openstack/compute/preserve_ephemeral_rebuild.py deleted file mode 100644 index 78e30ccc01c..00000000000 --- a/nova/api/openstack/compute/preserve_ephemeral_rebuild.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2015 IBM Corp. -# -# 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 oslo_utils import strutils - -from nova.api.openstack.compute.schemas import preserve_ephemeral_rebuild -from nova.api.openstack import extensions - -ALIAS = "os-preserve-ephemeral-rebuild" - - -class PreserveEphemeralRebuild(extensions.V21APIExtensionBase): - """Allow preservation of the ephemeral partition on rebuild.""" - - name = "PreserveEphemeralOnRebuild" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - return [] - - def get_resources(self): - return [] - - def server_rebuild(self, rebuild_dict, rebuild_kwargs, - body_deprecated_param=None): - if 'preserve_ephemeral' in rebuild_dict: - rebuild_kwargs['preserve_ephemeral'] = strutils.bool_from_string( - rebuild_dict['preserve_ephemeral'], strict=True) - - def get_server_rebuild_schema(self, version): - return preserve_ephemeral_rebuild.server_rebuild diff --git a/nova/api/openstack/compute/quota_classes.py b/nova/api/openstack/compute/quota_classes.py index 63e71fadb87..275238507ba 100644 --- a/nova/api/openstack/compute/quota_classes.py +++ b/nova/api/openstack/compute/quota_classes.py @@ -13,28 +13,31 @@ # License for the specific language governing permissions and limitations # under the License. -import six +import copy import webob +from nova.api.openstack import api_version_request from nova.api.openstack.compute.schemas import quota_classes from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api import validation -from nova import db from nova import exception +from nova import objects +from nova.policies import quota_class_sets as qcs_policies from nova import quota from nova import utils QUOTAS = quota.QUOTAS -ALIAS = "os-quota-class-sets" -# Quotas that are only enabled by specific extensions -EXTENDED_QUOTAS = {'server_groups': 'os-server-group-quotas', - 'server_group_members': 'os-server-group-quotas'} +# NOTE(gmann): Quotas which were returned in v2 but in v2.1 those +# were not returned. Fixed in microversion 2.50. Bug#1693168. +EXTENDED_QUOTAS = ['server_groups', 'server_group_members'] - -authorize = extensions.os_compute_authorizer(ALIAS) +# NOTE(gmann): Network related quotas are filter out in +# microversion 2.50. Bug#1701211. +FILTERED_QUOTAS = ["fixed_ips", "floating_ips", "networks", + "security_group_rules", "security_groups"] class QuotaClassSetsController(wsgi.Controller): @@ -43,20 +46,25 @@ class QuotaClassSetsController(wsgi.Controller): def __init__(self, **kwargs): self.supported_quotas = QUOTAS.resources - extension_info = kwargs.pop('extension_info').get_extensions() - for resource, extension in EXTENDED_QUOTAS.items(): - if extension not in extension_info: - self.supported_quotas.remove(resource) - def _format_quota_set(self, quota_class, quota_set): + def _format_quota_set(self, quota_class, quota_set, req): """Convert the quota object to a result dict.""" if quota_class: result = dict(id=str(quota_class)) else: result = {} - - for resource in self.supported_quotas: + original_quotas = copy.deepcopy(self.supported_quotas) + if api_version_request.is_supported(req, min_version="2.50"): + original_quotas = [resource for resource in original_quotas + if resource not in FILTERED_QUOTAS] + # NOTE(gmann): Before microversion v2.50, v2.1 API does not return the + # 'server_groups' & 'server_group_members' key in quota class API + # response. + else: + for resource in EXTENDED_QUOTAS: + original_quotas.remove(resource) + for resource in original_quotas: if resource in quota_set: result[resource] = quota_set[resource] @@ -65,15 +73,16 @@ def _format_quota_set(self, quota_class, quota_set): @extensions.expected_errors(()) def show(self, req, id): context = req.environ['nova.context'] - authorize(context, action='show', target={'quota_class': id}) + context.can(qcs_policies.POLICY_ROOT % 'show', {'quota_class': id}) values = QUOTAS.get_class_quotas(context, id) - return self._format_quota_set(id, values) + return self._format_quota_set(id, values, req) @extensions.expected_errors(400) - @validation.schema(quota_classes.update) + @validation.schema(quota_classes.update, "2.0", "2.49") + @validation.schema(quota_classes.update_v250, "2.50") def update(self, req, id, body): context = req.environ['nova.context'] - authorize(context, action='update', target={'quota_class': id}) + context.can(qcs_policies.POLICY_ROOT % 'update', {'quota_class': id}) try: utils.check_string_length(id, 'quota_class_name', min_length=1, max_length=255) @@ -83,30 +92,11 @@ def update(self, req, id, body): quota_class = id - for key, value in six.iteritems(body['quota_class_set']): + for key, value in body['quota_class_set'].items(): try: - db.quota_class_update(context, quota_class, key, value) + objects.Quotas.update_class(context, quota_class, key, value) except exception.QuotaClassNotFound: - db.quota_class_create(context, quota_class, key, value) + objects.Quotas.create_class(context, quota_class, key, value) values = QUOTAS.get_class_quotas(context, quota_class) - return self._format_quota_set(None, values) - - -class QuotaClasses(extensions.V21APIExtensionBase): - """Quota classes management support.""" - - name = "QuotaClasses" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [] - res = extensions.ResourceExtension( - ALIAS, - QuotaClassSetsController(extension_info=self.extension_info)) - resources.append(res) - return resources - - def get_controller_extensions(self): - return [] + return self._format_quota_set(None, values, req) diff --git a/nova/api/openstack/compute/quota_sets.py b/nova/api/openstack/compute/quota_sets.py index 8b616c23bf0..e618f666ba8 100644 --- a/nova/api/openstack/compute/quota_sets.py +++ b/nova/api/openstack/compute/quota_sets.py @@ -14,28 +14,37 @@ # under the License. from oslo_utils import strutils -import six + import six.moves.urllib.parse as urlparse import webob +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION +from nova.api.openstack.api_version_request \ + import MIN_WITHOUT_PROXY_API_SUPPORT_VERSION from nova.api.openstack.compute.schemas import quota_sets from nova.api.openstack import extensions +from nova.api.openstack import identity from nova.api.openstack import wsgi from nova.api import validation +import nova.conf from nova import exception from nova.i18n import _ from nova import objects +from nova.policies import quota_sets as qs_policies from nova import quota -ALIAS = "os-quota-sets" +CONF = nova.conf.CONF QUOTAS = quota.QUOTAS -authorize = extensions.os_compute_authorizer(ALIAS) + +FILTERED_QUOTAS = ["fixed_ips", "floating_ips", "networks", + "security_group_rules", "security_groups"] class QuotaSetsController(wsgi.Controller): - def _format_quota_set(self, project_id, quota_set): + def _format_quota_set(self, project_id, quota_set, filtered_quotas): """Convert the quota object to a result dict.""" if project_id: result = dict(id=str(project_id)) @@ -43,18 +52,12 @@ def _format_quota_set(self, project_id, quota_set): result = {} for resource in QUOTAS.resources: - if resource in quota_set: + if (resource not in filtered_quotas and + resource in quota_set): result[resource] = quota_set[resource] return dict(quota_set=result) def _validate_quota_limit(self, resource, limit, minimum, maximum): - # NOTE: -1 is a flag value for unlimited - if limit < -1: - msg = (_("Quota limit %(limit)s for %(resource)s " - "must be -1 or greater.") % - {'limit': limit, 'resource': resource}) - raise webob.exc.HTTPBadRequest(explanation=msg) - def conv_inf(value): return float("inf") if value == -1 else value @@ -82,34 +85,79 @@ def _get_quotas(self, context, id, user_id=None, usages=False): else: return {k: v['limit'] for k, v in values.items()} - @extensions.expected_errors(()) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) + @extensions.expected_errors(400) def show(self, req, id): + return self._show(req, id, []) + + @wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION) # noqa + @extensions.expected_errors(400) + def show(self, req, id): + return self._show(req, id, FILTERED_QUOTAS) + + def _show(self, req, id, filtered_quotas): context = req.environ['nova.context'] - authorize(context, action='show', target={'project_id': id}) + context.can(qs_policies.POLICY_ROOT % 'show', {'project_id': id}) + identity.verify_project_id(context, id) + params = urlparse.parse_qs(req.environ.get('QUERY_STRING', '')) user_id = params.get('user_id', [None])[0] return self._format_quota_set(id, - self._get_quotas(context, id, user_id=user_id)) + self._get_quotas(context, id, user_id=user_id), + filtered_quotas=filtered_quotas) - @extensions.expected_errors(()) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) + @extensions.expected_errors(400) + def detail(self, req, id): + return self._detail(req, id, []) + + @wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION) # noqa + @extensions.expected_errors(400) def detail(self, req, id): + return self._detail(req, id, FILTERED_QUOTAS) + + def _detail(self, req, id, filtered_quotas): context = req.environ['nova.context'] - authorize(context, action='detail', target={'project_id': id}) + context.can(qs_policies.POLICY_ROOT % 'detail', {'project_id': id}) + identity.verify_project_id(context, id) + user_id = req.GET.get('user_id', None) - return self._format_quota_set(id, self._get_quotas(context, id, - user_id=user_id, - usages=True)) + return self._format_quota_set( + id, + self._get_quotas(context, id, user_id=user_id, usages=True), + filtered_quotas=filtered_quotas) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(400) @validation.schema(quota_sets.update) def update(self, req, id, body): + return self._update(req, id, body, []) + + @wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION) # noqa + @extensions.expected_errors(400) + @validation.schema(quota_sets.update_v236) + def update(self, req, id, body): + return self._update(req, id, body, FILTERED_QUOTAS) + + def _update(self, req, id, body, filtered_quotas): context = req.environ['nova.context'] - authorize(context, action='update', target={'project_id': id}) + context.can(qs_policies.POLICY_ROOT % 'update', {'project_id': id}) + identity.verify_project_id(context, id) + project_id = id params = urlparse.parse_qs(req.environ.get('QUERY_STRING', '')) user_id = params.get('user_id', [None])[0] quota_set = body['quota_set'] + + # NOTE(alex_xu): The CONF.enable_network_quota was deprecated + # due to it is only used by nova-network, and nova-network will be + # deprecated also. So when CONF.enable_newtork_quota is removed, + # the networks quota will disappeare also. + if not CONF.enable_network_quota and 'networks' in quota_set: + raise webob.exc.HTTPBadRequest( + explanation=_('The networks quota is disabled')) + force_update = strutils.bool_from_string(quota_set.get('force', 'False')) settable_quotas = QUOTAS.get_settable_quotas(context, project_id, @@ -118,7 +166,7 @@ def update(self, req, id, body): # NOTE(dims): Pass #1 - In this loop for quota_set.items(), we validate # min/max values and bail out if any of the items in the set is bad. valid_quotas = {} - for key, value in six.iteritems(body['quota_set']): + for key, value in body['quota_set'].items(): if key == 'force' or (not value and value != 0): continue # validate whether already used and reserved exceeds the new @@ -144,15 +192,29 @@ def update(self, req, id, body): key, value, user_id=user_id) # Note(gmann): Removed 'id' from update's response to make it same # as V2. If needed it can be added with microversion. - return self._format_quota_set(None, self._get_quotas(context, id, - user_id=user_id)) + return self._format_quota_set( + None, + self._get_quotas(context, id, user_id=user_id), + filtered_quotas=filtered_quotas) - @extensions.expected_errors(()) + @wsgi.Controller.api_version("2.0", MAX_PROXY_API_SUPPORT_VERSION) + @extensions.expected_errors(400) + def defaults(self, req, id): + return self._defaults(req, id, []) + + @wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION) # noqa + @extensions.expected_errors(400) def defaults(self, req, id): + return self._defaults(req, id, FILTERED_QUOTAS) + + def _defaults(self, req, id, filtered_quotas): context = req.environ['nova.context'] - authorize(context, action='defaults', target={'project_id': id}) + context.can(qs_policies.POLICY_ROOT % 'defaults', {'project_id': id}) + identity.verify_project_id(context, id) + values = QUOTAS.get_defaults(context) - return self._format_quota_set(id, values) + return self._format_quota_set(id, values, + filtered_quotas=filtered_quotas) # TODO(oomichi): Here should be 204(No Content) instead of 202 by v2.1 # +microversions because the resource quota-set has been deleted completely @@ -161,7 +223,7 @@ def defaults(self, req, id): @wsgi.response(202) def delete(self, req, id): context = req.environ['nova.context'] - authorize(context, action='delete', target={'project_id': id}) + context.can(qs_policies.POLICY_ROOT % 'delete', {'project_id': id}) params = urlparse.parse_qs(req.environ.get('QUERY_STRING', '')) user_id = params.get('user_id', [None])[0] if user_id: @@ -169,25 +231,3 @@ def delete(self, req, id): id, user_id) else: QUOTAS.destroy_all_by_project(context, id) - - -class QuotaSets(extensions.V21APIExtensionBase): - """Quotas management support.""" - - name = "Quotas" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension(ALIAS, - QuotaSetsController(), - member_actions={'defaults': 'GET', - 'detail': 'GET'}) - resources.append(res) - - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/remote_consoles.py b/nova/api/openstack/compute/remote_consoles.py index 8a5cb3938e2..80faad25a9c 100644 --- a/nova/api/openstack/compute/remote_consoles.py +++ b/nova/api/openstack/compute/remote_consoles.py @@ -21,15 +21,12 @@ from nova.api import validation from nova import compute from nova import exception - - -ALIAS = "os-remote-consoles" -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import remote_consoles as rc_policies class RemoteConsolesController(wsgi.Controller): def __init__(self, *args, **kwargs): - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() self.handlers = {'vnc': self.compute_api.get_vnc_console, 'spice': self.compute_api.get_spice_console, 'rdp': self.compute_api.get_rdp_console, @@ -44,13 +41,13 @@ def __init__(self, *args, **kwargs): def get_vnc_console(self, req, id, body): """Get text console output.""" context = req.environ['nova.context'] - authorize(context) + context.can(rc_policies.BASE_POLICY_NAME) # If type is not supplied or unknown, get_vnc_console below will cope console_type = body['os-getVNCConsole'].get('type') + instance = common.get_instance(self.compute_api, context, id) try: - instance = common.get_instance(self.compute_api, context, id) output = self.compute_api.get_vnc_console(context, instance, console_type) @@ -73,13 +70,13 @@ def get_vnc_console(self, req, id, body): def get_spice_console(self, req, id, body): """Get text console output.""" context = req.environ['nova.context'] - authorize(context) + context.can(rc_policies.BASE_POLICY_NAME) # If type is not supplied or unknown, get_spice_console below will cope console_type = body['os-getSPICEConsole'].get('type') + instance = common.get_instance(self.compute_api, context, id) try: - instance = common.get_instance(self.compute_api, context, id) output = self.compute_api.get_spice_console(context, instance, console_type) @@ -102,7 +99,7 @@ def get_spice_console(self, req, id, body): def get_rdp_console(self, req, id, body): """Get text console output.""" context = req.environ['nova.context'] - authorize(context) + context.can(rc_policies.BASE_POLICY_NAME) # If type is not supplied or unknown, get_rdp_console below will cope console_type = body['os-getRDPConsole'].get('type') @@ -133,12 +130,12 @@ def get_rdp_console(self, req, id, body): def get_serial_console(self, req, id, body): """Get connection to a serial console.""" context = req.environ['nova.context'] - authorize(context) + context.can(rc_policies.BASE_POLICY_NAME) # If type is not supplied or unknown get_serial_console below will cope console_type = body['os-getSerialConsole'].get('type') + instance = common.get_instance(self.compute_api, context, id) try: - instance = common.get_instance(self.compute_api, context, id) output = self.compute_api.get_serial_console(context, instance, console_type) @@ -163,7 +160,7 @@ def get_serial_console(self, req, id, body): @validation.schema(remote_consoles.create_v28, "2.8") def create(self, req, server_id, body): context = req.environ['nova.context'] - authorize(context) + context.can(rc_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, server_id) protocol = body['remote_console']['protocol'] console_type = body['remote_console']['type'] @@ -186,24 +183,3 @@ def create(self, req, server_id, body): raise webob.exc.HTTPBadRequest(explanation=e.format_message()) except NotImplementedError: common.raise_feature_not_supported() - - -class RemoteConsoles(extensions.V21APIExtensionBase): - """Interactive Console support.""" - name = "Consoles" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = RemoteConsolesController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - parent = {'member_name': 'server', - 'collection_name': 'servers'} - resources = [ - extensions.ResourceExtension( - 'remote-consoles', RemoteConsolesController(), parent=parent, - member_name='remote-console')] - return resources diff --git a/nova/api/openstack/compute/rescue.py b/nova/api/openstack/compute/rescue.py index 12990bb9ebf..77918031ced 100644 --- a/nova/api/openstack/compute/rescue.py +++ b/nova/api/openstack/compute/rescue.py @@ -14,7 +14,6 @@ """The rescue mode extension.""" -from oslo_config import cfg from webob import exc from nova.api.openstack import common @@ -23,22 +22,18 @@ from nova.api.openstack import wsgi from nova.api import validation from nova import compute +import nova.conf from nova import exception +from nova.policies import rescue as rescue_policies from nova import utils - -ALIAS = "os-rescue" -CONF = cfg.CONF -CONF.import_opt('enable_instance_password', - 'nova.api.openstack.compute.legacy_v2.servers') - -authorize = extensions.os_compute_authorizer(ALIAS) +CONF = nova.conf.CONF class RescueController(wsgi.Controller): def __init__(self, *args, **kwargs): super(RescueController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() # TODO(cyeoh): Should be responding here with 202 Accept # because rescue is an async call, but keep to 200 @@ -49,7 +44,6 @@ def __init__(self, *args, **kwargs): def _rescue(self, req, id, body): """Rescue an instance.""" context = req.environ["nova.context"] - authorize(context) if body['rescue'] and 'adminPass' in body['rescue']: password = body['rescue']['adminPass'] @@ -57,9 +51,12 @@ def _rescue(self, req, id, body): password = utils.generate_password() instance = common.get_instance(self.compute_api, context, id) + context.can(rescue_policies.BASE_POLICY_NAME, + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) rescue_image_ref = None - if body['rescue'] and 'rescue_image_ref' in body['rescue']: - rescue_image_ref = body['rescue']['rescue_image_ref'] + if body['rescue']: + rescue_image_ref = body['rescue'].get('rescue_image_ref') try: self.compute_api.rescue(context, instance, @@ -78,7 +75,7 @@ def _rescue(self, req, id, body): raise exc.HTTPBadRequest( explanation=non_rescuable.format_message()) - if CONF.enable_instance_password: + if CONF.api.enable_instance_password: return {'adminPass': password} else: return {} @@ -89,7 +86,7 @@ def _rescue(self, req, id, body): def _unrescue(self, req, id, body): """Unrescue an instance.""" context = req.environ["nova.context"] - authorize(context) + context.can(rescue_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, id) try: self.compute_api.unrescue(context, instance) @@ -101,19 +98,3 @@ def _unrescue(self, req, id, body): common.raise_http_conflict_for_instance_invalid_state(state_error, 'unrescue', id) - - -class Rescue(extensions.V21APIExtensionBase): - """Instance rescue mode.""" - - name = "Rescue" - alias = ALIAS - version = 1 - - def get_resources(self): - return [] - - def get_controller_extensions(self): - controller = RescueController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] diff --git a/nova/api/openstack/compute/rest_api_version_history.rst b/nova/api/openstack/compute/rest_api_version_history.rst new file mode 100644 index 00000000000..68f65bd875f --- /dev/null +++ b/nova/api/openstack/compute/rest_api_version_history.rst @@ -0,0 +1,691 @@ +REST API Version History +======================== + +This documents the changes made to the REST API with every +microversion change. The description for each version should be a +verbose one which has enough information to be suitable for use in +user documentation. + +2.1 +--- + +This is the initial version of the v2.1 API which supports +microversions. The V2.1 API is from the REST API users's point of +view exactly the same as v2.0 except with strong input validation. + +A user can specify a header in the API request:: + + X-OpenStack-Nova-API-Version: + +where ```` is any valid api version for this API. + +If no version is specified then the API will behave as if a version +request of v2.1 was requested. + +2.2 +--- + +Added Keypair type. + +A user can request the creation of a certain 'type' of keypair (``ssh`` or ``x509``) +in the ``os-keypairs`` plugin + +If no keypair type is specified, then the default ``ssh`` type of keypair is +created. + +Fixes status code for ``os-keypairs`` create method from 200 to 201 + +Fixes status code for ``os-keypairs`` delete method from 202 to 204 + +2.3 (Maximum in Kilo) +--------------------- + +Exposed additional attributes in ``os-extended-server-attributes``: +``reservation_id``, ``launch_index``, ``ramdisk_id``, ``kernel_id``, ``hostname``, +``root_device_name``, ``userdata``. + +Exposed ``delete_on_termination`` for ``volumes_attached`` in ``os-extended-volumes``. + +This change is required for the extraction of EC2 API into a standalone +service. It exposes necessary properties absent in public nova APIs yet. +Add info for Standalone EC2 API to cut access to Nova DB. + +2.4 +--- + +Show the ``reserved`` status on a ``FixedIP`` object in the ``os-fixed-ips`` API +extension. The extension allows one to ``reserve`` and ``unreserve`` a fixed IP +but the show method does not report the current status. + +2.5 +--- + +Before version 2.5, the command ``nova list --ip6 xxx`` returns all servers +for non-admins, as the filter option is silently discarded. There is no +reason to treat ip6 different from ip, though, so we just add this +option to the allowed list. + +2.6 +--- + +A new API for getting remote console is added:: + + POST /servers//remote-consoles + { + "remote_console": { + "protocol": ["vnc"|"rdp"|"serial"|"spice"], + "type": ["novnc"|"xpvnc"|"rdp-html5"|"spice-html5"|"serial"] + } + } + +Example response:: + + { + "remote_console": { + "protocol": "vnc", + "type": "novnc", + "url": "http://example.com:6080/vnc_auto.html?token=XYZ" + } + } + +The old APIs 'os-getVNCConsole', 'os-getSPICEConsole', 'os-getSerialConsole' +and 'os-getRDPConsole' are removed. + +2.7 +--- + +Check the ``is_public`` attribute of a flavor before adding tenant access +to it. Reject the request with HTTPConflict error. + +2.8 +--- +Add 'mks' protocol and 'webmks' type for remote consoles. + +2.9 +--- + +Add a new ``locked`` attribute to the detailed view, update, +and rebuild action. ``locked`` will be ``true`` if anyone is currently holding +a lock on the server, ``false`` otherwise. + +2.10 +---- + +Added user_id parameter to os-keypairs plugin, as well as a new property +in the request body, for the create operation. + +Administrators will be able to list, get details and delete keypairs owned by +users other than themselves and to create new keypairs on behalf of their +users. + +2.11 +---- + +Exposed attribute ``forced_down`` for ``os-services``. +Added ability to change the ``forced_down`` attribute by calling an update. + +2.12 (Maximum in Liberty) +------------------------- + +Exposes VIF ``net_id`` attribute in ``os-virtual-interfaces``. +User will be able to get Virtual Interfaces ``net_id`` in Virtual Interfaces +list and can determine in which network a Virtual Interface is plugged into. + +2.13 +---- + +Add information ``project_id`` and ``user_id`` to ``os-server-groups`` +API response data. + +2.14 +---- + +Remove ``onSharedStorage`` parameter from server's evacuate action. Nova will +automatically detect if the instance is on shared storage. +Also adminPass is removed from the response body. The user can get the +password with the server's os-server-password action. + +2.15 +---- + +From this version of the API users can choose 'soft-affinity' and +'soft-anti-affinity' rules too for server-groups. + +2.16 +---- + +Exposes new host_status attribute for servers/detail and servers/{server_id}. +Ability to get nova-compute status when querying servers. By default, this is +only exposed to cloud administrators. + +2.17 +---- + +Add a new API for triggering crash dump in an instance. Different operation +systems in instance may need different configurations to trigger crash dump. + +2.18 +---- +Establishes a set of routes that makes project_id an optional construct in v2.1. + +2.19 +---- +Allow the user to set and get the server description. +The user will be able to set the description when creating, rebuilding, +or updating a server, and get the description as part of the server details. + +2.20 +---- +From this version of the API user can call detach and attach volumes for +instances which are in shelved and shelved_offloaded state. + +2.21 +---- + +The ``os-instance-actions`` API now returns information from deleted +instances. + +2.22 +---- + +A new resource servers:migrations added. A new API to force live migration +to complete added:: + + POST /servers//migrations//action + { + "force_complete": null + } + +2.23 +---- + +From this version of the API users can get the migration summary list by +index API or the information of a specific migration by get API. +And the old top-level resource `/os-migrations` won't be extended anymore. +Add migration_type for old /os-migrations API, also add ref link to the +/servers/{uuid}/migrations/{id} for it when the migration is an in-progress +live-migration. + +2.24 +---- + +A new API call to cancel a running live migration:: + + DELETE /servers//migrations/ + +2.25 (Maximum in Mitaka) +------------------------ + +Modify input parameter for ``os-migrateLive``. The block_migration will +support 'auto' value, and disk_over_commit flag will be removed. + +2.26 +---- + +Added support of server tags. + +A user can create, update, delete or check existence of simple string tags +for servers by the os-server-tags plugin. + +Tags have the following schema restrictions: + +* Tag is a Unicode bytestring no longer than 60 characters. +* Tag is a non-empty string. +* '/' is not allowed to be in a tag name +* Comma is not allowed to be in a tag name in order to simplify requests that + specify lists of tags +* All other characters are allowed to be in a tag name +* Each server can have up to 50 tags. + +The resource point for these operations is /servers//tags + +A user can add a single tag to the server by sending PUT request to the +/servers//tags/ + +where is any valid tag name. + +A user can replace **all** current server tags to the new set of tags +by sending PUT request to the /servers//tags. New set of tags +must be specified in request body. This set must be in list 'tags'. + +A user can remove specified tag from the server by sending DELETE request +to the /servers//tags/ + +where is tag name which user wants to remove. + +A user can remove **all** tags from the server by sending DELETE request +to the /servers//tags + +A user can get a set of server tags with information about server by sending +GET request to the /servers/ + +Request returns dictionary with information about specified server, including +list 'tags' :: + + { + 'id': {server_id}, + ... + 'tags': ['foo', 'bar', 'baz'] + } + +A user can get **only** a set of server tags by sending GET request to the +/servers//tags + +Response :: + + { + 'tags': ['foo', 'bar', 'baz'] + } + +A user can check if a tag exists or not on a server by sending +GET /servers/{server_id}/tags/{tag} + +Request returns `204 No Content` if tag exist on a server or `404 Not Found` +if tag doesn't exist on a server. + +A user can filter servers in GET /servers request by new filters: + +* tags +* tags-any +* not-tags +* not-tags-any + +These filters can be combined. Also user can use more than one string tags +for each filter. In this case string tags for each filter must be separated +by comma: GET /servers?tags=red&tags-any=green,orange + +2.27 +---- + +Added support for the new form of microversion headers described in the +`Microversion Specification +`_. +Both the original form of header and the new form is supported. + +2.28 +---- + +Nova API hypervisor.cpu_info change from string to JSON object. + +From this version of the API the hypervisor's 'cpu_info' field will be +will returned as JSON object (not string) by sending GET request +to the /v2.1/os-hypervisors/{hypervisor_id}. + +2.29 +---- + +Updates the POST request body for the ``evacuate`` action to include the +optional ``force`` boolean field defaulted to False. +Also changes the evacuate action behaviour when providing a ``host`` string +field by calling the nova scheduler to verify the provided host unless the +``force`` attribute is set. + +2.30 +---- + +Updates the POST request body for the ``live-migrate`` action to include the +optional ``force`` boolean field defaulted to False. +Also changes the live-migrate action behaviour when providing a ``host`` +string field by calling the nova scheduler to verify the provided host unless +the ``force`` attribute is set. + +2.31 +---- + +Fix os-console-auth-tokens to return connection info for all types of tokens, +not just RDP. + +2.32 +---- + +Adds an optional, arbitrary 'tag' item to the 'networks' item in the server +boot request body. In addition, every item in the block_device_mapping_v2 +array can also have an optional, arbitrary 'tag' item. These tags are used to +identify virtual device metadata, as exposed in the metadata API and on the +config drive. For example, a network interface on the virtual PCI bus tagged +with 'nic1' will appear in the metadata along with its bus (PCI), bus address +(ex: 0000:00:02.0), MAC address, and tag ('nic1'). + +.. note:: A bug has caused the tag attribute to no longer be accepted for + networks starting with version 2.37 and for block_device_mapping_v2 + starting with version 2.33. In other words, networks could only be tagged + between versions 2.32 and 2.36 inclusively and block devices only in + version 2.32. As of version 2.42 the tag attribute has been restored and + both networks and block devices can be tagged again. + +2.33 +---- + +Support pagination for hypervisor by accepting limit and marker from the GET +API request:: + + GET /v2.1/{tenant_id}/os-hypervisors?marker={hypervisor_id}&limit={limit} + +In the context of device tagging at server create time, 2.33 also removes the +tag attribute from block_device_mapping_v2. This is a bug that is fixed in +2.42, in which the tag attribute is reintroduced. + +2.34 +---- + +Checks in ``os-migrateLive`` before live-migration actually starts are now +made in background. ``os-migrateLive`` is not throwing `400 Bad Request` if +pre-live-migration checks fail. + +2.35 +---- + +Added pagination support for keypairs. + +Optional parameters 'limit' and 'marker' were added to GET /os-keypairs +request, the default sort_key was changed to 'name' field as ASC order, +the generic request format is:: + + GET /os-keypairs?limit={limit}&marker={kp_name} + +2.36 +---- + +All the APIs which proxy to another service were deprecated in this version, +also the fping API. Those APIs will return 404 with Microversion 2.36. The +network related quotas and limits are removed from API also. The deprecated +API endpoints as below:: + + '/images' + '/os-networks' + '/os-tenant-networks' + '/os-fixed-ips' + '/os-floating-ips' + '/os-floating-ips-bulk' + '/os-floating-ip-pools' + '/os-floating-ip-dns' + '/os-security-groups' + '/os-security-group-rules' + '/os-security-group-default-rules' + '/os-volumes' + '/os-snapshots' + '/os-baremetal-nodes' + '/os-fping' + +.. note:: A `regression`_ was introduced in this microversion which broke the + ``force`` parameter in the ``PUT /os-quota-sets`` API. The fix will have + to be applied to restore this functionality. + +.. _regression: https://bugs.launchpad.net/nova/+bug/1733886 + +2.37 +---- + +Added support for automatic allocation of networking, also known as "Get Me a +Network". With this microversion, when requesting the creation of a new +server (or servers) the ``networks`` entry in the ``server`` portion of the +request body is required. The ``networks`` object in the request can either +be a list or an enum with values: + +#. *none* which means no networking will be allocated for the created + server(s). +#. *auto* which means either a network that is already available to the + project will be used, or if one does not exist, will be automatically + created for the project. Automatic network allocation for a project only + happens once for a project. Subsequent requests using *auto* for the same + project will reuse the network that was previously allocated. + +Also, the ``uuid`` field in the ``networks`` object in the server create +request is now strictly enforced to be in UUID format. + +In the context of device tagging at server create time, 2.37 also removes the +tag attribute from networks. This is a bug that is fixed in 2.42, in which +the tag attribute is reintroduced. + +2.38 (Maximum in Newton) +------------------------ + +Before version 2.38, the command ``nova list --status invalid_status`` was +returning empty list for non admin user and 500 InternalServerError for admin +user. As there are sufficient statuses defined already, any invalid status +should not be accepted. From this version of the API admin as well as non +admin user will get 400 HTTPBadRequest if invalid status is passed to nova +list command. + +2.39 +---- + +Deprecates image-metadata proxy API that is just a proxy for Glance API +to operate the image metadata. Also removes the extra quota enforcement with +Nova `metadata` quota (quota checks for 'createImage' and 'createBackup' +actions in Nova were removed). After this version Glance configuration +option `image_property_quota` should be used to control the quota of +image metadatas. Also, removes the `maxImageMeta` field from `os-limits` +API response. + +2.40 +---- + +Optional query parameters ``limit`` and ``marker`` were added to the +``os-simple-tenant-usage`` endpoints for pagination. If a limit isn’t +provided, the configurable ``max_limit`` will be used which currently +defaults to 1000. + +:: + + GET /os-simple-tenant-usage?limit={limit}&marker={instance_uuid} + GET /os-simple-tenant-usage/{tenant_id}?limit={limit}&marker={instance_uuid} + +A tenant’s usage statistics may span multiple pages when the number of +instances exceeds limit, and API consumers will need to stitch together +the aggregate results if they still want totals for all instances in a +specific time window, grouped by tenant. + +Older versions of the ``os-simple-tenant-usage`` endpoints will not accept +these new paging query parameters, but they will start to silently limit by +``max_limit`` to encourage the adoption of this new microversion, and +circumvent the existing possibility of DoS-like usage requests when there +are thousands of instances. + +2.41 +---- + +The 'uuid' attribute of an aggregate is now returned from calls to the +`/os-aggregates` endpoint. This attribute is auto-generated upon creation of +an aggregate. The `os-aggregates` API resource endpoint remains an +administrator-only API. + +2.42 (Maximum in Ocata) +----------------------- + +In the context of device tagging at server create time, a bug has caused the +tag attribute to no longer be accepted for networks starting with version +2.37 and for block_device_mapping_v2 starting with version 2.33. Microversion +2.42 restores the tag parameter to both networks and block_device_mapping_v2, +allowing networks and block devices to be tagged again. + +2.43 +---- + +The ``os-hosts`` API is deprecated as of the 2.43 microversion. Requests +made with microversion >= 2.43 will result in a 404 error. To list and show +host details, use the ``os-hypervisors`` API. To enable or disable a +service, use the ``os-services`` API. There is no replacement for the +`shutdown`, `startup`, `reboot`, or `maintenance_mode` actions as those are +system-level operations which should be outside of the control of the +compute service. + +2.44 +---- + +The following APIs which are considered as proxies of Neutron networking API, +are deprecated and will result in a 404 error response in new Microversion:: + + POST /servers/{server_uuid}/action + { + "addFixedIp": {...} + } + + POST /servers/{server_uuid}/action + { + "removeFixedIp": {...} + } + + POST /servers/{server_uuid}/action + { + "addFloatingIp": {...} + } + + POST /servers/{server_uuid}/action + { + "removeFloatingIp": {...} + } + +Those server actions can be replaced by calling the Neutron API directly. + +The nova-network specific API to query the server's interfaces is +deprecated:: + + GET /servers/{server_uuid}/os-virtual-interfaces + +To query attached neutron interfaces for a specific server, the API +`GET /servers/{server_uuid}/os-interface` can be used. + +2.45 +---- + +The ``createImage`` and ``createBackup`` server action APIs no longer return +a ``Location`` header in the response for the snapshot image, they now return +a json dict in the response body with an ``image_id`` key and uuid value. + +2.46 +---- + +The request_id created for every inbound request is now returned in +``X-OpenStack-Request-ID`` in addition to ``X-Compute-Request-ID`` +to be consistent with the rest of OpenStack. This is a signaling +only microversion, as these header settings happen well before +microversion processing. + +2.47 +---- + +Replace the ``flavor`` name/ref with the actual flavor details from the embedded +flavor object when displaying server details. Requests made with microversion +>= 2.47 will no longer return the flavor ID/link but instead will return a +subset of the flavor details. If the user is prevented by policy from +indexing extra-specs, then the ``extra_specs`` field will not be included in the +flavor information. + +2.48 +---- + +Before version 2.48, VM diagnostics response was just a 'blob' of data +returned by each hypervisor. From this version VM diagnostics response is +standardized. It has a set of fields which each hypervisor will try to fill. +If a hypervisor driver is unable to provide a specific field then this field +will be reported as 'None'. + +2.49 +---- + +Continuing from device role tagging at server create time introduced in +version 2.32 and later fixed in 2.42, microversion 2.49 allows the attachment +of network interfaces and volumes with an optional ``tag`` parameter. This tag +is used to identify the virtual devices in the guest and is exposed in the +metadata API. Because the config drive cannot be updated while the guest is +running, it will only contain metadata of devices that were tagged at boot +time. Any changes made to devices while the instance is running - be it +detaching a tagged device or performing a tagged device attachment - will not +be reflected in the config drive. + +Tagged volume attachment is not supported for shelved-offloaded instances. + +2.50 +---- + +The ``server_groups`` and ``server_group_members`` keys are exposed in GET & PUT +``os-quota-class-sets`` APIs Response body. +Networks related quotas have been filtered out from os-quota-class. Below quotas +are filtered out and not available in ``os-quota-class-sets`` APIs from this +microversion onwards. + +- "fixed_ips" +- "floating_ips" +- "networks", +- "security_group_rules" +- "security_groups" + +2.51 +---- + +There are two changes for the 2.51 microversion: + +* Add ``volume-extended`` event name to the ``os-server-external-events`` + API. This will be used by the Block Storage service when extending the size + of an attached volume. This signals the Compute service to perform any + necessary actions on the compute host or hypervisor to adjust for the new + volume block device size. +* Expose the ``events`` field in the response body for the + ``GET /servers/{server_id}/os-instance-actions/{request_id}`` API. This is + useful for API users to monitor when a volume extend operation completes + for the given server instance. By default only users with the administrator + role will be able to see event ``traceback`` details. + +2.52 +---- + +Adds support for applying tags when creating a server. The tag schema is +the same as in the `2.26`_ microversion. + +2.53 (Maximum in Pike) +---------------------- + +**os-services** + +Services are now identified by uuid instead of database id to ensure +uniqueness across cells. This microversion brings the following changes: + +* ``GET /os-services`` returns a uuid in the ``id`` field of the response +* ``DELETE /os-services/{service_uuid}`` requires a service uuid in the path +* The following APIs have been superseded by + ``PUT /os-services/{service_uuid}/``: + + * ``PUT /os-services/disable`` + * ``PUT /os-services/disable-log-reason`` + * ``PUT /os-services/enable`` + * ``PUT /os-services/force-down`` + + ``PUT /os-services/{service_uuid}`` takes the following fields in the body: + + * ``status`` - can be either "enabled" or "disabled" to enable or disable + the given service + * ``disabled_reason`` - specify with status="disabled" to log a reason for + why the service is disabled + * ``forced_down`` - boolean indicating if the service was forced down by + an external service + +* ``PUT /os-services/{service_uuid}`` will now return a full service resource + representation like in a ``GET`` response + +**os-hypervisors** + +Hypervisors are now identified by uuid instead of database id to ensure +uniqueness across cells. This microversion brings the following changes: + +* ``GET /os-hypervisors/{hypervisor_hostname_pattern}/search`` is deprecated + and replaced with the ``hypervisor_hostname_pattern`` query parameter on + the ``GET /os-hypervisors`` and ``GET /os-hypervisors/detail`` APIs. + Paging with ``hypervisor_hostname_pattern`` is not supported. +* ``GET /os-hypervisors/{hypervisor_hostname_pattern}/servers`` is deprecated + and replaced with the ``with_servers`` query parameter on the + ``GET /os-hypervisors`` and ``GET /os-hypervisors/detail`` APIs. +* ``GET /os-hypervisors/{hypervisor_id}`` supports the ``with_servers`` query + parameter to include hosted server details in the response. +* ``GET /os-hypervisors/{hypervisor_id}`` and + ``GET /os-hypervisors/{hypervisor_id}/uptime`` APIs now take a uuid value + for the ``{hypervisor_id}`` path parameter. +* The ``GET /os-hypervisors`` and ``GET /os-hypervisors/detail`` APIs will + now use a uuid marker for paging across cells. +* The following APIs will now return a uuid value for the hypervisor id and + optionally service id fields in the response: + + * ``GET /os-hypervisors`` + * ``GET /os-hypervisors/detail`` + * ``GET /os-hypervisors/{hypervisor_id}`` + * ``GET /os-hypervisors/{hypervisor_id}/uptime`` diff --git a/nova/api/openstack/compute/routes.py b/nova/api/openstack/compute/routes.py new file mode 100644 index 00000000000..7b7d75cca69 --- /dev/null +++ b/nova/api/openstack/compute/routes.py @@ -0,0 +1,913 @@ +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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 functools + +import nova.api.openstack +from nova.api.openstack.compute import admin_actions +from nova.api.openstack.compute import admin_password +from nova.api.openstack.compute import agents +from nova.api.openstack.compute import aggregates +from nova.api.openstack.compute import assisted_volume_snapshots +from nova.api.openstack.compute import attach_interfaces +from nova.api.openstack.compute import availability_zone +from nova.api.openstack.compute import baremetal_nodes +from nova.api.openstack.compute import cells +from nova.api.openstack.compute import certificates +from nova.api.openstack.compute import cloudpipe +from nova.api.openstack.compute import config_drive +from nova.api.openstack.compute import console_auth_tokens +from nova.api.openstack.compute import console_output +from nova.api.openstack.compute import consoles +from nova.api.openstack.compute import create_backup +from nova.api.openstack.compute import deferred_delete +from nova.api.openstack.compute import evacuate +from nova.api.openstack.compute import extended_availability_zone +from nova.api.openstack.compute import extended_server_attributes +from nova.api.openstack.compute import extended_status +from nova.api.openstack.compute import extended_volumes +from nova.api.openstack.compute import extension_info +from nova.api.openstack.compute import fixed_ips +from nova.api.openstack.compute import flavor_access +from nova.api.openstack.compute import flavor_manage +from nova.api.openstack.compute import flavor_rxtx +from nova.api.openstack.compute import flavors +from nova.api.openstack.compute import flavors_extraspecs +from nova.api.openstack.compute import floating_ip_dns +from nova.api.openstack.compute import floating_ip_pools +from nova.api.openstack.compute import floating_ips +from nova.api.openstack.compute import floating_ips_bulk +from nova.api.openstack.compute import fping +from nova.api.openstack.compute import hide_server_addresses +from nova.api.openstack.compute import hosts +from nova.api.openstack.compute import hypervisors +from nova.api.openstack.compute import image_metadata +from nova.api.openstack.compute import image_size +from nova.api.openstack.compute import images +from nova.api.openstack.compute import instance_actions +from nova.api.openstack.compute import instance_usage_audit_log +from nova.api.openstack.compute import ips +from nova.api.openstack.compute import keypairs +from nova.api.openstack.compute import limits +from nova.api.openstack.compute import lock_server +from nova.api.openstack.compute import migrate_server +from nova.api.openstack.compute import migrations +from nova.api.openstack.compute import multinic +from nova.api.openstack.compute import networks +from nova.api.openstack.compute import networks_associate +from nova.api.openstack.compute import pause_server +from nova.api.openstack.compute import quota_classes +from nova.api.openstack.compute import quota_sets +from nova.api.openstack.compute import remote_consoles +from nova.api.openstack.compute import rescue +from nova.api.openstack.compute import security_group_default_rules +from nova.api.openstack.compute import security_groups +from nova.api.openstack.compute import server_diagnostics +from nova.api.openstack.compute import server_external_events +from nova.api.openstack.compute import server_groups +from nova.api.openstack.compute import server_metadata +from nova.api.openstack.compute import server_migrations +from nova.api.openstack.compute import server_password +from nova.api.openstack.compute import server_tags +from nova.api.openstack.compute import server_usage +from nova.api.openstack.compute import servers +from nova.api.openstack.compute import services +from nova.api.openstack.compute import shelve +from nova.api.openstack.compute import simple_tenant_usage +from nova.api.openstack.compute import suspend_server +from nova.api.openstack.compute import tenant_networks +from nova.api.openstack.compute import used_limits +from nova.api.openstack.compute import versionsV21 +from nova.api.openstack.compute import virtual_interfaces +from nova.api.openstack.compute import volumes +from nova.api.openstack import wsgi +import nova.conf +from nova import wsgi as base_wsgi + + +CONF = nova.conf.CONF + + +def _create_controller(main_controller, controller_list, + action_controller_list): + """This is a helper method to create controller with a + list of extended controller. This is for backward compatible + with old extension interface. Finally, the controller for the + same resource will be merged into single one controller. + """ + + controller = wsgi.ResourceV21(main_controller()) + for ctl in controller_list: + controller.register_extensions(ctl()) + for ctl in action_controller_list: + controller.register_actions(ctl()) + return controller + + +agents_controller = functools.partial( + _create_controller, agents.AgentController, [], []) + + +aggregates_controller = functools.partial( + _create_controller, aggregates.AggregateController, [], []) + + +assisted_volume_snapshots_controller = functools.partial( + _create_controller, + assisted_volume_snapshots.AssistedVolumeSnapshotsController, [], []) + + +availability_zone_controller = functools.partial( + _create_controller, availability_zone.AvailabilityZoneController, [], []) + + +baremetal_nodes_controller = functools.partial( + _create_controller, baremetal_nodes.BareMetalNodeController, [], []) + + +cells_controller = functools.partial( + _create_controller, cells.CellsController, [], []) + + +certificates_controller = functools.partial( + _create_controller, certificates.CertificatesController, [], []) + + +cloudpipe_controller = functools.partial( + _create_controller, cloudpipe.CloudpipeController, [], []) + + +extensions_controller = functools.partial( + _create_controller, extension_info.ExtensionInfoController, [], []) + + +fixed_ips_controller = functools.partial(_create_controller, + fixed_ips.FixedIPController, [], []) + + +flavor_controller = functools.partial(_create_controller, + flavors.FlavorsController, + [ + flavor_rxtx.FlavorRxtxController, + flavor_access.FlavorActionController + ], + [ + flavor_manage.FlavorManageController, + flavor_access.FlavorActionController + ] +) + + +flavor_access_controller = functools.partial(_create_controller, + flavor_access.FlavorAccessController, [], []) + + +flavor_extraspec_controller = functools.partial(_create_controller, + flavors_extraspecs.FlavorExtraSpecsController, [], []) + + +floating_ip_dns_controller = functools.partial(_create_controller, + floating_ip_dns.FloatingIPDNSDomainController, [], []) + + +floating_ip_dnsentry_controller = functools.partial(_create_controller, + floating_ip_dns.FloatingIPDNSEntryController, [], []) + + +floating_ip_pools_controller = functools.partial(_create_controller, + floating_ip_pools.FloatingIPPoolsController, [], []) + + +floating_ips_controller = functools.partial(_create_controller, + floating_ips.FloatingIPController, [], []) + + +floating_ips_bulk_controller = functools.partial(_create_controller, + floating_ips_bulk.FloatingIPBulkController, [], []) + + +fping_controller = functools.partial(_create_controller, + fping.FpingController, [], []) + + +hosts_controller = functools.partial( + _create_controller, hosts.HostController, [], []) + + +hypervisors_controller = functools.partial( + _create_controller, hypervisors.HypervisorsController, [], []) + + +images_controller = functools.partial( + _create_controller, images.ImagesController, + [image_size.ImageSizeController], []) + + +image_metadata_controller = functools.partial( + _create_controller, image_metadata.ImageMetadataController, + [], []) + + +instance_actions_controller = functools.partial(_create_controller, + instance_actions.InstanceActionsController, [], []) + + +instance_usage_audit_log_controller = functools.partial(_create_controller, + instance_usage_audit_log.InstanceUsageAuditLogController, [], []) + + +ips_controller = functools.partial(_create_controller, + ips.IPsController, [], []) + + +keypairs_controller = functools.partial( + _create_controller, keypairs.KeypairController, [], []) + + +limits_controller = functools.partial( + _create_controller, limits.LimitsController, + [ + used_limits.UsedLimitsController, + ], + []) + + +migrations_controller = functools.partial(_create_controller, + migrations.MigrationsController, [], []) + + +networks_controller = functools.partial(_create_controller, + networks.NetworkController, [], + [networks_associate.NetworkAssociateActionController]) + + +quota_classes_controller = functools.partial(_create_controller, + quota_classes.QuotaClassSetsController, [], []) + + +quota_set_controller = functools.partial(_create_controller, + quota_sets.QuotaSetsController, [], []) + + +security_group_controller = functools.partial(_create_controller, + security_groups.SecurityGroupController, [], []) + + +security_group_default_rules_controller = functools.partial(_create_controller, + security_group_default_rules.SecurityGroupDefaultRulesController, [], []) + + +security_group_rules_controller = functools.partial(_create_controller, + security_groups.SecurityGroupRulesController, [], []) + + +server_controller = functools.partial(_create_controller, + servers.ServersController, + [ + config_drive.ConfigDriveController, + extended_availability_zone.ExtendedAZController, + extended_server_attributes.ExtendedServerAttributesController, + extended_status.ExtendedStatusController, + extended_volumes.ExtendedVolumesController, + hide_server_addresses.Controller, + keypairs.Controller, + security_groups.SecurityGroupsOutputController, + server_usage.ServerUsageController, + ], + [ + admin_actions.AdminActionsController, + admin_password.AdminPasswordController, + console_output.ConsoleOutputController, + create_backup.CreateBackupController, + deferred_delete.DeferredDeleteController, + evacuate.EvacuateController, + floating_ips.FloatingIPActionController, + lock_server.LockServerController, + migrate_server.MigrateServerController, + multinic.MultinicController, + pause_server.PauseServerController, + remote_consoles.RemoteConsolesController, + rescue.RescueController, + security_groups.SecurityGroupActionController, + shelve.ShelveController, + suspend_server.SuspendServerController + ] +) + + +console_auth_tokens_controller = functools.partial(_create_controller, + console_auth_tokens.ConsoleAuthTokensController, [], []) + + +consoles_controller = functools.partial(_create_controller, + consoles.ConsolesController, [], []) + + +server_diagnostics_controller = functools.partial(_create_controller, + server_diagnostics.ServerDiagnosticsController, [], []) + + +server_external_events_controller = functools.partial(_create_controller, + server_external_events.ServerExternalEventsController, [], []) + + +server_groups_controller = functools.partial(_create_controller, + server_groups.ServerGroupController, [], []) + + +server_metadata_controller = functools.partial(_create_controller, + server_metadata.ServerMetadataController, [], []) + + +server_migrations_controller = functools.partial(_create_controller, + server_migrations.ServerMigrationsController, [], []) + + +server_os_interface_controller = functools.partial(_create_controller, + attach_interfaces.InterfaceAttachmentController, [], []) + + +server_password_controller = functools.partial(_create_controller, + server_password.ServerPasswordController, [], []) + + +server_remote_consoles_controller = functools.partial(_create_controller, + remote_consoles.RemoteConsolesController, [], []) + + +server_security_groups_controller = functools.partial(_create_controller, + security_groups.ServerSecurityGroupController, [], []) + + +server_tags_controller = functools.partial(_create_controller, + server_tags.ServerTagsController, [], []) + + +server_volume_attachments_controller = functools.partial(_create_controller, + volumes.VolumeAttachmentController, [], []) + + +services_controller = functools.partial(_create_controller, + services.ServiceController, [], []) + + +simple_tenant_usage_controller = functools.partial(_create_controller, + simple_tenant_usage.SimpleTenantUsageController, [], []) + + +snapshots_controller = functools.partial(_create_controller, + volumes.SnapshotController, [], []) + + +tenant_networks_controller = functools.partial(_create_controller, + tenant_networks.TenantNetworkController, [], []) + + +version_controller = functools.partial(_create_controller, + versionsV21.VersionsController, [], []) + + +virtual_interfaces_controller = functools.partial(_create_controller, + virtual_interfaces.ServerVirtualInterfaceController, [], []) + + +volumes_controller = functools.partial(_create_controller, + volumes.VolumeController, [], []) + + +# NOTE(alex_xu): This is structure of this route list as below: +# ( +# ('Route path': { +# 'HTTP method: [ +# 'Controller', +# 'The method of controller is used to handle this route' +# ], +# ... +# }), +# ... +# ) +# +# Also note that this is ordered tuple. For example, the '/servers/detail' +# should be in the front of '/servers/{id}', otherwise the request to +# '/servers/detail' always matches to '/servers/{id}' as the id is 'detail'. +ROUTE_LIST = ( + # NOTE: This is a redirection from '' to '/'. The request to the '/v2.1' + # or '/2.0' without the ending '/' will get a response with status code + # '302' returned. + ('', '/'), + ('/', { + 'GET': [version_controller, 'show'] + }), + ('/versions/{id}', { + 'GET': [version_controller, 'show'] + }), + ('/extensions', { + 'GET': [extensions_controller, 'index'], + }), + ('/extensions/{id}', { + 'GET': [extensions_controller, 'show'], + }), + ('/flavors', { + 'GET': [flavor_controller, 'index'], + 'POST': [flavor_controller, 'create'] + }), + ('/flavors/detail', { + 'GET': [flavor_controller, 'detail'] + }), + ('/flavors/{id}', { + 'GET': [flavor_controller, 'show'], + 'DELETE': [flavor_controller, 'delete'] + }), + ('/flavors/{id}/action', { + 'POST': [flavor_controller, 'action'] + }), + ('/flavors/{flavor_id}/os-extra_specs', { + 'GET': [flavor_extraspec_controller, 'index'], + 'POST': [flavor_extraspec_controller, 'create'] + }), + ('/flavors/{flavor_id}/os-extra_specs/{id}', { + 'GET': [flavor_extraspec_controller, 'show'], + 'PUT': [flavor_extraspec_controller, 'update'], + 'DELETE': [flavor_extraspec_controller, 'delete'] + }), + ('/flavors/{flavor_id}/os-flavor-access', { + 'GET': [flavor_access_controller, 'index'] + }), + ('/images', { + 'GET': [images_controller, 'index'] + }), + ('/images/detail', { + 'GET': [images_controller, 'detail'], + }), + ('/images/{id}', { + 'GET': [images_controller, 'show'], + 'DELETE': [images_controller, 'delete'] + }), + ('/images/{image_id}/metadata', { + 'GET': [image_metadata_controller, 'index'], + 'POST': [image_metadata_controller, 'create'], + 'PUT': [image_metadata_controller, 'update_all'] + }), + ('/images/{image_id}/metadata/{id}', { + 'GET': [image_metadata_controller, 'show'], + 'PUT': [image_metadata_controller, 'update'], + 'DELETE': [image_metadata_controller, 'delete'] + }), + ('/limits', { + 'GET': [limits_controller, 'index'] + }), + ('/os-agents', { + 'GET': [agents_controller, 'index'], + 'POST': [agents_controller, 'create'] + }), + ('/os-agents/{id}', { + 'PUT': [agents_controller, 'update'], + 'DELETE': [agents_controller, 'delete'] + }), + ('/os-aggregates', { + 'GET': [aggregates_controller, 'index'], + 'POST': [aggregates_controller, 'create'] + }), + ('/os-aggregates/{id}', { + 'GET': [aggregates_controller, 'show'], + 'PUT': [aggregates_controller, 'update'], + 'DELETE': [aggregates_controller, 'delete'] + }), + ('/os-aggregates/{id}/action', { + 'POST': [aggregates_controller, 'action'], + }), + ('/os-assisted-volume-snapshots', { + 'POST': [assisted_volume_snapshots_controller, 'create'] + }), + ('/os-assisted-volume-snapshots/{id}', { + 'DELETE': [assisted_volume_snapshots_controller, 'delete'] + }), + ('/os-availability-zone', { + 'GET': [availability_zone_controller, 'index'] + }), + ('/os-availability-zone/detail', { + 'GET': [availability_zone_controller, 'detail'], + }), + ('/os-baremetal-nodes', { + 'GET': [baremetal_nodes_controller, 'index'], + 'POST': [baremetal_nodes_controller, 'create'] + }), + ('/os-baremetal-nodes/{id}', { + 'GET': [baremetal_nodes_controller, 'show'], + 'DELETE': [baremetal_nodes_controller, 'delete'] + }), + ('/os-baremetal-nodes/{id}/action', { + 'POST': [baremetal_nodes_controller, 'action'] + }), + ('/os-cells', { + 'POST': [cells_controller, 'create'], + 'GET': [cells_controller, 'index'], + }), + ('/os-cells/capacities', { + 'GET': [cells_controller, 'capacities'] + }), + ('/os-cells/detail', { + 'GET': [cells_controller, 'detail'] + }), + ('/os-cells/info', { + 'GET': [cells_controller, 'info'] + }), + ('/os-cells/sync_instances', { + 'POST': [cells_controller, 'sync_instances'] + }), + ('/os-cells/{id}', { + 'GET': [cells_controller, 'show'], + 'PUT': [cells_controller, 'update'], + 'DELETE': [cells_controller, 'delete'] + }), + ('/os-cells/{id}/capacities', { + 'GET': [cells_controller, 'capacities'] + }), + ('/os-certificates', { + 'POST': [certificates_controller, 'create'] + }), + ('/os-certificates/{id}', { + 'GET': [certificates_controller, 'show'] + }), + ('/os-cloudpipe', { + 'GET': [cloudpipe_controller, 'index'], + 'POST': [cloudpipe_controller, 'create'] + }), + ('/os-cloudpipe/{id}', { + 'PUT': [cloudpipe_controller, 'update'] + }), + ('/os-console-auth-tokens/{id}', { + 'GET': [console_auth_tokens_controller, 'show'] + }), + ('/os-fixed-ips/{id}', { + 'GET': [fixed_ips_controller, 'show'] + }), + ('/os-fixed-ips/{id}/action', { + 'POST': [fixed_ips_controller, 'action'], + }), + ('/os-floating-ip-dns', { + 'GET': [floating_ip_dns_controller, 'index'] + }), + ('/os-floating-ip-dns/{id}', { + 'PUT': [floating_ip_dns_controller, 'update'], + 'DELETE': [floating_ip_dns_controller, 'delete'] + }), + ('/os-floating-ip-dns/{domain_id}/entries/{id}', { + 'GET': [floating_ip_dnsentry_controller, 'show'], + 'PUT': [floating_ip_dnsentry_controller, 'update'], + 'DELETE': [floating_ip_dnsentry_controller, 'delete'] + }), + ('/os-floating-ip-pools', { + 'GET': [floating_ip_pools_controller, 'index'], + }), + ('/os-floating-ips', { + 'GET': [floating_ips_controller, 'index'], + 'POST': [floating_ips_controller, 'create'] + }), + ('/os-floating-ips/{id}', { + 'GET': [floating_ips_controller, 'show'], + 'DELETE': [floating_ips_controller, 'delete'] + }), + ('/os-floating-ips-bulk', { + 'GET': [floating_ips_bulk_controller, 'index'], + 'POST': [floating_ips_bulk_controller, 'create'] + }), + ('/os-floating-ips-bulk/{id}', { + 'GET': [floating_ips_bulk_controller, 'show'], + 'PUT': [floating_ips_bulk_controller, 'update'] + }), + ('/os-fping', { + 'GET': [fping_controller, 'index'] + }), + ('/os-fping/{id}', { + 'GET': [fping_controller, 'show'] + }), + ('/os-hosts', { + 'GET': [hosts_controller, 'index'] + }), + ('/os-hosts/{id}', { + 'GET': [hosts_controller, 'show'], + 'PUT': [hosts_controller, 'update'] + }), + ('/os-hosts/{id}/reboot', { + 'GET': [hosts_controller, 'reboot'] + }), + ('/os-hosts/{id}/shutdown', { + 'GET': [hosts_controller, 'shutdown'] + }), + ('/os-hosts/{id}/startup', { + 'GET': [hosts_controller, 'startup'] + }), + ('/os-hypervisors', { + 'GET': [hypervisors_controller, 'index'] + }), + ('/os-hypervisors/detail', { + 'GET': [hypervisors_controller, 'detail'] + }), + ('/os-hypervisors/statistics', { + 'GET': [hypervisors_controller, 'statistics'] + }), + ('/os-hypervisors/{id}', { + 'GET': [hypervisors_controller, 'show'] + }), + ('/os-hypervisors/{id}/search', { + 'GET': [hypervisors_controller, 'search'] + }), + ('/os-hypervisors/{id}/servers', { + 'GET': [hypervisors_controller, 'servers'] + }), + ('/os-hypervisors/{id}/uptime', { + 'GET': [hypervisors_controller, 'uptime'] + }), + ('/os-instance_usage_audit_log', { + 'GET': [instance_usage_audit_log_controller, 'index'] + }), + ('/os-instance_usage_audit_log/{id}', { + 'GET': [instance_usage_audit_log_controller, 'show'] + }), + ('/os-keypairs', { + 'GET': [keypairs_controller, 'index'], + 'POST': [keypairs_controller, 'create'] + }), + ('/os-keypairs/{id}', { + 'GET': [keypairs_controller, 'show'], + 'DELETE': [keypairs_controller, 'delete'] + }), + ('/os-migrations', { + 'GET': [migrations_controller, 'index'] + }), + ('/os-networks', { + 'GET': [networks_controller, 'index'], + 'POST': [networks_controller, 'create'] + }), + ('/os-networks/add', { + 'POST': [networks_controller, 'add'] + }), + ('/os-networks/{id}', { + 'GET': [networks_controller, 'show'], + 'DELETE': [networks_controller, 'delete'] + }), + ('/os-networks/{id}/action', { + 'POST': [networks_controller, 'action'], + }), + ('/os-quota-class-sets/{id}', { + 'GET': [quota_classes_controller, 'show'], + 'PUT': [quota_classes_controller, 'update'] + }), + ('/os-quota-sets/{id}', { + 'GET': [quota_set_controller, 'show'], + 'PUT': [quota_set_controller, 'update'], + 'DELETE': [quota_set_controller, 'delete'] + }), + ('/os-quota-sets/{id}/detail', { + 'GET': [quota_set_controller, 'detail'] + }), + ('/os-quota-sets/{id}/defaults', { + 'GET': [quota_set_controller, 'defaults'] + }), + ('/os-security-group-default-rules', { + 'GET': [security_group_default_rules_controller, 'index'], + 'POST': [security_group_default_rules_controller, 'create'] + }), + ('/os-security-group-default-rules/{id}', { + 'GET': [security_group_default_rules_controller, 'show'], + 'DELETE': [security_group_default_rules_controller, 'delete'] + }), + ('/os-security-group-rules', { + 'POST': [security_group_rules_controller, 'create'] + }), + ('/os-security-group-rules/{id}', { + 'DELETE': [security_group_rules_controller, 'delete'] + }), + ('/os-security-groups', { + 'GET': [security_group_controller, 'index'], + 'POST': [security_group_controller, 'create'] + }), + ('/os-security-groups/{id}', { + 'GET': [security_group_controller, 'show'], + 'PUT': [security_group_controller, 'update'], + 'DELETE': [security_group_controller, 'delete'] + }), + ('/os-server-external-events', { + 'POST': [server_external_events_controller, 'create'] + }), + ('/os-server-groups', { + 'GET': [server_groups_controller, 'index'], + 'POST': [server_groups_controller, 'create'] + }), + ('/os-server-groups/{id}', { + 'GET': [server_groups_controller, 'show'], + 'DELETE': [server_groups_controller, 'delete'] + }), + ('/os-services', { + 'GET': [services_controller, 'index'] + }), + ('/os-services/{id}', { + 'PUT': [services_controller, 'update'], + 'DELETE': [services_controller, 'delete'] + }), + ('/os-simple-tenant-usage', { + 'GET': [simple_tenant_usage_controller, 'index'] + }), + ('/os-simple-tenant-usage/{id}', { + 'GET': [simple_tenant_usage_controller, 'show'] + }), + ('/os-snapshots', { + 'GET': [snapshots_controller, 'index'], + 'POST': [snapshots_controller, 'create'] + }), + ('/os-snapshots/detail', { + 'GET': [snapshots_controller, 'detail'] + }), + ('/os-snapshots/{id}', { + 'GET': [snapshots_controller, 'show'], + 'DELETE': [snapshots_controller, 'delete'] + }), + ('/os-tenant-networks', { + 'GET': [tenant_networks_controller, 'index'], + 'POST': [tenant_networks_controller, 'create'] + }), + ('/os-tenant-networks/{id}', { + 'GET': [tenant_networks_controller, 'show'], + 'DELETE': [tenant_networks_controller, 'delete'] + }), + ('/os-volumes', { + 'GET': [volumes_controller, 'index'], + 'POST': [volumes_controller, 'create'], + }), + ('/os-volumes/detail', { + 'GET': [volumes_controller, 'detail'], + }), + ('/os-volumes/{id}', { + 'GET': [volumes_controller, 'show'], + 'DELETE': [volumes_controller, 'delete'] + }), + # NOTE: '/os-volumes_boot' is a clone of '/servers'. We may want to + # deprecate it in the future. + ('/os-volumes_boot', { + 'GET': [server_controller, 'index'], + 'POST': [server_controller, 'create'] + }), + ('/os-volumes_boot/detail', { + 'GET': [server_controller, 'detail'] + }), + ('/os-volumes_boot/{id}', { + 'GET': [server_controller, 'show'], + 'PUT': [server_controller, 'update'], + 'DELETE': [server_controller, 'delete'] + }), + ('/os-volumes_boot/{id}/action', { + 'POST': [server_controller, 'action'] + }), + ('/servers', { + 'GET': [server_controller, 'index'], + 'POST': [server_controller, 'create'] + }), + ('/servers/detail', { + 'GET': [server_controller, 'detail'] + }), + ('/servers/{id}', { + 'GET': [server_controller, 'show'], + 'PUT': [server_controller, 'update'], + 'DELETE': [server_controller, 'delete'] + }), + ('/servers/{id}/action', { + 'POST': [server_controller, 'action'] + }), + ('/servers/{server_id}/consoles', { + 'GET': [consoles_controller, 'index'], + 'POST': [consoles_controller, 'create'] + }), + ('/servers/{server_id}/consoles/{id}', { + 'GET': [consoles_controller, 'show'], + 'DELETE': [consoles_controller, 'delete'] + }), + ('/servers/{server_id}/diagnostics', { + 'GET': [server_diagnostics_controller, 'index'] + }), + ('/servers/{server_id}/ips', { + 'GET': [ips_controller, 'index'] + }), + ('/servers/{server_id}/ips/{id}', { + 'GET': [ips_controller, 'show'] + }), + ('/servers/{server_id}/metadata', { + 'GET': [server_metadata_controller, 'index'], + 'POST': [server_metadata_controller, 'create'], + 'PUT': [server_metadata_controller, 'update_all'], + }), + ('/servers/{server_id}/metadata/{id}', { + 'GET': [server_metadata_controller, 'show'], + 'PUT': [server_metadata_controller, 'update'], + 'DELETE': [server_metadata_controller, 'delete'], + }), + ('/servers/{server_id}/migrations', { + 'GET': [server_migrations_controller, 'index'] + }), + ('/servers/{server_id}/migrations/{id}', { + 'GET': [server_migrations_controller, 'show'], + 'DELETE': [server_migrations_controller, 'delete'] + }), + ('/servers/{server_id}/migrations/{id}/action', { + 'POST': [server_migrations_controller, 'action'] + }), + ('/servers/{server_id}/os-instance-actions', { + 'GET': [instance_actions_controller, 'index'] + }), + ('/servers/{server_id}/os-instance-actions/{id}', { + 'GET': [instance_actions_controller, 'show'] + }), + ('/servers/{server_id}/os-interface', { + 'GET': [server_os_interface_controller, 'index'], + 'POST': [server_os_interface_controller, 'create'] + }), + ('/servers/{server_id}/os-interface/{id}', { + 'GET': [server_os_interface_controller, 'show'], + 'DELETE': [server_os_interface_controller, 'delete'] + }), + ('/servers/{server_id}/os-server-password', { + 'GET': [server_password_controller, 'index'], + 'DELETE': [server_password_controller, 'clear'] + }), + ('/servers/{server_id}/os-virtual-interfaces', { + 'GET': [virtual_interfaces_controller, 'index'] + }), + ('/servers/{server_id}/os-volume_attachments', { + 'GET': [server_volume_attachments_controller, 'index'], + 'POST': [server_volume_attachments_controller, 'create'], + }), + ('/servers/{server_id}/os-volume_attachments/{id}', { + 'GET': [server_volume_attachments_controller, 'show'], + 'PUT': [server_volume_attachments_controller, 'update'], + 'DELETE': [server_volume_attachments_controller, 'delete'] + }), + ('/servers/{server_id}/remote-consoles', { + 'POST': [server_remote_consoles_controller, 'create'] + }), + ('/servers/{server_id}/os-security-groups', { + 'GET': [server_security_groups_controller, 'index'] + }), + ('/servers/{server_id}/tags', { + 'GET': [server_tags_controller, 'index'], + 'PUT': [server_tags_controller, 'update_all'], + 'DELETE': [server_tags_controller, 'delete_all'], + }), + ('/servers/{server_id}/tags/{id}', { + 'GET': [server_tags_controller, 'show'], + 'PUT': [server_tags_controller, 'update'], + 'DELETE': [server_tags_controller, 'delete'] + }), +) + + +class APIRouterV21(base_wsgi.Router): + """Routes requests on the OpenStack API to the appropriate controller + and method. The URL mapping based on the plain list `ROUTE_LIST` is built + at here. + """ + def __init__(self, custom_routes=None): + """:param custom_routes: the additional routes can be added by this + parameter. This parameter is used to test on some fake routes + primarily. + """ + super(APIRouterV21, self).__init__(nova.api.openstack.ProjectMapper()) + + if custom_routes is None: + custom_routes = tuple() + + for path, methods in ROUTE_LIST + custom_routes: + # NOTE(alex_xu): The variable 'methods' is a dict in normal, since + # the dict includes all the methods supported in the path. But + # if the variable 'method' is a string, it means a redirection. + # For example, the request to the '' will be redirect to the '/' in + # the Nova API. To indicate that, using the target path instead of + # a dict. The route entry just writes as "('', '/)". + if isinstance(methods, str): + self.map.redirect(path, methods) + continue + + for method, controller_info in methods.items(): + # TODO(alex_xu): In the end, I want to create single controller + # instance instead of create controller instance for each + # route. + controller = controller_info[0]() + action = controller_info[1] + self.map.create_route(path, method, controller, action) + + @classmethod + def factory(cls, global_config, **local_config): + """Simple paste factory, :class:`nova.wsgi.Router` doesn't have one.""" + return cls() diff --git a/nova/api/openstack/compute/scheduler_hints.py b/nova/api/openstack/compute/scheduler_hints.py index 35f446c7561..f38ba770253 100644 --- a/nova/api/openstack/compute/scheduler_hints.py +++ b/nova/api/openstack/compute/scheduler_hints.py @@ -13,36 +13,21 @@ # under the License. from nova.api.openstack.compute.schemas import scheduler_hints as schema -from nova.api.openstack import extensions -ALIAS = "os-scheduler-hints" +# NOTE(gmann): Accepting request body in this function to fetch "scheduler +# hint". This is a workaround to allow OS_SCH-HNT at the top level +# of the body request, but that it will be changed in the future to be a +# subset of the servers dict. +def server_create(server_dict, create_kwargs, req_body): + scheduler_hints = {} + if 'os:scheduler_hints' in req_body: + scheduler_hints = req_body['os:scheduler_hints'] + elif 'OS-SCH-HNT:scheduler_hints' in req_body: + scheduler_hints = req_body['OS-SCH-HNT:scheduler_hints'] -class SchedulerHints(extensions.V21APIExtensionBase): - """Pass arbitrary key/value pairs to the scheduler.""" + create_kwargs['scheduler_hints'] = scheduler_hints - name = "SchedulerHints" - alias = ALIAS - version = 1 - def get_controller_extensions(self): - return [] - - def get_resources(self): - return [] - - # NOTE(gmann): Accepting request body in this function to fetch "scheduler - # hint". This is a workaround to allow OS_SCH-HNT at the top level - # of the body request, but that it will be changed in the future to be a - # subset of the servers dict. - def server_create(self, server_dict, create_kwargs, req_body): - scheduler_hints = {} - if 'os:scheduler_hints' in req_body: - scheduler_hints = req_body['os:scheduler_hints'] - elif 'OS-SCH-HNT:scheduler_hints' in req_body: - scheduler_hints = req_body['OS-SCH-HNT:scheduler_hints'] - - create_kwargs['scheduler_hints'] = scheduler_hints - - def get_server_create_schema(self, version): - return schema.server_create +def get_server_create_schema(version): + return schema.server_create diff --git a/nova/api/openstack/compute/schemas/access_ips.py b/nova/api/openstack/compute/schemas/access_ips.py deleted file mode 100644 index 37d3eea4b1b..00000000000 --- a/nova/api/openstack/compute/schemas/access_ips.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2014 NEC Corporation. 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. - -server_create = { - 'accessIPv4': { - 'type': 'string', - 'format': 'ipv4', - }, - 'accessIPv6': { - 'type': 'string', - 'format': 'ipv6', - } -} diff --git a/nova/api/openstack/compute/schemas/aggregates.py b/nova/api/openstack/compute/schemas/aggregates.py index 5283429e78e..396e4fe31c0 100644 --- a/nova/api/openstack/compute/schemas/aggregates.py +++ b/nova/api/openstack/compute/schemas/aggregates.py @@ -16,9 +16,9 @@ from nova.api.validation import parameter_types -availability_zone = {'oneOf': [parameter_types.name, {'type': 'null'}]} +availability_zone = {'oneOf': [parameter_types.az_name, {'type': 'null'}]} availability_zone_with_leading_trailing_spaces = { - 'oneOf': [parameter_types.name_with_leading_trailing_spaces, + 'oneOf': [parameter_types.az_name_with_leading_trailing_spaces, {'type': 'null'}] } diff --git a/nova/api/openstack/compute/schemas/assisted_volume_snapshots.py b/nova/api/openstack/compute/schemas/assisted_volume_snapshots.py index 4cb2b71bd66..0b7f3ba2522 100644 --- a/nova/api/openstack/compute/schemas/assisted_volume_snapshots.py +++ b/nova/api/openstack/compute/schemas/assisted_volume_snapshots.py @@ -43,8 +43,8 @@ }, 'required': ['volume_id', 'create_info'], 'additionalProperties': False, - }, - 'required': ['snapshot'], - 'additionalProperties': False, + } }, + 'required': ['snapshot'], + 'additionalProperties': False, } diff --git a/nova/api/openstack/compute/schemas/attach_interfaces.py b/nova/api/openstack/compute/schemas/attach_interfaces.py index fa966181d16..1c06887dd3d 100644 --- a/nova/api/openstack/compute/schemas/attach_interfaces.py +++ b/nova/api/openstack/compute/schemas/attach_interfaces.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + from nova.api.validation import parameter_types @@ -44,3 +46,7 @@ }, 'additionalProperties': False, } + +create_v249 = copy.deepcopy(create) +create_v249['properties']['interfaceAttachment'][ + 'properties']['tag'] = parameter_types.tag diff --git a/nova/api/openstack/compute/schemas/block_device_mapping.py b/nova/api/openstack/compute/schemas/block_device_mapping.py index ec20c944fc9..667b95b0669 100644 --- a/nova/api/openstack/compute/schemas/block_device_mapping.py +++ b/nova/api/openstack/compute/schemas/block_device_mapping.py @@ -32,10 +32,9 @@ 'pattern': '^[a-zA-Z0-9._-]*$', }, 'image_id': parameter_types.image_id, - # Defined as varchar(255) in column "destination_type" in table - # "block_device_mapping" 'destination_type': { - 'type': 'string', 'maxLength': 255, + 'type': 'string', + 'enum': ['local', 'volume'], }, # Defined as varchar(255) in column "guest_format" in table # "block_device_mapping" @@ -53,8 +52,10 @@ 'type': 'string', 'maxLength': 255, }, # Defined as integer in nova/block_device.py:from_api() + # NOTE(mriedem): boot_index=None is also accepted for backward + # compatibility with the legacy v2 API. 'boot_index': { - 'type': ['integer', 'string'], + 'type': ['integer', 'string', 'null'], 'pattern': '^-?[0-9]+$', }, } @@ -66,6 +67,21 @@ server_create = { 'block_device_mapping_v2': { 'type': 'array', - 'items': [block_device_mapping] + 'items': block_device_mapping + } +} + +block_device_mapping_with_tags_new_item = { + 'tag': parameter_types.tag +} + +block_device_mapping_with_tags = copy.deepcopy(block_device_mapping) +block_device_mapping_with_tags['properties'].update( + block_device_mapping_with_tags_new_item) + +server_create_with_tags = { + 'block_device_mapping_v2': { + 'type': 'array', + 'items': block_device_mapping_with_tags } } diff --git a/nova/api/openstack/compute/schemas/block_device_mapping_v1.py b/nova/api/openstack/compute/schemas/block_device_mapping_v1.py index d4e6e9f8366..91ca3a2ec9e 100644 --- a/nova/api/openstack/compute/schemas/block_device_mapping_v1.py +++ b/nova/api/openstack/compute/schemas/block_device_mapping_v1.py @@ -22,7 +22,7 @@ }, 'volume_id': parameter_types.volume_id, 'snapshot_id': parameter_types.image_id, - 'volume_size': parameter_types.non_negative_integer, + 'volume_size': parameter_types.volume_size, # Do not allow empty device names and number values and # containing spaces(defined in nova/block_device.py:from_api()) 'device_name': { @@ -44,6 +44,6 @@ server_create = { 'block_device_mapping': { 'type': 'array', - 'items': [legacy_block_device_mapping] + 'items': legacy_block_device_mapping } } diff --git a/nova/api/openstack/compute/schemas/cells.py b/nova/api/openstack/compute/schemas/cells.py index 1115aeaf7cc..f0d52f6792b 100644 --- a/nova/api/openstack/compute/schemas/cells.py +++ b/nova/api/openstack/compute/schemas/cells.py @@ -33,7 +33,7 @@ # following parameters: # "qpid://:@:/" # or - # "rabiit://:@:/" + # "rabbit://:@:/" # Then the url is stored into transport_url of cells table # which is defined with String(255). 'username': { diff --git a/nova/api/openstack/compute/schemas/cloudpipe.py b/nova/api/openstack/compute/schemas/cloudpipe.py deleted file mode 100644 index 43d72d0f404..00000000000 --- a/nova/api/openstack/compute/schemas/cloudpipe.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2014 IBM Corporation. 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. - -from nova.api.validation import parameter_types - - -create = { - 'type': 'object', - 'properties': { - 'cloudpipe': { - 'type': 'object', - 'properties': { - 'project_id': parameter_types.project_id, - }, - 'additionalProperties': False, - }, - }, - 'required': ['cloudpipe'], - 'additionalProperties': False, -} - -update = { - 'type': 'object', - 'properties': { - 'configure_project': { - 'type': 'object', - 'properties': { - 'vpn_ip': parameter_types.ip_address, - 'vpn_port': parameter_types.tcp_udp_port, - }, - 'required': ['vpn_ip', 'vpn_port'], - 'additionalProperties': False, - }, - }, - 'required': ['configure_project'], - 'additionalProperties': False, -} diff --git a/nova/api/openstack/compute/schemas/create_backup.py b/nova/api/openstack/compute/schemas/create_backup.py index 452ac78acac..29401c853b4 100644 --- a/nova/api/openstack/compute/schemas/create_backup.py +++ b/nova/api/openstack/compute/schemas/create_backup.py @@ -28,9 +28,7 @@ 'type': 'string', }, 'rotation': parameter_types.non_negative_integer, - 'metadata': { - 'type': 'object', - } + 'metadata': parameter_types.metadata, }, 'required': ['name', 'backup_type', 'rotation'], 'additionalProperties': False, diff --git a/nova/api/openstack/compute/schemas/disk_config.py b/nova/api/openstack/compute/schemas/disk_config.py deleted file mode 100644 index 0b2fda93e59..00000000000 --- a/nova/api/openstack/compute/schemas/disk_config.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2014 NEC Corporation. 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. - -server_create = { - 'OS-DCF:diskConfig': { - 'type': 'string', - 'enum': ['AUTO', 'MANUAL'] - } -} diff --git a/nova/api/openstack/compute/schemas/evacuate.py b/nova/api/openstack/compute/schemas/evacuate.py index 51fb05410fc..439ab2d3fd1 100644 --- a/nova/api/openstack/compute/schemas/evacuate.py +++ b/nova/api/openstack/compute/schemas/evacuate.py @@ -38,3 +38,7 @@ evacuate_v214 = copy.deepcopy(evacuate) del evacuate_v214['properties']['evacuate']['properties']['onSharedStorage'] del evacuate_v214['properties']['evacuate']['required'] + +evacuate_v2_29 = copy.deepcopy(evacuate_v214) +evacuate_v2_29['properties']['evacuate']['properties'][ + 'force'] = parameter_types.boolean diff --git a/nova/api/openstack/compute/schemas/flavor_manage.py b/nova/api/openstack/compute/schemas/flavor_manage.py index 127e1a5d8b3..5eb53263e9c 100644 --- a/nova/api/openstack/compute/schemas/flavor_manage.py +++ b/nova/api/openstack/compute/schemas/flavor_manage.py @@ -15,6 +15,7 @@ import copy from nova.api.validation import parameter_types +from nova import db create = { 'type': 'object', @@ -30,17 +31,18 @@ 'minLength': 1, 'maxLength': 255, 'pattern': '^(?! )[a-zA-Z0-9. _-]+(? 0) float 'rxtx_factor': { 'type': ['number', 'string'], 'pattern': '^[0-9]+(\.[0-9]+)?$', - 'minimum': 0, 'exclusiveMinimum': True + 'minimum': 0, 'exclusiveMinimum': True, + 'maximum': db.SQL_SP_FLOAT_MAX }, 'os-flavor-access:is_public': parameter_types.boolean, }, diff --git a/nova/api/openstack/compute/schemas/hypervisors.py b/nova/api/openstack/compute/schemas/hypervisors.py new file mode 100644 index 00000000000..38f856dac8a --- /dev/null +++ b/nova/api/openstack/compute/schemas/hypervisors.py @@ -0,0 +1,43 @@ +# Copyright 2017 Huawei Technologies Co.,LTD. +# +# 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 nova.api.validation import parameter_types + + +list_query_schema_v253 = { + 'type': 'object', + 'properties': { + # The 2.33 microversion added support for paging by limit and marker. + 'limit': parameter_types.single_param( + parameter_types.non_negative_integer), + 'marker': parameter_types.single_param({'type': 'string'}), + # The 2.53 microversion adds support for filtering by hostname pattern + # and requesting hosted servers in the GET /os-hypervisors and + # GET /os-hypervisors/detail response. + 'hypervisor_hostname_pattern': parameter_types.single_param( + parameter_types.hostname), + 'with_servers': parameter_types.single_param( + parameter_types.boolean) + }, + 'additionalProperties': False +} + +show_query_schema_v253 = { + 'type': 'object', + 'properties': { + 'with_servers': parameter_types.single_param( + parameter_types.boolean) + }, + 'additionalProperties': False +} diff --git a/nova/api/openstack/compute/schemas/keypairs.py b/nova/api/openstack/compute/schemas/keypairs.py index 22868769dae..d14a2d671c9 100644 --- a/nova/api/openstack/compute/schemas/keypairs.py +++ b/nova/api/openstack/compute/schemas/keypairs.py @@ -90,3 +90,26 @@ server_create_v20 = { 'key_name': parameter_types.name_with_leading_trailing_spaces, } + +index_query_schema_v20 = { + 'type': 'object', + 'properties': {}, + 'additionalProperties': True +} + +index_query_schema_v210 = { + 'type': 'object', + 'properties': { + 'user_id': parameter_types.multi_params({'type': 'string'}) + }, + 'additionalProperties': True +} + +index_query_schema_v235 = copy.deepcopy(index_query_schema_v210) +index_query_schema_v235['properties'].update( + parameter_types.pagination_parameters) + +show_query_schema_v20 = index_query_schema_v20 +show_query_schema_v210 = index_query_schema_v210 +delete_query_schema_v20 = index_query_schema_v20 +delete_query_schema_v210 = index_query_schema_v210 diff --git a/nova/api/openstack/compute/schemas/migrate_server.py b/nova/api/openstack/compute/schemas/migrate_server.py index 0121fa9db43..f2264d074f4 100644 --- a/nova/api/openstack/compute/schemas/migrate_server.py +++ b/nova/api/openstack/compute/schemas/migrate_server.py @@ -49,3 +49,7 @@ 'block_migration'] = block_migration migrate_live_v2_25['properties']['os-migrateLive']['required'] = ( ['block_migration', 'host']) + +migrate_live_v2_30 = copy.deepcopy(migrate_live_v2_25) +migrate_live_v2_30['properties']['os-migrateLive']['properties'][ + 'force'] = parameter_types.boolean diff --git a/nova/api/openstack/compute/schemas/networks.py b/nova/api/openstack/compute/schemas/networks.py index f91dfdb199c..2fd68709439 100644 --- a/nova/api/openstack/compute/schemas/networks.py +++ b/nova/api/openstack/compute/schemas/networks.py @@ -48,9 +48,9 @@ 'allowed_end': parameter_types.ip_address, 'enable_dhcp': parameter_types.boolean, 'share_address': parameter_types.boolean, - 'mtu': parameter_types.positive_integer, - 'vlan': parameter_types.positive_integer, - 'vlan_start': parameter_types.positive_integer, + 'mtu': parameter_types.positive_integer_with_empty_str, + 'vlan': parameter_types.positive_integer_with_empty_str, + 'vlan_start': parameter_types.positive_integer_with_empty_str, 'vpn_start': { 'type': 'string', }, diff --git a/nova/api/openstack/compute/schemas/personality.py b/nova/api/openstack/compute/schemas/personality.py deleted file mode 100644 index 0c7d5eaef2b..00000000000 --- a/nova/api/openstack/compute/schemas/personality.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2014 NEC Corporation. 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. - -server_create = { - 'personality': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'path': {'type': 'string'}, - 'contents': { - 'type': 'string', - 'format': 'base64' - } - }, - 'additionalProperties': False, - } - } -} diff --git a/nova/api/openstack/compute/schemas/preserve_ephemeral_rebuild.py b/nova/api/openstack/compute/schemas/preserve_ephemeral_rebuild.py deleted file mode 100644 index 5035743f9d7..00000000000 --- a/nova/api/openstack/compute/schemas/preserve_ephemeral_rebuild.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2015 IBM Corp. -# -# 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 nova.api.validation import parameter_types - - -server_rebuild = { - 'preserve_ephemeral': parameter_types.boolean, -} diff --git a/nova/api/openstack/compute/schemas/quota_classes.py b/nova/api/openstack/compute/schemas/quota_classes.py index a9226e77b27..173cfb46414 100644 --- a/nova/api/openstack/compute/schemas/quota_classes.py +++ b/nova/api/openstack/compute/schemas/quota_classes.py @@ -11,6 +11,7 @@ # 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 copy from nova.api.openstack.compute.schemas import quota_sets @@ -26,3 +27,12 @@ 'required': ['quota_class_set'], 'additionalProperties': False, } + +update_v250 = copy.deepcopy(update) +del update_v250['properties']['quota_class_set']['properties']['fixed_ips'] +del update_v250['properties']['quota_class_set']['properties']['floating_ips'] +del update_v250['properties']['quota_class_set']['properties'][ + 'security_groups'] +del update_v250['properties']['quota_class_set']['properties'][ + 'security_group_rules'] +del update_v250['properties']['quota_class_set']['properties']['networks'] diff --git a/nova/api/openstack/compute/schemas/quota_sets.py b/nova/api/openstack/compute/schemas/quota_sets.py index f24fe3aca79..fff4add7431 100644 --- a/nova/api/openstack/compute/schemas/quota_sets.py +++ b/nova/api/openstack/compute/schemas/quota_sets.py @@ -40,11 +40,19 @@ 'injected_file_path_bytes': common_quota, 'server_groups': common_quota, 'server_group_members': common_quota, + 'networks': common_quota } update_quota_set = copy.deepcopy(quota_resources) update_quota_set.update({'force': parameter_types.boolean}) +update_quota_set_v236 = copy.deepcopy(update_quota_set) +del update_quota_set_v236['fixed_ips'] +del update_quota_set_v236['floating_ips'] +del update_quota_set_v236['security_groups'] +del update_quota_set_v236['security_group_rules'] +del update_quota_set_v236['networks'] + update = { 'type': 'object', 'properties': { @@ -57,3 +65,6 @@ 'required': ['quota_set'], 'additionalProperties': False, } + +update_v236 = copy.deepcopy(update) +update_v236['properties']['quota_set']['properties'] = update_quota_set_v236 diff --git a/nova/api/openstack/compute/schemas/remote_consoles.py b/nova/api/openstack/compute/schemas/remote_consoles.py index 5369513b3f0..baa47468075 100644 --- a/nova/api/openstack/compute/schemas/remote_consoles.py +++ b/nova/api/openstack/compute/schemas/remote_consoles.py @@ -95,9 +95,11 @@ 'type': 'object', 'properties': { 'protocol': { + 'type': 'string', 'enum': ['vnc', 'spice', 'rdp', 'serial'], }, 'type': { + 'type': 'string', 'enum': ['novnc', 'xvpvnc', 'rdp-html5', 'spice-html5', 'serial'], }, @@ -117,9 +119,11 @@ 'type': 'object', 'properties': { 'protocol': { + 'type': 'string', 'enum': ['vnc', 'spice', 'rdp', 'serial', 'mks'], }, 'type': { + 'type': 'string', 'enum': ['novnc', 'xvpvnc', 'rdp-html5', 'spice-html5', 'serial', 'webmks'], }, diff --git a/nova/api/openstack/compute/schemas/rescue.py b/nova/api/openstack/compute/schemas/rescue.py index 92b6247ff34..f60bdf88642 100644 --- a/nova/api/openstack/compute/schemas/rescue.py +++ b/nova/api/openstack/compute/schemas/rescue.py @@ -22,7 +22,7 @@ 'type': ['object', 'null'], 'properties': { 'adminPass': parameter_types.admin_password, - 'rescue_image_ref': parameter_types.image_ref, + 'rescue_image_ref': parameter_types.image_id, }, 'additionalProperties': False, }, diff --git a/nova/api/openstack/compute/schemas/server_external_events.py b/nova/api/openstack/compute/schemas/server_external_events.py index c8c7ebdc238..38435e0dc5c 100644 --- a/nova/api/openstack/compute/schemas/server_external_events.py +++ b/nova/api/openstack/compute/schemas/server_external_events.py @@ -11,6 +11,7 @@ # 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 copy from nova.objects import external_event as external_event_obj @@ -27,7 +28,12 @@ }, 'name': { 'type': 'string', - 'enum': external_event_obj.EVENT_NAMES + 'enum': [ + 'network-changed', + 'network-vif-plugged', + 'network-vif-unplugged', + 'network-vif-deleted' + ], }, 'status': { 'type': 'string', @@ -45,3 +51,7 @@ 'required': ['events'], 'additionalProperties': False, } + +create_v251 = copy.deepcopy(create) +name = create_v251['properties']['events']['items']['properties']['name'] +name['enum'].append('volume-extended') diff --git a/nova/api/openstack/compute/schemas/server_groups.py b/nova/api/openstack/compute/schemas/server_groups.py index 52a08413aa8..08820091ae7 100644 --- a/nova/api/openstack/compute/schemas/server_groups.py +++ b/nova/api/openstack/compute/schemas/server_groups.py @@ -26,8 +26,15 @@ 'properties': { 'name': parameter_types.name, 'policies': { + # This allows only a single item and it must be one of the + # enumerated values. So this is really just a single string + # value, but for legacy reasons is an array. We could + # probably change the type from array to string with a + # microversion at some point but it's very low priority. 'type': 'array', - 'items': [{'enum': ['anti-affinity', 'affinity']}], + 'items': [{ + 'type': 'string', + 'enum': ['anti-affinity', 'affinity']}], 'uniqueItems': True, 'additionalItems': False, } diff --git a/nova/api/openstack/compute/schemas/server_tags.py b/nova/api/openstack/compute/schemas/server_tags.py new file mode 100644 index 00000000000..bc616acee98 --- /dev/null +++ b/nova/api/openstack/compute/schemas/server_tags.py @@ -0,0 +1,35 @@ +# 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 nova.api.validation import parameter_types +from nova.objects import instance + +update_all = { + "title": "Server tags", + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": parameter_types.tag, + "maxItems": instance.MAX_TAG_COUNT + } + }, + 'required': ['tags'], + 'additionalProperties': False +} + +update = { + "title": "Server tag", + "type": "null", + 'required': [], + 'additionalProperties': False +} diff --git a/nova/api/openstack/compute/schemas/servers.py b/nova/api/openstack/compute/schemas/servers.py index d857a607193..b6a2c54a847 100644 --- a/nova/api/openstack/compute/schemas/servers.py +++ b/nova/api/openstack/compute/schemas/servers.py @@ -15,7 +15,8 @@ import copy from nova.api.validation import parameter_types - +from nova.api.validation.parameter_types import multi_params +from nova.objects import instance base_create = { 'type': 'object', @@ -24,7 +25,12 @@ 'type': 'object', 'properties': { 'name': parameter_types.name, - 'imageRef': parameter_types.image_ref, + # NOTE(gmann): In case of boot from volume, imageRef was + # allowed as the empty string also So keeping the same + # behavior and allow empty string in case of boot from + # volume only. Python code make sure empty string is + # not allowed for other cases. + 'imageRef': parameter_types.image_id_or_empty_string, 'flavorRef': parameter_types.flavor_ref, 'adminPass': parameter_types.admin_password, 'metadata': parameter_types.metadata, @@ -42,7 +48,11 @@ }, 'additionalProperties': False, } - } + }, + 'OS-DCF:diskConfig': parameter_types.disk_config, + 'accessIPv4': parameter_types.accessIPv4, + 'accessIPv6': parameter_types.accessIPv6, + 'personality': parameter_types.personality, }, 'required': ['name', 'flavorRef'], 'additionalProperties': False, @@ -63,6 +73,72 @@ 'properties']['description'] = parameter_types.description +base_create_v232 = copy.deepcopy(base_create_v219) +base_create_v232['properties']['server'][ + 'properties']['networks']['items'][ + 'properties']['tag'] = parameter_types.tag + + +# 2.37 builds on 2.32 and makes the following changes: +# 1. server.networks is required +# 2. server.networks is now either an enum or a list +# 3. server.networks.uuid is now required to be a uuid +base_create_v237 = copy.deepcopy(base_create_v232) +base_create_v237['properties']['server']['required'].append('networks') +base_create_v237['properties']['server']['properties']['networks'] = { + 'oneOf': [ + {'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'fixed_ip': parameter_types.ip_address, + 'port': { + 'oneOf': [{'type': 'string', 'format': 'uuid'}, + {'type': 'null'}] + }, + 'uuid': {'type': 'string', 'format': 'uuid'}, + }, + 'additionalProperties': False, + }, + }, + {'type': 'string', 'enum': ['none', 'auto']}, + ]} + + +# 2.42 builds on 2.37 and re-introduces the tag field to the list of network +# objects. +base_create_v242 = copy.deepcopy(base_create_v237) +base_create_v242['properties']['server']['properties']['networks'] = { + 'oneOf': [ + {'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'fixed_ip': parameter_types.ip_address, + 'port': { + 'oneOf': [{'type': 'string', 'format': 'uuid'}, + {'type': 'null'}] + }, + 'uuid': {'type': 'string', 'format': 'uuid'}, + 'tag': parameter_types.tag, + }, + 'additionalProperties': False, + }, + }, + {'type': 'string', 'enum': ['none', 'auto']}, + ]} + + +# 2.52 builds on 2.42 and makes the following changes: +# Allowing adding tags to instances when booting +base_create_v252 = copy.deepcopy(base_create_v242) +base_create_v252['properties']['server']['properties']['tags'] = { + "type": "array", + "items": parameter_types.tag, + "maxItems": instance.MAX_TAG_COUNT +} + + base_update = { 'type': 'object', 'properties': { @@ -70,6 +146,9 @@ 'type': 'object', 'properties': { 'name': parameter_types.name, + 'OS-DCF:diskConfig': parameter_types.disk_config, + 'accessIPv4': parameter_types.accessIPv4, + 'accessIPv6': parameter_types.accessIPv6, }, 'additionalProperties': False, }, @@ -94,10 +173,14 @@ 'type': 'object', 'properties': { 'name': parameter_types.name, - 'imageRef': parameter_types.image_ref, + 'imageRef': parameter_types.image_id, 'adminPass': parameter_types.admin_password, 'metadata': parameter_types.metadata, 'preserve_ephemeral': parameter_types.boolean, + 'OS-DCF:diskConfig': parameter_types.disk_config, + 'accessIPv4': parameter_types.accessIPv4, + 'accessIPv6': parameter_types.accessIPv6, + 'personality': parameter_types.personality, }, 'required': ['imageRef'], 'additionalProperties': False, @@ -116,13 +199,14 @@ base_rebuild_v219['properties']['rebuild'][ 'properties']['description'] = parameter_types.description -base_resize = { +resize = { 'type': 'object', 'properties': { 'resize': { 'type': 'object', 'properties': { 'flavorRef': parameter_types.flavor_ref, + 'OS-DCF:diskConfig': parameter_types.disk_config, }, 'required': ['flavorRef'], 'additionalProperties': False, @@ -162,6 +246,7 @@ 'type': 'object', 'properties': { 'type': { + 'type': 'string', 'enum': ['HARD', 'Hard', 'hard', 'SOFT', 'Soft', 'soft'] } }, @@ -183,3 +268,127 @@ 'required': ['trigger_crash_dump'], 'additionalProperties': False } + +# NOTE: We don't check actual values of queries on params +# which are defined as the following common_param. +common_param = multi_params({'type': 'string'}) +common_regex_param = multi_params({'type': 'string', 'format': 'regex'}) + +JOINED_TABLE_QUERY_PARAMS_SERVERS = { + 'block_device_mapping': common_param, + 'services': common_param, + 'metadata': common_param, + 'system_metadata': common_param, + 'info_cache': common_param, + 'security_groups': common_param, + 'pci_devices': common_param +} + +# These fields are valid values for sort_keys before we start +# using schema validation, but are considered to be bad values +# and disabled to use. In order to avoid backward incompatibility, +# they are ignored instead of return HTTP 400. +SERVER_LIST_IGNORE_SORT_KEY = [ + 'architecture', 'cell_name', 'cleaned', 'default_ephemeral_device', + 'default_swap_device', 'deleted', 'deleted_at', 'disable_terminate', + 'ephemeral_gb', 'ephemeral_key_uuid', 'id', 'key_data', 'launched_on', + 'locked', 'memory_mb', 'os_type', 'reservation_id', 'root_gb', + 'shutdown_terminate', 'user_data', 'vcpus', 'vm_mode' +] + + +VALID_SORT_KEYS = { + "type": "string", + "enum": ['access_ip_v4', 'access_ip_v6', 'auto_disk_config', + 'availability_zone', 'config_drive', 'created_at', + 'display_description', 'display_name', 'host', 'hostname', + 'image_ref', 'instance_type_id', 'kernel_id', 'key_name', + 'launch_index', 'launched_at', 'locked_by', 'node', 'power_state', + 'progress', 'project_id', 'ramdisk_id', 'root_device_name', + 'task_state', 'terminated_at', 'updated_at', 'user_id', 'uuid', + 'vm_state'] + + SERVER_LIST_IGNORE_SORT_KEY +} + +query_params_v21 = { + 'type': 'object', + 'properties': { + 'user_id': common_param, + 'project_id': common_param, + # The alias of project_id. It should be removed in the + # future with microversion bump. + 'tenant_id': common_param, + 'launch_index': common_param, + # The alias of image. It should be removed in the + # future with microversion bump. + 'image_ref': common_param, + 'image': common_param, + 'kernel_id': common_regex_param, + 'ramdisk_id': common_regex_param, + 'hostname': common_regex_param, + 'key_name': common_regex_param, + 'power_state': common_regex_param, + 'vm_state': common_param, + 'task_state': common_param, + 'host': common_param, + 'node': common_regex_param, + 'flavor': common_regex_param, + 'reservation_id': common_regex_param, + 'launched_at': common_regex_param, + 'terminated_at': common_regex_param, + 'availability_zone': common_regex_param, + # NOTE(alex_xu): This is pattern matching, it didn't get any benefit + # from DB index. + 'name': common_regex_param, + # The alias of name. It should be removed in the future + # with microversion bump. + 'display_name': common_regex_param, + 'description': common_regex_param, + # The alias of description. It should be removed in the + # future with microversion bump. + 'display_description': common_regex_param, + 'locked_by': common_regex_param, + 'uuid': common_param, + 'root_device_name': common_regex_param, + 'config_drive': common_regex_param, + 'access_ip_v4': common_regex_param, + 'access_ip_v6': common_regex_param, + 'auto_disk_config': common_regex_param, + 'progress': common_regex_param, + 'sort_key': multi_params(VALID_SORT_KEYS), + 'sort_dir': common_param, + 'all_tenants': common_param, + 'deleted': common_param, + 'status': common_param, + 'changes-since': multi_params({'type': 'string', + 'format': 'date-time'}), + # NOTE(alex_xu): The ip and ip6 are implemented in the python. + 'ip': common_regex_param, + 'ip6': common_regex_param, + 'created_at': common_regex_param, + }, + # For backward-compatible additionalProperties is set to be True here. + # And we will either strip the extra params out or raise HTTP 400 + # according to the params' value in the later process. + 'additionalProperties': True, + # Prevent internal-attributes that are started with underscore from + # being striped out in schema validation, and raise HTTP 400 in API. + 'patternProperties': {"^_": common_param} +} + +# Update the joined-table fields to the list so it will not be +# stripped in later process, thus can be handled later in api +# to raise HTTP 400. +query_params_v21['properties'].update( + JOINED_TABLE_QUERY_PARAMS_SERVERS) + +query_params_v21['properties'].update( + parameter_types.pagination_parameters) + +query_params_v226 = copy.deepcopy(query_params_v21) +query_params_v226['properties'].update({ + 'tags': common_regex_param, + 'tags-any': common_regex_param, + 'not-tags': common_regex_param, + 'not-tags-any': common_regex_param, +}) diff --git a/nova/api/openstack/compute/schemas/services.py b/nova/api/openstack/compute/schemas/services.py index f62dd0ccf5a..c90b74c9f6d 100644 --- a/nova/api/openstack/compute/schemas/services.py +++ b/nova/api/openstack/compute/schemas/services.py @@ -44,3 +44,24 @@ 'required': ['host', 'binary'], 'additionalProperties': False } + +# The 2.53 body is for updating a service's status and/or forced_down fields. +# There are no required attributes since the service is identified using a +# unique service_id on the request path, and status and/or forced_down can +# be specified in the body. If status=='disabled', then 'disabled_reason' is +# also checked in the body but is not required. Requesting status='enabled' and +# including a 'disabled_reason' results in a 400, but this is checked in code. +service_update_v2_53 = { + 'type': 'object', + 'properties': { + 'status': { + 'type': 'string', + 'enum': ['enabled', 'disabled'], + }, + 'disabled_reason': { + 'type': 'string', 'minLength': 1, 'maxLength': 255, + }, + 'forced_down': parameter_types.boolean + }, + 'additionalProperties': False +} diff --git a/nova/api/openstack/compute/schemas/tenant_networks.py b/nova/api/openstack/compute/schemas/tenant_networks.py index fd749d7d464..f5f4c03ac3a 100644 --- a/nova/api/openstack/compute/schemas/tenant_networks.py +++ b/nova/api/openstack/compute/schemas/tenant_networks.py @@ -26,9 +26,10 @@ 'ipam': parameter_types.boolean, 'cidr': parameter_types.cidr, 'cidr_v6': parameter_types.cidr, - 'vlan_start': parameter_types.positive_integer, - 'network_size': parameter_types.positive_integer, - 'num_networks': parameter_types.positive_integer + 'vlan_start': parameter_types.positive_integer_with_empty_str, + 'network_size': + parameter_types.positive_integer_with_empty_str, + 'num_networks': parameter_types.positive_integer_with_empty_str }, 'required': ['label'], 'oneOf': [ diff --git a/nova/api/openstack/compute/schemas/user_data.py b/nova/api/openstack/compute/schemas/user_data.py index dd4f9ad2449..6ffc4aaab31 100644 --- a/nova/api/openstack/compute/schemas/user_data.py +++ b/nova/api/openstack/compute/schemas/user_data.py @@ -18,3 +18,13 @@ 'format': 'base64' }, } + + +server_create_v20 = { + 'user_data': { + 'oneOf': [ + {'type': 'string', 'format': 'base64'}, + {'type': 'null'}, + ], + }, +} diff --git a/nova/api/openstack/compute/schemas/volumes.py b/nova/api/openstack/compute/schemas/volumes.py index f70ff08456c..b08ea5f8e51 100644 --- a/nova/api/openstack/compute/schemas/volumes.py +++ b/nova/api/openstack/compute/schemas/volumes.py @@ -83,6 +83,9 @@ 'required': ['volumeAttachment'], 'additionalProperties': False, } +create_volume_attachment_v249 = copy.deepcopy(create_volume_attachment) +create_volume_attachment_v249['properties']['volumeAttachment'][ + 'properties']['tag'] = parameter_types.tag update_volume_attachment = copy.deepcopy(create_volume_attachment) del update_volume_attachment['properties']['volumeAttachment'][ diff --git a/nova/api/openstack/compute/security_group_default_rules.py b/nova/api/openstack/compute/security_group_default_rules.py index 24afbd5f4b5..b4b6a8bef88 100644 --- a/nova/api/openstack/compute/security_group_default_rules.py +++ b/nova/api/openstack/compute/security_group_default_rules.py @@ -14,29 +14,29 @@ from webob import exc +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack.compute import security_groups as sg from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import exception from nova.i18n import _ from nova.network.security_group import openstack_driver +from nova.policies import security_group_default_rules as sgdr_policies -ALIAS = "os-security-group-default-rules" -authorize = extensions.os_compute_authorizer(ALIAS) - - -class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase): +class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase, + wsgi.Controller): def __init__(self): self.security_group_api = ( - openstack_driver.get_openstack_security_group_driver( - skip_policy_check=True)) + openstack_driver.get_openstack_security_group_driver()) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 409, 501)) def create(self, req, body): context = req.environ['nova.context'] - authorize(context) + context.can(sgdr_policies.BASE_POLICY_NAME) sg_rule = self._from_body(body, 'security_group_default_rule') @@ -70,10 +70,11 @@ def _rule_args_to_dict(self, to_port=None, from_port=None, return self.security_group_api.new_cidr_ingress_rule( cidr, ip_protocol, from_port, to_port) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 404, 501)) def show(self, req, id): context = req.environ['nova.context'] - authorize(context) + context.can(sgdr_policies.BASE_POLICY_NAME) try: id = self.security_group_api.validate_id(id) @@ -88,11 +89,12 @@ def show(self, req, id): fmt_rule = self._format_security_group_default_rule(rule) return {"security_group_default_rule": fmt_rule} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 404, 501)) @wsgi.response(204) def delete(self, req, id): context = req.environ['nova.context'] - authorize(context) + context.can(sgdr_policies.BASE_POLICY_NAME) try: id = self.security_group_api.validate_id(id) @@ -105,10 +107,11 @@ def delete(self, req, id): except exception.SecurityGroupDefaultRuleNotFound as ex: raise exc.HTTPNotFound(explanation=ex.format_message()) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((404, 501)) def index(self, req): context = req.environ['nova.context'] - authorize(context) + context.can(sgdr_policies.BASE_POLICY_NAME) ret = {'security_group_default_rules': []} try: @@ -129,20 +132,3 @@ def _format_security_group_default_rule(self, rule): sg_rule['ip_range'] = {} sg_rule['ip_range'] = {'cidr': rule['cidr']} return sg_rule - - -class SecurityGroupDefaultRules(extensions.V21APIExtensionBase): - """Default rules for security group support.""" - name = "SecurityGroupDefaultRules" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [ - extensions.ResourceExtension(ALIAS, - SecurityGroupDefaultRulesController())] - - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/security_groups.py b/nova/api/openstack/compute/security_groups.py index 2f4d9be173c..e8d071760f2 100644 --- a/nova/api/openstack/compute/security_groups.py +++ b/nova/api/openstack/compute/security_groups.py @@ -19,6 +19,8 @@ from oslo_serialization import jsonutils from webob import exc +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import common from nova.api.openstack.compute.schemas import security_groups as \ schema_security_groups @@ -28,31 +30,28 @@ from nova import exception from nova.i18n import _ from nova.network.security_group import openstack_driver +from nova.policies import security_groups as sg_policies from nova.virt import netutils LOG = logging.getLogger(__name__) -ALIAS = 'os-security-groups' ATTRIBUTE_NAME = 'security_groups' -authorize = extensions.os_compute_authorizer(ALIAS) -softauth = extensions.os_compute_soft_authorizer(ALIAS) def _authorize_context(req): context = req.environ['nova.context'] - authorize(context) + context.can(sg_policies.BASE_POLICY_NAME) return context -class SecurityGroupControllerBase(wsgi.Controller): +class SecurityGroupControllerBase(object): """Base class for Security Group controllers.""" def __init__(self): self.security_group_api = ( - openstack_driver.get_openstack_security_group_driver( - skip_policy_check=True)) + openstack_driver.get_openstack_security_group_driver()) self.compute_api = compute.API( - security_group_api=self.security_group_api, skip_policy_check=True) + security_group_api=self.security_group_api) def _format_security_group_rule(self, context, rule, group_rule_data=None): """Return a security group rule in desired API response format. @@ -68,7 +67,9 @@ def _format_security_group_rule(self, context, rule, group_rule_data=None): sg_rule['to_port'] = rule['to_port'] sg_rule['group'] = {} sg_rule['ip_range'] = {} - if rule['group_id']: + if group_rule_data: + sg_rule['group'] = group_rule_data + elif rule['group_id']: try: source_group = self.security_group_api.get( context, id=rule['group_id']) @@ -85,8 +86,6 @@ def _format_security_group_rule(self, context, rule, group_rule_data=None): return sg_rule['group'] = {'name': source_group.get('name'), 'tenant_id': source_group.get('project_id')} - elif group_rule_data: - sg_rule['group'] = group_rule_data else: sg_rule['ip_range'] = {'cidr': rule['cidr']} return sg_rule @@ -115,9 +114,10 @@ def _from_body(self, body, key): return value -class SecurityGroupController(SecurityGroupControllerBase): +class SecurityGroupController(SecurityGroupControllerBase, wsgi.Controller): """The Security group API controller for the OpenStack API.""" + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 404)) def show(self, req, id): """Return data about the given security group.""" @@ -135,6 +135,7 @@ def show(self, req, id): return {'security_group': self._format_security_group(context, security_group)} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 404)) @wsgi.response(202) def delete(self, req, id): @@ -151,6 +152,7 @@ def delete(self, req, id): except exception.Invalid as exp: raise exc.HTTPBadRequest(explanation=exp.format_message()) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(404) def index(self, req): """Returns a list of security groups.""" @@ -172,6 +174,7 @@ def index(self, req): list(sorted(result, key=lambda k: (k['tenant_id'], k['name'])))} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 403)) def create(self, req, body): """Creates a new security group.""" @@ -196,6 +199,7 @@ def create(self, req, body): return {'security_group': self._format_security_group(context, group_ref)} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 404)) def update(self, req, id, body): """Update a security group.""" @@ -229,13 +233,17 @@ def update(self, req, id, body): group_ref)} -class SecurityGroupRulesController(SecurityGroupControllerBase): +class SecurityGroupRulesController(SecurityGroupControllerBase, + wsgi.Controller): + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 403, 404)) def create(self, req, body): context = _authorize_context(req) sg_rule = self._from_body(body, 'security_group_rule') + group_id = sg_rule.get('group_id') + source_group = {} try: parent_group_id = self.security_group_api.validate_id( @@ -243,12 +251,17 @@ def create(self, req, body): security_group = self.security_group_api.get(context, None, parent_group_id, map_exception=True) + if group_id is not None: + group_id = self.security_group_api.validate_id(group_id) + + source_group = self.security_group_api.get( + context, id=group_id) new_rule = self._rule_args_to_dict(context, to_port=sg_rule.get('to_port'), from_port=sg_rule.get('from_port'), ip_protocol=sg_rule.get('ip_protocol'), cidr=sg_rule.get('cidr'), - group_id=sg_rule.get('group_id')) + group_id=group_id) except (exception.Invalid, exception.InvalidCidr) as exp: raise exc.HTTPBadRequest(explanation=exp.format_message()) except exception.SecurityGroupNotFound as exp: @@ -268,9 +281,7 @@ def create(self, req, body): group_rule_data = None try: - if sg_rule.get('group_id'): - source_group = self.security_group_api.get( - context, id=sg_rule['group_id']) + if group_id: group_rule_data = {'name': source_group.get('name'), 'tenant_id': source_group.get('project_id')} @@ -293,10 +304,6 @@ def _rule_args_to_dict(self, context, to_port=None, from_port=None, ip_protocol=None, cidr=None, group_id=None): if group_id is not None: - group_id = self.security_group_api.validate_id(group_id) - - # check if groupId exists - self.security_group_api.get(context, id=group_id) return self.security_group_api.new_group_ingress_rule( group_id, ip_protocol, from_port, to_port) else: @@ -304,6 +311,7 @@ def _rule_args_to_dict(self, context, to_port=None, from_port=None, return self.security_group_api.new_cidr_ingress_rule( cidr, ip_protocol, from_port, to_port) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 404, 409)) @wsgi.response(202) def delete(self, req, id): @@ -335,9 +343,8 @@ def index(self, req, server_id): self.security_group_api.ensure_default(context) + instance = common.get_instance(self.compute_api, context, server_id) try: - instance = common.get_instance(self.compute_api, context, - server_id) groups = self.security_group_api.get_instance_security_groups( context, instance, True) except (exception.SecurityGroupNotFound, @@ -357,10 +364,9 @@ class SecurityGroupActionController(wsgi.Controller): def __init__(self, *args, **kwargs): super(SecurityGroupActionController, self).__init__(*args, **kwargs) self.security_group_api = ( - openstack_driver.get_openstack_security_group_driver( - skip_policy_check=True)) + openstack_driver.get_openstack_security_group_driver()) self.compute_api = compute.API( - security_group_api=self.security_group_api, skip_policy_check=True) + security_group_api=self.security_group_api) def _parse(self, body, action): try: @@ -388,7 +394,7 @@ def _invoke(self, method, context, id, group_name): @wsgi.action('addSecurityGroup') def _addSecurityGroup(self, req, id, body): context = req.environ['nova.context'] - authorize(context) + context.can(sg_policies.BASE_POLICY_NAME) group_name = self._parse(body, 'addSecurityGroup') try: @@ -408,7 +414,7 @@ def _addSecurityGroup(self, req, id, body): @wsgi.action('removeSecurityGroup') def _removeSecurityGroup(self, req, id, body): context = req.environ['nova.context'] - authorize(context) + context.can(sg_policies.BASE_POLICY_NAME) group_name = self._parse(body, 'removeSecurityGroup') @@ -427,10 +433,9 @@ def _removeSecurityGroup(self, req, id, body): class SecurityGroupsOutputController(wsgi.Controller): def __init__(self, *args, **kwargs): super(SecurityGroupsOutputController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() self.security_group_api = ( - openstack_driver.get_openstack_security_group_driver( - skip_policy_check=True)) + openstack_driver.get_openstack_security_group_driver()) def _extend_servers(self, req, servers): # TODO(arosen) this function should be refactored to reduce duplicate @@ -439,7 +444,7 @@ def _extend_servers(self, req, servers): return key = "security_groups" context = req.environ['nova.context'] - if not softauth(context): + if not context.can(sg_policies.BASE_POLICY_NAME, fatal=False): return if not openstack_driver.is_neutron_security_groups(): @@ -447,7 +452,7 @@ def _extend_servers(self, req, servers): instance = req.get_db_instance(server['id']) groups = instance.get(key) if groups: - server[ATTRIBUTE_NAME] = [{"name": group["name"]} + server[ATTRIBUTE_NAME] = [{"name": group.name} for group in groups] else: # If method is a POST we get the security groups intended for an @@ -491,42 +496,18 @@ def detail(self, req, resp_obj): self._extend_servers(req, list(resp_obj.obj['servers'])) -class SecurityGroups(extensions.V21APIExtensionBase): - """Security group support.""" - name = "SecurityGroups" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - secgrp_output_ext = extensions.ControllerExtension( - self, 'servers', SecurityGroupsOutputController()) - secgrp_act_ext = extensions.ControllerExtension( - self, 'servers', SecurityGroupActionController()) - return [secgrp_output_ext, secgrp_act_ext] - - def get_resources(self): - secgrp_ext = extensions.ResourceExtension(ALIAS, - SecurityGroupController()) - server_secgrp_ext = extensions.ResourceExtension( - ALIAS, - controller=ServerSecurityGroupController(), - parent=dict(member_name='server', collection_name='servers')) - secgrp_rules_ext = extensions.ResourceExtension( - 'os-security-group-rules', - controller=SecurityGroupRulesController()) - return [secgrp_ext, server_secgrp_ext, secgrp_rules_ext] - - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - security_groups = server_dict.get(ATTRIBUTE_NAME) - if security_groups is not None: - create_kwargs['security_group'] = [ - sg['name'] for sg in security_groups if sg.get('name')] - create_kwargs['security_group'] = list( - set(create_kwargs['security_group'])) - - def get_server_create_schema(self, version): - if version == '2.0': - return schema_security_groups.server_create_v20 - return schema_security_groups.server_create +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + security_groups = server_dict.get(ATTRIBUTE_NAME) + if security_groups is not None: + create_kwargs['security_groups'] = [ + sg['name'] for sg in security_groups if sg.get('name')] + create_kwargs['security_groups'] = list( + set(create_kwargs['security_groups'])) + + +def get_server_create_schema(version): + if version == '2.0': + return schema_security_groups.server_create_v20 + return schema_security_groups.server_create diff --git a/nova/api/openstack/compute/server_diagnostics.py b/nova/api/openstack/compute/server_diagnostics.py index 6659161156c..eca71a56399 100644 --- a/nova/api/openstack/compute/server_diagnostics.py +++ b/nova/api/openstack/compute/server_diagnostics.py @@ -13,56 +13,55 @@ # License for the specific language governing permissions and limitations # under the License. +import webob + +from nova.api.openstack import api_version_request from nova.api.openstack import common +from nova.api.openstack.compute.views import server_diagnostics from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute from nova import exception - - -ALIAS = "os-server-diagnostics" -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.i18n import _ +from nova.policies import server_diagnostics as sd_policies class ServerDiagnosticsController(wsgi.Controller): - def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) + _view_builder_class = server_diagnostics.ViewBuilder - @extensions.expected_errors((404, 409, 501)) + def __init__(self, *args, **kwargs): + super(ServerDiagnosticsController, self).__init__(*args, **kwargs) + self.compute_api = compute.API() + + @extensions.expected_errors((400, 404, 409, 501)) def index(self, req, server_id): context = req.environ["nova.context"] - authorize(context) + context.can(sd_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, server_id) try: - # NOTE(gmann): To make V21 same as V2 API, this method will call - # 'get_diagnostics' instead of 'get_instance_diagnostics'. - # In future, 'get_instance_diagnostics' needs to be called to - # provide VM diagnostics in a defined format for all driver. - # BP - https://blueprints.launchpad.net/nova/+spec/v3-diagnostics. + if api_version_request.is_supported(req, min_version='2.48'): + diagnostics = self.compute_api.get_instance_diagnostics( + context, instance) + return self._view_builder.instance_diagnostics(diagnostics) + return self.compute_api.get_diagnostics(context, instance) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'get_diagnostics', server_id) + except exception.InstanceNotReady as e: + raise webob.exc.HTTPConflict(explanation=e.format_message()) + except exception.InstanceDiagnosticsNotSupported: + # NOTE(snikitin): During upgrade we may face situation when env + # has new API and old compute. New compute returns a + # Diagnostics object. Old compute returns a dictionary. So we + # can't perform a request correctly if compute is too old. + msg = _('Compute node is too old. You must complete the ' + 'upgrade process to be able to get standardized ' + 'diagnostics data which is available since v2.48. However ' + 'you are still able to get diagnostics data in ' + 'non-standardized format which is available until v2.47.') + raise webob.exc.HTTPBadRequest(explanation=msg) except NotImplementedError: common.raise_feature_not_supported() - - -class ServerDiagnostics(extensions.V21APIExtensionBase): - """Allow Admins to view server diagnostics through server action.""" - - name = "ServerDiagnostics" - alias = ALIAS - version = 1 - - def get_resources(self): - parent_def = {'member_name': 'server', 'collection_name': 'servers'} - resources = [ - extensions.ResourceExtension('diagnostics', - ServerDiagnosticsController(), - parent=parent_def)] - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/server_external_events.py b/nova/api/openstack/compute/server_external_events.py index 6a6b3592da1..e8e156f5f47 100644 --- a/nova/api/openstack/compute/server_external_events.py +++ b/nova/api/openstack/compute/server_external_events.py @@ -20,15 +20,13 @@ from nova.api.openstack import wsgi from nova.api import validation from nova import compute -from nova import exception +from nova import context as nova_context from nova.i18n import _ -from nova.i18n import _LI from nova import objects +from nova.policies import server_external_events as see_policies LOG = logging.getLogger(__name__) -ALIAS = 'os-server-external-events' -authorize = extensions.os_compute_authorizer(ALIAS) class ServerExternalEventsController(wsgi.Controller): @@ -37,22 +35,57 @@ def __init__(self): self.compute_api = compute.API() super(ServerExternalEventsController, self).__init__() + @staticmethod + def _is_event_tag_present_when_required(event): + if event.name == 'volume-extended' and event.tag is None: + return False + return True + + def _get_instances_all_cells(self, context, instance_uuids, + instance_mappings): + cells = {} + instance_uuids_by_cell = {} + for im in instance_mappings: + if im.cell_mapping.uuid not in cells: + cells[im.cell_mapping.uuid] = im.cell_mapping + instance_uuids_by_cell.setdefault(im.cell_mapping.uuid, list()) + instance_uuids_by_cell[im.cell_mapping.uuid].append( + im.instance_uuid) + + instances = {} + for cell_uuid, cell in cells.items(): + with nova_context.target_cell(context, cell) as cctxt: + instances.update( + {inst.uuid: inst for inst in + objects.InstanceList.get_by_filters( + cctxt, {'uuid': instance_uuids_by_cell[cell_uuid]}, + expected_attrs=['migration_context', 'info_cache'])}) + + return instances + @extensions.expected_errors((400, 403, 404)) @wsgi.response(200) - @validation.schema(server_external_events.create) + @validation.schema(server_external_events.create, '2.1', '2.50') + @validation.schema(server_external_events.create_v251, '2.51') def create(self, req, body): """Creates a new instance event.""" context = req.environ['nova.context'] - authorize(context, action='create') + context.can(see_policies.POLICY_ROOT % 'create') response_events = [] accepted_events = [] accepted_instances = set() - instances = {} result = 200 body_events = body['events'] + # Fetch instance objects for all relevant instances + instance_uuids = set([event['server_uuid'] for event in body_events]) + instance_mappings = objects.InstanceMappingList.get_by_instance_uuids( + context, list(instance_uuids)) + instances = self._get_instances_all_cells(context, instance_uuids, + instance_mappings) + for _event in body_events: client_event = dict(_event) event = objects.InstanceExternalEvent(context) @@ -62,46 +95,50 @@ def create(self, req, body): event.status = client_event.pop('status', 'completed') event.tag = client_event.pop('tag', None) + response_events.append(_event) + instance = instances.get(event.instance_uuid) if not instance: - try: - instance = objects.Instance.get_by_uuid( - context, event.instance_uuid) - instances[event.instance_uuid] = instance - except exception.InstanceNotFound: - LOG.debug('Dropping event %(name)s:%(tag)s for unknown ' - 'instance %(instance_uuid)s', - {'name': event.name, 'tag': event.tag, - 'instance_uuid': event.instance_uuid}) - _event['status'] = 'failed' - _event['code'] = 404 - result = 207 + LOG.debug('Dropping event %(name)s:%(tag)s for unknown ' + 'instance %(instance_uuid)s', + {'name': event.name, 'tag': event.tag, + 'instance_uuid': event.instance_uuid}) + _event['status'] = 'failed' + _event['code'] = 404 + result = 207 + continue # NOTE: before accepting the event, make sure the instance # for which the event is sent is assigned to a host; otherwise # it will not be possible to dispatch the event - if instance: - if instance.host: - accepted_events.append(event) - accepted_instances.add(instance) - LOG.info(_LI('Creating event %(name)s:%(tag)s for ' - 'instance %(instance_uuid)s'), - {'name': event.name, 'tag': event.tag, - 'instance_uuid': event.instance_uuid}) - # NOTE: as the event is processed asynchronously verify - # whether 202 is a more suitable response code than 200 - _event['status'] = 'completed' - _event['code'] = 200 - else: - LOG.debug("Unable to find a host for instance " - "%(instance)s. Dropping event %(event)s", - {'instance': event.instance_uuid, - 'event': event.name}) - _event['status'] = 'failed' - _event['code'] = 422 - result = 207 - - response_events.append(_event) + if not self._is_event_tag_present_when_required(event): + LOG.debug("Event tag is missing for instance " + "%(instance)s. Dropping event %(event)s", + {'instance': event.instance_uuid, + 'event': event.name}) + _event['status'] = 'failed' + _event['code'] = 400 + result = 207 + elif instance.host: + accepted_events.append(event) + accepted_instances.add(instance) + LOG.info('Creating event %(name)s:%(tag)s for ' + 'instance %(instance_uuid)s on %(host)s', + {'name': event.name, 'tag': event.tag, + 'instance_uuid': event.instance_uuid, + 'host': instance.host}) + # NOTE: as the event is processed asynchronously verify + # whether 202 is a more suitable response code than 200 + _event['status'] = 'completed' + _event['code'] = 200 + else: + LOG.debug("Unable to find a host for instance " + "%(instance)s. Dropping event %(event)s", + {'instance': event.instance_uuid, + 'event': event.name}) + _event['status'] = 'failed' + _event['code'] = 422 + result = 207 if accepted_events: self.compute_api.external_instance_event( @@ -115,20 +152,3 @@ def create(self, req, body): robj = wsgi.ResponseObject({'events': response_events}) robj._code = result return robj - - -class ServerExternalEvents(extensions.V21APIExtensionBase): - """Server External Event Triggers.""" - - name = "ServerExternalEvents" - alias = ALIAS - version = 1 - - def get_resources(self): - resource = extensions.ResourceExtension(ALIAS, - ServerExternalEventsController()) - - return [resource] - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/server_groups.py b/nova/api/openstack/compute/server_groups.py index 80e0b38764c..afe3de9b718 100644 --- a/nova/api/openstack/compute/server_groups.py +++ b/nova/api/openstack/compute/server_groups.py @@ -15,6 +15,8 @@ """The Server Group API Extension.""" +import collections + from oslo_log import log as logging import webob from webob import exc @@ -25,25 +27,58 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api import validation +import nova.conf +from nova import context as nova_context import nova.exception from nova.i18n import _ -from nova.i18n import _LE from nova import objects +from nova.policies import server_groups as sg_policies LOG = logging.getLogger(__name__) -ALIAS = "os-server-groups" - +CONF = nova.conf.CONF -authorize = extensions.os_compute_authorizer(ALIAS) - -def _authorize_context(req): +def _authorize_context(req, action): context = req.environ['nova.context'] - authorize(context) + context.can(sg_policies.POLICY_ROOT % action) return context +def _get_not_deleted(context, uuids): + mappings = objects.InstanceMappingList.get_by_instance_uuids( + context, uuids) + inst_by_cell = collections.defaultdict(list) + cell_mappings = {} + found_inst_uuids = [] + + # Get a master list of cell mappings, and a list of instance + # uuids organized by cell + for im in mappings: + if not im.cell_mapping: + # Not scheduled yet, so just throw it in the final list + # and move on + found_inst_uuids.append(im.instance_uuid) + continue + if im.cell_mapping.uuid not in cell_mappings: + cell_mappings[im.cell_mapping.uuid] = im.cell_mapping + inst_by_cell[im.cell_mapping.uuid].append(im.instance_uuid) + + # Query each cell for the instances that are inside, building + # a list of non-deleted instance uuids. + for cell_uuid, cell_mapping in cell_mappings.items(): + inst_uuids = inst_by_cell[cell_uuid] + LOG.debug('Querying cell %(cell)s for %(num)i instances', + {'cell': cell_mapping.identity, 'num': len(uuids)}) + filters = {'uuid': inst_uuids, 'deleted': False} + with nova_context.target_cell(context, cell_mapping) as ctx: + found_inst_uuids.extend([ + inst.uuid for inst in objects.InstanceList.get_by_filters( + ctx, filters=filters)]) + + return found_inst_uuids + + class ServerGroupController(wsgi.Controller): """The Server group API controller for the OpenStack API.""" @@ -62,10 +97,7 @@ def _format_server_group(self, context, group, req): members = [] if group.members: # Display the instances that are not deleted. - filters = {'uuid': group.members, 'deleted': False} - instances = objects.InstanceList.get_by_filters( - context, filters=filters) - members = [instance.uuid for instance in instances] + members = _get_not_deleted(context, group.members) server_group['members'] = members # Add project id information to the response data for # API version v2.13 @@ -77,7 +109,7 @@ def _format_server_group(self, context, group, req): @extensions.expected_errors(404) def show(self, req, id): """Return data about the given server group.""" - context = _authorize_context(req) + context = _authorize_context(req, 'show') try: sg = objects.InstanceGroup.get_by_uuid(context, id) except nova.exception.InstanceGroupNotFound as e: @@ -87,39 +119,21 @@ def show(self, req, id): @wsgi.response(204) @extensions.expected_errors(404) def delete(self, req, id): - """Delete an server group.""" - context = _authorize_context(req) + """Delete a server group.""" + context = _authorize_context(req, 'delete') try: sg = objects.InstanceGroup.get_by_uuid(context, id) except nova.exception.InstanceGroupNotFound as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) - - quotas = objects.Quotas(context=context) - project_id, user_id = objects.quotas.ids_from_server_group(context, sg) - try: - # We have to add the quota back to the user that created - # the server group - quotas.reserve(project_id=project_id, - user_id=user_id, server_groups=-1) - except Exception: - quotas = None - LOG.exception(_LE("Failed to update usages deallocating " - "server group")) - try: sg.destroy() except nova.exception.InstanceGroupNotFound as e: - if quotas: - quotas.rollback() raise webob.exc.HTTPNotFound(explanation=e.format_message()) - if quotas: - quotas.commit() - @extensions.expected_errors(()) def index(self, req): """Returns a list of server groups.""" - context = _authorize_context(req) + context = _authorize_context(req, 'index') project_id = context.project_id if 'all_projects' in req.GET and context.is_admin: sgs = objects.InstanceGroupList.get_all(context) @@ -133,16 +147,15 @@ def index(self, req): @wsgi.Controller.api_version("2.1") @extensions.expected_errors((400, 403)) - @validation.schema(schema.create, "2.1", "2.14") + @validation.schema(schema.create, "2.0", "2.14") @validation.schema(schema.create_v215, "2.15") def create(self, req, body): """Creates a new server group.""" - context = _authorize_context(req) + context = _authorize_context(req, 'create') - quotas = objects.Quotas(context=context) try: - quotas.reserve(project_id=context.project_id, - user_id=context.user_id, server_groups=1) + objects.Quotas.check_deltas(context, {'server_groups': 1}, + context.project_id, context.user_id) except nova.exception.OverQuota: msg = _("Quota exceeded, too many server groups.") raise exc.HTTPForbidden(explanation=msg) @@ -156,24 +169,20 @@ def create(self, req, body): sg.policies = vals.get('policies') sg.create() except ValueError as e: - quotas.rollback() raise exc.HTTPBadRequest(explanation=e) - quotas.commit() - return {'server_group': self._format_server_group(context, sg, req)} - + # NOTE(melwitt): We recheck the quota after creating the object to + # prevent users from allocating more resources than their allowed quota + # in the event of a race. This is configurable because it can be + # expensive if strict quota limits are not required in a deployment. + if CONF.quota.recheck_quota: + try: + objects.Quotas.check_deltas(context, {'server_groups': 0}, + context.project_id, + context.user_id) + except nova.exception.OverQuota: + sg.destroy() + msg = _("Quota exceeded, too many server groups.") + raise exc.HTTPForbidden(explanation=msg) -class ServerGroups(extensions.V21APIExtensionBase): - """Server group support.""" - name = "ServerGroups" - alias = ALIAS - version = 1 - - def get_resources(self): - res = extensions.ResourceExtension( - ALIAS, controller=ServerGroupController(), - member_actions={"action": "POST", }) - return [res] - - def get_controller_extensions(self): - return [] + return {'server_group': self._format_server_group(context, sg, req)} diff --git a/nova/api/openstack/compute/server_metadata.py b/nova/api/openstack/compute/server_metadata.py index f7be44f97c0..543a34da091 100644 --- a/nova/api/openstack/compute/server_metadata.py +++ b/nova/api/openstack/compute/server_metadata.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import six + from webob import exc from nova.api.openstack import common @@ -24,22 +24,20 @@ from nova import compute from nova import exception from nova.i18n import _ - -ALIAS = 'server-metadata' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import server_metadata as sm_policies class ServerMetadataController(wsgi.Controller): """The server metadata API controller for the OpenStack API.""" def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() super(ServerMetadataController, self).__init__() def _get_metadata(self, context, server_id): server = common.get_instance(self.compute_api, context, server_id) try: - # NOTE(mikal): get_instanc_metadata sometimes returns + # NOTE(mikal): get_instance_metadata sometimes returns # InstanceNotFound in unit tests, even though the instance is # fetched on the line above. I blame mocking. meta = self.compute_api.get_instance_metadata(context, server) @@ -47,7 +45,7 @@ def _get_metadata(self, context, server_id): msg = _('Server does not exist') raise exc.HTTPNotFound(explanation=msg) meta_dict = {} - for key, value in six.iteritems(meta): + for key, value in meta.items(): meta_dict[key] = value return meta_dict @@ -55,17 +53,17 @@ def _get_metadata(self, context, server_id): def index(self, req, server_id): """Returns the list of metadata for a given instance.""" context = req.environ['nova.context'] - authorize(context, action='index') + context.can(sm_policies.POLICY_ROOT % 'index') return {'metadata': self._get_metadata(context, server_id)} - @extensions.expected_errors((400, 403, 404, 409, 413)) + @extensions.expected_errors((400, 403, 404, 409)) # NOTE(gmann): Returns 200 for backwards compatibility but should be 201 # as this operation complete the creation of metadata. @validation.schema(server_metadata.create) def create(self, req, server_id, body): metadata = body['metadata'] context = req.environ['nova.context'] - authorize(context, action='create') + context.can(sm_policies.POLICY_ROOT % 'create') new_metadata = self._update_instance_metadata(context, server_id, metadata, @@ -73,11 +71,11 @@ def create(self, req, server_id, body): return {'metadata': new_metadata} - @extensions.expected_errors((400, 403, 404, 409, 413)) + @extensions.expected_errors((400, 403, 404, 409)) @validation.schema(server_metadata.update) def update(self, req, server_id, id, body): context = req.environ['nova.context'] - authorize(context, action='update') + context.can(sm_policies.POLICY_ROOT % 'update') meta_item = body['meta'] if id not in meta_item: expl = _('Request body and URI mismatch') @@ -90,11 +88,11 @@ def update(self, req, server_id, id, body): return {'meta': meta_item} - @extensions.expected_errors((400, 403, 404, 409, 413)) + @extensions.expected_errors((400, 403, 404, 409)) @validation.schema(server_metadata.update_all) def update_all(self, req, server_id, body): context = req.environ['nova.context'] - authorize(context, action='update_all') + context.can(sm_policies.POLICY_ROOT % 'update_all') metadata = body['metadata'] new_metadata = self._update_instance_metadata(context, server_id, @@ -105,8 +103,8 @@ def update_all(self, req, server_id, body): def _update_instance_metadata(self, context, server_id, metadata, delete=False): + server = common.get_instance(self.compute_api, context, server_id) try: - server = common.get_instance(self.compute_api, context, server_id) return self.compute_api.update_instance_metadata(context, server, metadata, @@ -129,7 +127,7 @@ def _update_instance_metadata(self, context, server_id, metadata, def show(self, req, server_id, id): """Return a single metadata item.""" context = req.environ['nova.context'] - authorize(context, action='show') + context.can(sm_policies.POLICY_ROOT % 'show') data = self._get_metadata(context, server_id) try: @@ -143,7 +141,7 @@ def show(self, req, server_id, id): def delete(self, req, server_id, id): """Deletes an existing metadata.""" context = req.environ['nova.context'] - authorize(context, action='delete') + context.can(sm_policies.POLICY_ROOT % 'delete') metadata = self._get_metadata(context, server_id) if id not in metadata: @@ -163,36 +161,3 @@ def delete(self, req, server_id, id): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'delete metadata', server_id) - - -class ServerMetadata(extensions.V21APIExtensionBase): - """Server Metadata API.""" - name = "ServerMetadata" - alias = ALIAS - version = 1 - - def get_resources(self): - parent = {'member_name': 'server', - 'collection_name': 'servers'} - resources = [extensions.ResourceExtension('metadata', - ServerMetadataController(), - member_name='server_meta', - parent=parent, - custom_routes_fn= - self.server_metadata_map - )] - return resources - - def get_controller_extensions(self): - return [] - - def server_metadata_map(self, mapper, wsgi_resource): - mapper.connect("metadata", - "/{project_id}/servers/{server_id}/metadata", - controller=wsgi_resource, - action='update_all', conditions={"method": ['PUT']}) - # Also connect the non project_id routes - mapper.connect("metadata", - "/servers/{server_id}/metadata", - controller=wsgi_resource, - action='update_all', conditions={"method": ['PUT']}) diff --git a/nova/api/openstack/compute/server_migrations.py b/nova/api/openstack/compute/server_migrations.py index fa5ba5e5b57..c0fd4c38b5a 100644 --- a/nova/api/openstack/compute/server_migrations.py +++ b/nova/api/openstack/compute/server_migrations.py @@ -23,10 +23,7 @@ from nova import compute from nova import exception from nova.i18n import _ - - -ALIAS = 'servers:migrations' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import servers_migrations as sm_policies def output(migration): @@ -59,7 +56,7 @@ class ServerMigrationsController(wsgi.Controller): """The server migrations API controller for the OpenStack API.""" def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() super(ServerMigrationsController, self).__init__() @wsgi.Controller.api_version("2.22") @@ -69,7 +66,7 @@ def __init__(self): @validation.schema(server_migrations.force_complete) def _force_complete(self, req, id, server_id, body): context = req.environ['nova.context'] - authorize(context, action='force_complete') + context.can(sm_policies.POLICY_ROOT % 'force_complete') instance = common.get_instance(self.compute_api, context, server_id) try: @@ -91,7 +88,7 @@ def _force_complete(self, req, id, server_id, body): def index(self, req, server_id): """Return all migrations of an instance in progress.""" context = req.environ['nova.context'] - authorize(context, action="index") + context.can(sm_policies.POLICY_ROOT % 'index') # NOTE(Shaohe Feng) just check the instance is available. To keep # consistency with other API, check it before get migrations. @@ -107,7 +104,7 @@ def index(self, req, server_id): def show(self, req, server_id, id): """Return the migration of an instance in progress by id.""" context = req.environ['nova.context'] - authorize(context, action="show") + context.can(sm_policies.POLICY_ROOT % 'show') # NOTE(Shaohe Feng) just check the instance is available. To keep # consistency with other API, check it before get migrations. @@ -141,7 +138,7 @@ def show(self, req, server_id, id): def delete(self, req, server_id, id): """Abort an in progress migration of an instance.""" context = req.environ['nova.context'] - authorize(context, action="delete") + context.can(sm_policies.POLICY_ROOT % 'delete') instance = common.get_instance(self.compute_api, context, server_id) try: @@ -153,22 +150,3 @@ def delete(self, req, server_id, id): raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InvalidMigrationState as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - - -class ServerMigrations(extensions.V21APIExtensionBase): - """Server Migrations API.""" - name = "ServerMigrations" - alias = 'server-migrations' - version = 1 - - def get_resources(self): - parent = {'member_name': 'server', - 'collection_name': 'servers'} - member_actions = {'action': 'POST'} - resources = [extensions.ResourceExtension( - 'migrations', ServerMigrationsController(), - parent=parent, member_actions=member_actions)] - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/server_password.py b/nova/api/openstack/compute/server_password.py index e10c80b14be..4d9e25c649c 100644 --- a/nova/api/openstack/compute/server_password.py +++ b/nova/api/openstack/compute/server_password.py @@ -20,21 +20,18 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute - - -ALIAS = 'os-server-password' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import server_password as sp_policies class ServerPasswordController(wsgi.Controller): """The Server Password API controller for the OpenStack API.""" def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() @extensions.expected_errors(404) def index(self, req, server_id): context = req.environ['nova.context'] - authorize(context) + context.can(sp_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, server_id) passw = password.extract_password(instance) @@ -50,27 +47,8 @@ def clear(self, req, server_id): """ context = req.environ['nova.context'] - authorize(context) + context.can(sp_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, server_id) meta = password.convert_password(context, None) instance.system_metadata.update(meta) instance.save() - - -class ServerPassword(extensions.V21APIExtensionBase): - """Server password support.""" - - name = "ServerPassword" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [ - extensions.ResourceExtension( - ALIAS, ServerPasswordController(), - collection_actions={'clear': 'DELETE'}, - parent=dict(member_name='server', collection_name='servers'))] - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/server_tags.py b/nova/api/openstack/compute/server_tags.py new file mode 100644 index 00000000000..25413b6b3eb --- /dev/null +++ b/nova/api/openstack/compute/server_tags.py @@ -0,0 +1,222 @@ +# 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 jsonschema +import webob + +from nova.api.openstack import common +from nova.api.openstack.compute.schemas import server_tags as schema +from nova.api.openstack.compute.views import server_tags +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova.api import validation +from nova.api.validation import parameter_types +from nova import compute +from nova.compute import vm_states +from nova import context as nova_context +from nova import exception +from nova.i18n import _ +from nova.notifications import base as notifications_base +from nova import objects +from nova.policies import server_tags as st_policies + + +def _get_tags_names(tags): + return [t.tag for t in tags] + + +def _get_instance_mapping(context, server_id): + try: + return objects.InstanceMapping.get_by_instance_uuid(context, + server_id) + except exception.InstanceMappingNotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + +class ServerTagsController(wsgi.Controller): + _view_builder_class = server_tags.ViewBuilder + + def __init__(self): + self.compute_api = compute.API() + super(ServerTagsController, self).__init__() + + def _check_instance_in_valid_state(self, context, server_id, action): + instance = common.get_instance(self.compute_api, context, server_id) + if instance.vm_state not in (vm_states.ACTIVE, vm_states.PAUSED, + vm_states.SUSPENDED, vm_states.STOPPED): + exc = exception.InstanceInvalidState(attr='vm_state', + instance_uuid=instance.uuid, + state=instance.vm_state, + method=action) + common.raise_http_conflict_for_instance_invalid_state(exc, action, + server_id) + return instance + + @wsgi.Controller.api_version("2.26") + @wsgi.response(204) + @extensions.expected_errors(404) + def show(self, req, server_id, id): + context = req.environ["nova.context"] + context.can(st_policies.POLICY_ROOT % 'show') + + try: + im = objects.InstanceMapping.get_by_instance_uuid(context, + server_id) + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + exists = objects.Tag.exists(cctxt, server_id, id) + except (exception.InstanceNotFound, + exception.InstanceMappingNotFound) as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + if not exists: + msg = (_("Server %(server_id)s has no tag '%(tag)s'") + % {'server_id': server_id, 'tag': id}) + raise webob.exc.HTTPNotFound(explanation=msg) + + @wsgi.Controller.api_version("2.26") + @extensions.expected_errors(404) + def index(self, req, server_id): + context = req.environ["nova.context"] + context.can(st_policies.POLICY_ROOT % 'index') + + try: + im = objects.InstanceMapping.get_by_instance_uuid(context, + server_id) + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + tags = objects.TagList.get_by_resource_id(cctxt, server_id) + except (exception.InstanceNotFound, + exception.InstanceMappingNotFound) as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + return {'tags': _get_tags_names(tags)} + + @wsgi.Controller.api_version("2.26") + @extensions.expected_errors((400, 404, 409)) + @validation.schema(schema.update) + def update(self, req, server_id, id, body): + context = req.environ["nova.context"] + context.can(st_policies.POLICY_ROOT % 'update') + im = _get_instance_mapping(context, server_id) + + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + instance = self._check_instance_in_valid_state( + cctxt, server_id, 'update tag') + + try: + jsonschema.validate(id, parameter_types.tag) + except jsonschema.ValidationError as e: + msg = (_("Tag '%(tag)s' is invalid. It must be a non empty string " + "without characters '/' and ','. Validation error " + "message: %(err)s") % {'tag': id, 'err': e.message}) + raise webob.exc.HTTPBadRequest(explanation=msg) + + try: + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + tags = objects.TagList.get_by_resource_id(cctxt, server_id) + except exception.InstanceNotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + if len(tags) >= objects.instance.MAX_TAG_COUNT: + msg = (_("The number of tags exceeded the per-server limit %d") + % objects.instance.MAX_TAG_COUNT) + raise webob.exc.HTTPBadRequest(explanation=msg) + + if id in _get_tags_names(tags): + # NOTE(snikitin): server already has specified tag + return webob.Response(status_int=204) + + try: + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + tag = objects.Tag(context=cctxt, resource_id=server_id, tag=id) + tag.create() + instance.tags = objects.TagList.get_by_resource_id(cctxt, + server_id) + except exception.InstanceNotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + notifications_base.send_instance_update_notification( + context, instance, service="nova-api") + + response = webob.Response(status_int=201) + response.headers['Location'] = self._view_builder.get_location( + req, server_id, id) + return response + + @wsgi.Controller.api_version("2.26") + @extensions.expected_errors((400, 404, 409)) + @validation.schema(schema.update_all) + def update_all(self, req, server_id, body): + context = req.environ["nova.context"] + context.can(st_policies.POLICY_ROOT % 'update_all') + im = _get_instance_mapping(context, server_id) + + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + instance = self._check_instance_in_valid_state( + cctxt, server_id, 'update tags') + + try: + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + tags = objects.TagList.create(cctxt, server_id, body['tags']) + instance.tags = tags + except exception.InstanceNotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + notifications_base.send_instance_update_notification( + context, instance, service="nova-api") + + return {'tags': _get_tags_names(tags)} + + @wsgi.Controller.api_version("2.26") + @wsgi.response(204) + @extensions.expected_errors((404, 409)) + def delete(self, req, server_id, id): + context = req.environ["nova.context"] + context.can(st_policies.POLICY_ROOT % 'delete') + im = _get_instance_mapping(context, server_id) + + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + instance = self._check_instance_in_valid_state( + cctxt, server_id, 'delete tag') + + try: + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + objects.Tag.destroy(cctxt, server_id, id) + instance.tags = objects.TagList.get_by_resource_id(cctxt, + server_id) + except (exception.InstanceTagNotFound, + exception.InstanceNotFound) as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + notifications_base.send_instance_update_notification( + context, instance, service="nova-api") + + @wsgi.Controller.api_version("2.26") + @wsgi.response(204) + @extensions.expected_errors((404, 409)) + def delete_all(self, req, server_id): + context = req.environ["nova.context"] + context.can(st_policies.POLICY_ROOT % 'delete_all') + im = _get_instance_mapping(context, server_id) + + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + instance = self._check_instance_in_valid_state( + cctxt, server_id, 'delete tags') + + try: + with nova_context.target_cell(context, im.cell_mapping) as cctxt: + objects.TagList.destroy(cctxt, server_id) + instance.tags = objects.TagList() + except exception.InstanceNotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + notifications_base.send_instance_update_notification( + context, instance, service="nova-api") diff --git a/nova/api/openstack/compute/server_usage.py b/nova/api/openstack/compute/server_usage.py index 1ee7adee246..5448f8bc23f 100644 --- a/nova/api/openstack/compute/server_usage.py +++ b/nova/api/openstack/compute/server_usage.py @@ -12,13 +12,10 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.api.openstack import extensions from nova.api.openstack import wsgi +from nova.policies import server_usage as su_policies -ALIAS = "os-server-usage" -authorize = extensions.os_compute_soft_authorizer(ALIAS) - resp_topic = "OS-SRV-USG" @@ -37,7 +34,7 @@ def _extend_server(self, server, instance): @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['nova.context'] - if authorize(context): + if context.can(su_policies.BASE_POLICY_NAME, fatal=False): server = resp_obj.obj['server'] db_instance = req.get_db_instance(server['id']) # server['id'] is guaranteed to be in the cache due to @@ -47,26 +44,10 @@ def show(self, req, resp_obj, id): @wsgi.extends def detail(self, req, resp_obj): context = req.environ['nova.context'] - if authorize(context): + if context.can(su_policies.BASE_POLICY_NAME, fatal=False): servers = list(resp_obj.obj['servers']) for server in servers: db_instance = req.get_db_instance(server['id']) # server['id'] is guaranteed to be in the cache due to # the core API adding it in its 'detail' method. self._extend_server(server, db_instance) - - -class ServerUsage(extensions.V21APIExtensionBase): - """Adds launched_at and terminated_at on Servers.""" - - name = "ServerUsage" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = ServerUsageController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 8e0869a84fa..48f0d64982b 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -14,68 +14,63 @@ # License for the specific language governing permissions and limitations # under the License. -import base64 -import re +import copy -from oslo_config import cfg from oslo_log import log as logging import oslo_messaging as messaging from oslo_utils import strutils from oslo_utils import timeutils from oslo_utils import uuidutils import six -import stevedore import webob from webob import exc from nova.api.openstack import api_version_request from nova.api.openstack import common +from nova.api.openstack.compute import availability_zone +from nova.api.openstack.compute import block_device_mapping +from nova.api.openstack.compute import block_device_mapping_v1 +from nova.api.openstack.compute import config_drive +from nova.api.openstack.compute import helpers +from nova.api.openstack.compute import keypairs +from nova.api.openstack.compute import multiple_create +from nova.api.openstack.compute import scheduler_hints from nova.api.openstack.compute.schemas import servers as schema_servers +from nova.api.openstack.compute import security_groups +from nova.api.openstack.compute import user_data from nova.api.openstack.compute.views import servers as views_servers from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api import validation from nova import compute from nova.compute import flavors +from nova.compute import utils as compute_utils +import nova.conf +from nova import context as nova_context from nova import exception from nova.i18n import _ -from nova.i18n import _LW from nova.image import glance from nova import objects +from nova.objects import service as service_obj +from nova.policies import servers as server_policies from nova import utils -ALIAS = 'servers' +TAG_SEARCH_FILTERS = ('tags', 'tags-any', 'not-tags', 'not-tags-any') +DEVICE_TAGGING_MIN_COMPUTE_VERSION = 14 -CONF = cfg.CONF -CONF.import_opt('enable_instance_password', - 'nova.api.openstack.compute.legacy_v2.servers') -CONF.import_opt('reclaim_instance_interval', 'nova.compute.manager') -CONF.import_opt('extensions_blacklist', 'nova.api.openstack', - group='osapi_v21') -CONF.import_opt('extensions_whitelist', 'nova.api.openstack', - group='osapi_v21') +CONF = nova.conf.CONF LOG = logging.getLogger(__name__) -authorize = extensions.os_compute_authorizer(ALIAS) class ServersController(wsgi.Controller): """The Server API base controller class for the OpenStack API.""" - EXTENSION_CREATE_NAMESPACE = 'nova.api.v21.extensions.server.create' - - EXTENSION_REBUILD_NAMESPACE = 'nova.api.v21.extensions.server.rebuild' - - EXTENSION_UPDATE_NAMESPACE = 'nova.api.v21.extensions.server.update' - - EXTENSION_RESIZE_NAMESPACE = 'nova.api.v21.extensions.server.resize' - - _view_builder_class = views_servers.ViewBuilderV21 + _view_builder_class = views_servers.ViewBuilder schema_server_create = schema_servers.base_create schema_server_update = schema_servers.base_update schema_server_rebuild = schema_servers.base_rebuild - schema_server_resize = schema_servers.base_resize schema_server_create_v20 = schema_servers.base_create_v20 schema_server_update_v20 = schema_servers.base_update_v20 @@ -85,6 +80,39 @@ class ServersController(wsgi.Controller): schema_server_update_v219 = schema_servers.base_update_v219 schema_server_rebuild_v219 = schema_servers.base_rebuild_v219 + schema_server_create_v232 = schema_servers.base_create_v232 + schema_server_create_v237 = schema_servers.base_create_v237 + schema_server_create_v242 = schema_servers.base_create_v242 + schema_server_create_v252 = schema_servers.base_create_v252 + + # NOTE(alex_xu): Please do not add more items into this list. This list + # should be removed in the future. + schema_func_list = [ + availability_zone.get_server_create_schema, + block_device_mapping.get_server_create_schema, + block_device_mapping_v1.get_server_create_schema, + config_drive.get_server_create_schema, + keypairs.get_server_create_schema, + multiple_create.get_server_create_schema, + scheduler_hints.get_server_create_schema, + security_groups.get_server_create_schema, + user_data.get_server_create_schema, + ] + + # NOTE(alex_xu): Please do not add more items into this list. This list + # should be removed in the future. + server_create_func_list = [ + availability_zone.server_create, + block_device_mapping.server_create, + block_device_mapping_v1.server_create, + config_drive.server_create, + keypairs.server_create, + multiple_create.server_create, + scheduler_hints.server_create, + security_groups.server_create, + user_data.server_create, + ] + @staticmethod def _add_location(robj): # Just in case... @@ -99,186 +127,30 @@ def _add_location(robj): return robj def __init__(self, **kwargs): - def _check_load_extension(required_function): - - def should_load_extension(ext): - # Check whitelist is either empty or if not then the extension - # is in the whitelist - whitelist = CONF.osapi_v21.extensions_whitelist - blacklist = CONF.osapi_v21.extensions_blacklist - if not whitelist: - # if there is no whitelist, we accept everything, - # so we only care about the blacklist. - if ext.obj.alias in blacklist: - return False - else: - return True - else: - if ext.obj.alias in whitelist: - if ext.obj.alias in blacklist: - LOG.warning( - _LW( - "Extension %s is both in whitelist and " - "blacklist, blacklisting takes precedence" - ), - ext.obj.alias) - return False - else: - return True - else: - return False - - def check_load_extension(ext): - if isinstance(ext.obj, extensions.V21APIExtensionBase): - # Filter out for the existence of the required - # function here rather than on every request. We - # don't have a new abstract base class to reduce - # duplication in the extensions as they may want - # to implement multiple server (and other) entry - # points if hasattr(ext.obj, 'server_create'): - if hasattr(ext.obj, required_function): - LOG.debug('extension %(ext_alias)s detected by ' - 'servers extension for function %(func)s', - {'ext_alias': ext.obj.alias, - 'func': required_function}) - return should_load_extension(ext) - else: - LOG.debug( - 'extension %(ext_alias)s is missing %(func)s', - {'ext_alias': ext.obj.alias, - 'func': required_function}) - return False - else: - return False - return check_load_extension + # TODO(alex_xu): Remove this line when 'extension_info' won't be passed + # in when creating controller. + kwargs.pop('extension_info', None) - self.extension_info = kwargs.pop('extension_info') super(ServersController, self).__init__(**kwargs) - self.compute_api = compute.API(skip_policy_check=True) - - # Look for implementation of extension point of server creation - self.create_extension_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_CREATE_NAMESPACE, - check_func=_check_load_extension('server_create'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if not list(self.create_extension_manager): - LOG.debug("Did not find any server create extensions") - - # Look for implementation of extension point of server rebuild - self.rebuild_extension_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_REBUILD_NAMESPACE, - check_func=_check_load_extension('server_rebuild'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if not list(self.rebuild_extension_manager): - LOG.debug("Did not find any server rebuild extensions") - - # Look for implementation of extension point of server update - self.update_extension_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_UPDATE_NAMESPACE, - check_func=_check_load_extension('server_update'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if not list(self.update_extension_manager): - LOG.debug("Did not find any server update extensions") - - # Look for implementation of extension point of server resize - self.resize_extension_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_RESIZE_NAMESPACE, - check_func=_check_load_extension('server_resize'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if not list(self.resize_extension_manager): - LOG.debug("Did not find any server resize extensions") - - # Look for API schema of server create extension - self.create_schema_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_CREATE_NAMESPACE, - check_func=_check_load_extension('get_server_create_schema'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if list(self.create_schema_manager): - self.create_schema_manager.map(self._create_extension_schema, - self.schema_server_create_v219, - '2.19') - self.create_schema_manager.map(self._create_extension_schema, - self.schema_server_create, '2.1') - self.create_schema_manager.map(self._create_extension_schema, - self.schema_server_create_v20, - '2.0') - else: - LOG.debug("Did not find any server create schemas") - - # Look for API schema of server update extension - self.update_schema_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_UPDATE_NAMESPACE, - check_func=_check_load_extension('get_server_update_schema'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if list(self.update_schema_manager): - self.update_schema_manager.map(self._update_extension_schema, - self.schema_server_update_v219, - '2.19') - self.update_schema_manager.map(self._update_extension_schema, - self.schema_server_update, '2.1') - self.update_schema_manager.map(self._update_extension_schema, - self.schema_server_update_v20, - '2.0') - else: - LOG.debug("Did not find any server update schemas") - - # Look for API schema of server rebuild extension - self.rebuild_schema_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_REBUILD_NAMESPACE, - check_func=_check_load_extension('get_server_rebuild_schema'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if list(self.rebuild_schema_manager): - self.rebuild_schema_manager.map(self._rebuild_extension_schema, - self.schema_server_rebuild_v219, - '2.19') - self.rebuild_schema_manager.map(self._rebuild_extension_schema, - self.schema_server_rebuild, '2.1') - self.rebuild_schema_manager.map(self._rebuild_extension_schema, - self.schema_server_rebuild_v20, - '2.0') - else: - LOG.debug("Did not find any server rebuild schemas") - - # Look for API schema of server resize extension - self.resize_schema_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_RESIZE_NAMESPACE, - check_func=_check_load_extension('get_server_resize_schema'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if list(self.resize_schema_manager): - self.resize_schema_manager.map(self._resize_extension_schema, - self.schema_server_resize, '2.1') - else: - LOG.debug("Did not find any server resize schemas") + self.compute_api = compute.API() + + # TODO(alex_xu): The final goal is that merging all of + # extended json-schema into server main json-schema. + self._create_schema(self.schema_server_create_v252, '2.52') + self._create_schema(self.schema_server_create_v242, '2.42') + self._create_schema(self.schema_server_create_v237, '2.37') + self._create_schema(self.schema_server_create_v232, '2.32') + self._create_schema(self.schema_server_create_v219, '2.19') + self._create_schema(self.schema_server_create, '2.1') + self._create_schema(self.schema_server_create_v20, '2.0') @extensions.expected_errors((400, 403)) + @validation.query_schema(schema_servers.query_params_v226, '2.26') + @validation.query_schema(schema_servers.query_params_v21, '2.1', '2.25') def index(self, req): """Returns a list of server names and ids for a given user.""" context = req.environ['nova.context'] - authorize(context, action="index") + context.can(server_policies.SERVERS % 'index') try: servers = self._get_servers(req, is_detail=False) except exception.Invalid as err: @@ -286,10 +158,12 @@ def index(self, req): return servers @extensions.expected_errors((400, 403)) + @validation.query_schema(schema_servers.query_params_v226, '2.26') + @validation.query_schema(schema_servers.query_params_v21, '2.1', '2.25') def detail(self, req): """Returns a list of server details for a given user.""" context = req.environ['nova.context'] - authorize(context, action="detail") + context.can(server_policies.SERVERS % 'detail') try: servers = self._get_servers(req, is_detail=True) except exception.Invalid as err: @@ -306,14 +180,28 @@ def _get_servers(self, req, is_detail): remove_invalid_options(context, search_opts, self._get_server_search_options(req)) + for search_opt in search_opts: + if (search_opt in + schema_servers.JOINED_TABLE_QUERY_PARAMS_SERVERS.keys() or + search_opt.startswith('_')): + msg = _("Invalid filter field: %s.") % search_opt + raise exc.HTTPBadRequest(explanation=msg) + # Verify search by 'status' contains a valid status. # Convert it to filter by vm_state or task_state for compute_api. + # For non-admin user, vm_state and task_state are filtered through + # remove_invalid_options function, based on value of status field. + # Set value to vm_state and task_state to make search simple. search_opts.pop('status', None) if 'status' in req.GET.keys(): statuses = req.GET.getall('status') states = common.task_and_vm_state_from_status(statuses) vm_state, task_state = states if not vm_state and not task_state: + if api_version_request.is_supported(req, min_version='2.38'): + msg = _('Invalid status value') + raise exc.HTTPBadRequest(explanation=msg) + return {'servers': []} search_opts['vm_state'] = vm_state # When we search by vm state, task state will return 'default'. @@ -322,12 +210,8 @@ def _get_servers(self, req, is_detail): search_opts['task_state'] = task_state if 'changes-since' in search_opts: - try: - parsed = timeutils.parse_isotime(search_opts['changes-since']) - except ValueError: - msg = _('Invalid changes-since value') - raise exc.HTTPBadRequest(explanation=msg) - search_opts['changes-since'] = parsed + search_opts['changes-since'] = timeutils.parse_isotime( + search_opts['changes-since']) # By default, compute's get_all() will return deleted instances. # If an admin hasn't specified a 'deleted' search option, we need @@ -353,6 +237,12 @@ def _get_servers(self, req, is_detail): msg = _("Only administrators may list deleted instances") raise exc.HTTPForbidden(explanation=msg) + if api_version_request.is_supported(req, min_version='2.26'): + for tag_filter in TAG_SEARCH_FILTERS: + if tag_filter in search_opts: + search_opts[tag_filter] = search_opts[ + tag_filter].split(',') + # If tenant_id is passed as a search parameter this should # imply that all_tenants is also enabled unless explicitly # disabled. Note that the tenant_id parameter is filtered out @@ -382,11 +272,16 @@ def _get_servers(self, req, is_detail): elevated = None if all_tenants: if is_detail: - authorize(context, action="detail:get_all_tenants") + context.can(server_policies.SERVERS % 'detail:get_all_tenants') else: - authorize(context, action="index:get_all_tenants") + context.can(server_policies.SERVERS % 'index:get_all_tenants') elevated = context.elevated() else: + # As explained in lp:#1185290, if `all_tenants` is not passed + # we must ignore the `tenant_id` search option. As explained + # in a above code comment, any change to this behavior would + # require a microversion bump. + search_opts.pop('tenant_id', None) if context.project_id: search_opts['project_id'] = context.project_id else: @@ -394,9 +289,16 @@ def _get_servers(self, req, is_detail): limit, marker = common.get_limit_and_marker(req) sort_keys, sort_dirs = common.get_sort_params(req.params) + sort_keys, sort_dirs = remove_invalid_sort_keys( + context, sort_keys, sort_dirs, + schema_servers.SERVER_LIST_IGNORE_SORT_KEY, ('host', 'node')) - expected_attrs = ['pci_devices'] + expected_attrs = [] if is_detail: + expected_attrs.append('services') + if api_version_request.is_supported(req, '2.26'): + expected_attrs.append("tags") + # merge our expected attrs with what the view builder needs for # showing details expected_attrs = self._view_builder.get_show_expected_attrs( @@ -405,7 +307,7 @@ def _get_servers(self, req, is_detail): try: instance_list = self.compute_api.get_all(elevated or context, search_opts=search_opts, limit=limit, marker=marker, - want_objects=True, expected_attrs=expected_attrs, + expected_attrs=expected_attrs, sort_keys=sort_keys, sort_dirs=sort_dirs) except exception.MarkerNotFound: msg = _('marker [%s] not found') % marker @@ -433,8 +335,10 @@ def _get_server(self, context, req, instance_uuid, is_detail=False): :param is_detail: True if you plan on showing the details of the instance in the response, False otherwise. """ - expected_attrs = ['flavor', 'pci_devices', 'numa_topology'] + expected_attrs = ['flavor', 'numa_topology'] if is_detail: + if api_version_request.is_supported(req, '2.26'): + expected_attrs.append("tags") expected_attrs = self._view_builder.get_show_expected_attrs( expected_attrs) instance = common.get_instance(self.compute_api, context, @@ -443,8 +347,47 @@ def _get_server(self, context, req, instance_uuid, is_detail=False): req.cache_db_instance(instance) return instance - def _get_requested_networks(self, requested_networks): + @staticmethod + def _validate_network_id(net_id, network_uuids): + """Validates that a requested network id. + + This method performs two checks: + + 1. That the network id is in the proper uuid format. + 2. That the network is not a duplicate when using nova-network. + + :param net_id: The network id to validate. + :param network_uuids: A running list of requested network IDs that have + passed validation already. + :raises: webob.exc.HTTPBadRequest if validation fails + """ + if not uuidutils.is_uuid_like(net_id): + # NOTE(mriedem): Neutron would allow a network id with a br- prefix + # back in Folsom so continue to honor that. + # TODO(mriedem): Need to figure out if this is still a valid case. + br_uuid = net_id.split('-', 1)[-1] + if not uuidutils.is_uuid_like(br_uuid): + msg = _("Bad networks format: network uuid is " + "not in proper format (%s)") % net_id + raise exc.HTTPBadRequest(explanation=msg) + + # duplicate networks are allowed only for neutron v2.0 + if net_id in network_uuids and not utils.is_neutron(): + expl = _("Duplicate networks (%s) are not allowed") % net_id + raise exc.HTTPBadRequest(explanation=expl) + + def _get_requested_networks(self, requested_networks, + supports_device_tagging=False): """Create a list of requested networks from the networks attribute.""" + + # Starting in the 2.37 microversion, requested_networks is either a + # list or a string enum with value 'auto' or 'none'. The auto/none + # values are verified via jsonschema so we don't check them again here. + if isinstance(requested_networks, six.string_types): + return objects.NetworkRequestList( + objects=[objects.NetworkRequest( + network_id=requested_networks)]) + networks = [] network_uuids = [] for network in requested_networks: @@ -456,6 +399,11 @@ def _get_requested_networks(self, requested_networks): request.address = network.get('fixed_ip', None) request.port_id = network.get('port', None) + request.tag = network.get('tag', None) + if request.tag and not supports_device_tagging: + msg = _('Network interface tags are not yet supported.') + raise exc.HTTPBadRequest(explanation=msg) + if request.port_id: request.network_id = None if not utils.is_neutron(): @@ -464,31 +412,17 @@ def _get_requested_networks(self, requested_networks): raise exc.HTTPBadRequest(explanation=msg) if request.address is not None: msg = _("Specified Fixed IP '%(addr)s' cannot be used " - "with port '%(port)s': port already has " - "a Fixed IP allocated.") % { + "with port '%(port)s': the two cannot be " + "specified together.") % { "addr": request.address, "port": request.port_id} raise exc.HTTPBadRequest(explanation=msg) else: request.network_id = network['uuid'] + self._validate_network_id( + request.network_id, network_uuids) + network_uuids.append(request.network_id) - if (not request.port_id and - not uuidutils.is_uuid_like(request.network_id)): - br_uuid = request.network_id.split('-', 1)[-1] - if not uuidutils.is_uuid_like(br_uuid): - msg = _("Bad networks format: network uuid is " - "not in proper format " - "(%s)") % request.network_id - raise exc.HTTPBadRequest(explanation=msg) - - # duplicate networks are allowed only for neutron v2.0 - if (not utils.is_neutron() and request.network_id and - request.network_id in network_uuids): - expl = (_("Duplicate networks" - " (%s) are not allowed") % - request.network_id) - raise exc.HTTPBadRequest(explanation=expl) - network_uuids.append(request.network_id) networks.append(request) except KeyError as key: expl = _('Bad network format: missing %s') % key @@ -499,91 +433,85 @@ def _get_requested_networks(self, requested_networks): return objects.NetworkRequestList(objects=networks) - # NOTE(vish): Without this regex, b64decode will happily - # ignore illegal bytes in the base64 encoded - # data. - B64_REGEX = re.compile('^(?:[A-Za-z0-9+\/]{4})*' - '(?:[A-Za-z0-9+\/]{2}==' - '|[A-Za-z0-9+\/]{3}=)?$') - - def _decode_base64(self, data): - data = re.sub(r'\s', '', data) - if not self.B64_REGEX.match(data): - return None - try: - return base64.b64decode(data) - except TypeError: - return None - @extensions.expected_errors(404) def show(self, req, id): """Returns server details by server id.""" context = req.environ['nova.context'] - authorize(context, action="show") + context.can(server_policies.SERVERS % 'show') instance = self._get_server(context, req, id, is_detail=True) return self._view_builder.show(req, instance) @wsgi.response(202) - @extensions.expected_errors((400, 403, 409, 413)) + @extensions.expected_errors((400, 403, 409)) @validation.schema(schema_server_create_v20, '2.0', '2.0') @validation.schema(schema_server_create, '2.1', '2.18') - @validation.schema(schema_server_create_v219, '2.19') + @validation.schema(schema_server_create_v219, '2.19', '2.31') + @validation.schema(schema_server_create_v232, '2.32', '2.36') + @validation.schema(schema_server_create_v237, '2.37', '2.41') + @validation.schema(schema_server_create_v242, '2.42', '2.51') + @validation.schema(schema_server_create_v252, '2.52') def create(self, req, body): """Creates a new server for a given user.""" - context = req.environ['nova.context'] server_dict = body['server'] password = self._get_server_admin_password(server_dict) name = common.normalize_name(server_dict['name']) - + description = name if api_version_request.is_supported(req, min_version='2.19'): - if 'description' in server_dict: - # This is allowed to be None - description = server_dict['description'] - else: - # No default description - description = None - else: - description = name + description = server_dict.get('description') # Arguments to be passed to instance create function create_kwargs = {} - # Query extensions which want to manipulate the keyword - # arguments. - # NOTE(cyeoh): This is the hook that extensions use - # to replace the extension specific code below. - # When the extensions are ported this will also result - # in some convenience function from this class being - # moved to the extension - if list(self.create_extension_manager): - self.create_extension_manager.map(self._create_extension_point, - server_dict, create_kwargs, body) + # TODO(alex_xu): This is for back-compatible with stevedore + # extension interface. But the final goal is that merging + # all of extended code into ServersController. + self._create_by_func_list(server_dict, create_kwargs, body) availability_zone = create_kwargs.pop("availability_zone", None) + if api_version_request.is_supported(req, min_version='2.52'): + create_kwargs['tags'] = server_dict.get('tags') + + helpers.translate_attributes(helpers.CREATE, + server_dict, create_kwargs) + target = { 'project_id': context.project_id, 'user_id': context.user_id, 'availability_zone': availability_zone} - authorize(context, target, 'create') + context.can(server_policies.SERVERS % 'create', target) - # TODO(Shao He, Feng) move this policy check to os-availabilty-zone + # TODO(Shao He, Feng) move this policy check to os-availability-zone # extension after refactor it. parse_az = self.compute_api.parse_availability_zone - availability_zone, host, node = parse_az(context, availability_zone) + try: + availability_zone, host, node = parse_az(context, + availability_zone) + except exception.InvalidInput as err: + raise exc.HTTPBadRequest(explanation=six.text_type(err)) if host or node: - authorize(context, {}, 'create:forced_host') + context.can(server_policies.SERVERS % 'create:forced_host', {}) + + min_compute_version = service_obj.get_minimum_version_all_cells( + nova_context.get_admin_context(), ['nova-compute']) + supports_device_tagging = (min_compute_version >= + DEVICE_TAGGING_MIN_COMPUTE_VERSION) block_device_mapping = create_kwargs.get("block_device_mapping") # TODO(Shao He, Feng) move this policy check to os-block-device-mapping # extension after refactor it. if block_device_mapping: - authorize(context, target, 'create:attach_volume') + context.can(server_policies.SERVERS % 'create:attach_volume', + target) + for bdm in block_device_mapping: + if bdm.get('tag', None) and not supports_device_tagging: + msg = _('Block device tags are not yet supported.') + raise exc.HTTPBadRequest(explanation=msg) image_uuid = self._image_from_req_data(server_dict, create_kwargs) - # NOTE(cyeoh): Although an extension can set + # NOTE(cyeoh): Although upper layer can set the value of # return_reservation_id in order to request that a reservation # id be returned to the client instead of the newly created # instance information we do not want to pass this parameter @@ -593,24 +521,20 @@ def create(self, req, body): return_reservation_id = create_kwargs.pop('return_reservation_id', False) - requested_networks = None - if ('os-networks' in self.extension_info.get_extensions() - or utils.is_neutron()): - requested_networks = server_dict.get('networks') + requested_networks = server_dict.get('networks', None) if requested_networks is not None: requested_networks = self._get_requested_networks( - requested_networks) + requested_networks, supports_device_tagging) - if requested_networks and len(requested_networks): - authorize(context, target, 'create:attach_network') - - try: - flavor_id = self._flavor_id_from_req_data(body) - except ValueError: - msg = _("Invalid flavorRef provided.") - raise exc.HTTPBadRequest(explanation=msg) + # Skip policy check for 'create:attach_network' if there is no + # network allocation request. + if requested_networks and len(requested_networks) and \ + not requested_networks.no_allocate: + context.can(server_policies.SERVERS % 'create:attach_network', + target) + flavor_id = self._flavor_id_from_req_data(body) try: inst_type = flavors.get_flavor_by_flavor_id( flavor_id, ctxt=context, read_deleted="no") @@ -634,9 +558,6 @@ def create(self, req, body): except exception.ImageNotFound: msg = _("Can not find requested image") raise exc.HTTPBadRequest(explanation=msg) - except exception.FlavorNotFound: - msg = _("Invalid flavorRef provided.") - raise exc.HTTPBadRequest(explanation=msg) except exception.KeypairNotFound: msg = _("Invalid key_name provided.") raise exc.HTTPBadRequest(explanation=msg) @@ -652,9 +573,12 @@ def create(self, req, body): except UnicodeDecodeError as error: msg = "UnicodeError: %s" % error raise exc.HTTPBadRequest(explanation=msg) - except (exception.ImageNotActive, + except (exception.CPUThreadPolicyConfigurationInvalid, + exception.ImageNotActive, exception.ImageBadRequest, + exception.ImageNotAuthorized, exception.FixedIpNotFoundForAddress, + exception.FlavorNotFound, exception.FlavorDiskTooSmall, exception.FlavorMemoryTooSmall, exception.InvalidMetadata, @@ -670,7 +594,6 @@ def create(self, req, body): exception.PortRequiresFixedIP, exception.NetworkRequiresSubnet, exception.NetworkNotFound, - exception.NetworkDuplicated, exception.InvalidBDM, exception.InvalidBDMSnapshot, exception.InvalidBDMVolume, @@ -682,6 +605,8 @@ def create(self, req, body): exception.InvalidBDMFormat, exception.InvalidBDMSwapSize, exception.AutoDiskConfigDisabledByImage, + exception.ImageCPUPinningForbidden, + exception.ImageCPUThreadPolicyForbidden, exception.ImageNUMATopologyIncomplete, exception.ImageNUMATopologyForbidden, exception.ImageNUMATopologyAsymmetric, @@ -689,7 +614,15 @@ def create(self, req, body): exception.ImageNUMATopologyCPUDuplicates, exception.ImageNUMATopologyCPUsUnassigned, exception.ImageNUMATopologyMemoryOutOfRange, - exception.InstanceGroupNotFound) as error: + exception.InvalidNUMANodesNumber, + exception.InstanceGroupNotFound, + exception.MemoryPageSizeInvalid, + exception.MemoryPageSizeForbidden, + exception.PciRequestAliasNotDefined, + exception.RealtimeConfigurationInvalid, + exception.RealtimeMaskNotFoundOrInvalid, + exception.SnapshotNotFound, + exception.UnableToAutoAllocateNetwork) as error: raise exc.HTTPBadRequest(explanation=error.format_message()) except (exception.PortInUse, exception.InstanceExists, @@ -699,16 +632,12 @@ def create(self, req, body): # If the caller wanted a reservation_id, return it if return_reservation_id: - # NOTE(cyeoh): In v3 reservation_id was wrapped in - # servers_reservation but this is reverted for V2 API - # compatibility. In the long term with the tasks API we - # will probably just drop the concept of reservation_id return wsgi.ResponseObject({'reservation_id': resv_id}) req.cache_db_instances(instances) server = self._view_builder.create(req, instances[0]) - if CONF.enable_instance_password: + if CONF.api.enable_instance_password: server['server']['adminPass'] = password robj = wsgi.ResponseObject(server) @@ -718,36 +647,20 @@ def create(self, req, body): # NOTE(gmann): Parameter 'req_body' is placed to handle scheduler_hint # extension for V2.1. No other extension supposed to use this as # it will be removed soon. - def _create_extension_point(self, ext, server_dict, - create_kwargs, req_body): - handler = ext.obj - LOG.debug("Running _create_extension_point for %s", ext.obj) - - handler.server_create(server_dict, create_kwargs, req_body) - - def _rebuild_extension_point(self, ext, rebuild_dict, rebuild_kwargs): - handler = ext.obj - LOG.debug("Running _rebuild_extension_point for %s", ext.obj) + def _create_by_func_list(self, server_dict, + create_kwargs, req_body): + for func in self.server_create_func_list: + func(server_dict, create_kwargs, req_body) - handler.server_rebuild(rebuild_dict, rebuild_kwargs) + def _create_schema(self, create_schema, version): + for schema_func in self.schema_func_list: + self._create_schema_by_func(create_schema, version, schema_func) - def _resize_extension_point(self, ext, resize_dict, resize_kwargs): - handler = ext.obj - LOG.debug("Running _resize_extension_point for %s", ext.obj) + def _create_schema_by_func(self, create_schema, version, schema_func): + schema = schema_func(version) - handler.server_resize(resize_dict, resize_kwargs) - - def _update_extension_point(self, ext, update_dict, update_kwargs): - handler = ext.obj - LOG.debug("Running _update_extension_point for %s", ext.obj) - handler.server_update(update_dict, update_kwargs) - - def _create_extension_schema(self, ext, create_schema, version): - handler = ext.obj - LOG.debug("Running _create_extension_schema for %s", ext.obj) - - schema = handler.get_server_create_schema(version) - if ext.obj.name == 'SchedulerHints': + if (schema_func.__module__ == + 'nova.api.openstack.compute.scheduler_hints'): # NOTE(oomichi): The request parameter position of scheduler-hint # extension is different from the other extensions, so here handles # the difference. @@ -755,30 +668,11 @@ def _create_extension_schema(self, ext, create_schema, version): else: create_schema['properties']['server']['properties'].update(schema) - def _update_extension_schema(self, ext, update_schema, version): - handler = ext.obj - LOG.debug("Running _update_extension_schema for %s", ext.obj) - - schema = handler.get_server_update_schema(version) - update_schema['properties']['server']['properties'].update(schema) - - def _rebuild_extension_schema(self, ext, rebuild_schema, version): - handler = ext.obj - LOG.debug("Running _rebuild_extension_schema for %s", ext.obj) - - schema = handler.get_server_rebuild_schema(version) - rebuild_schema['properties']['rebuild']['properties'].update(schema) - - def _resize_extension_schema(self, ext, resize_schema, version): - handler = ext.obj - LOG.debug("Running _resize_extension_schema for %s", ext.obj) - - schema = handler.get_server_resize_schema(version) - resize_schema['properties']['resize']['properties'].update(schema) - def _delete(self, context, req, instance_uuid): - authorize(context, action='delete') instance = self._get_server(context, req, instance_uuid) + context.can(server_policies.SERVERS % 'delete', + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) if CONF.reclaim_instance_interval: try: self.compute_api.soft_delete(context, instance) @@ -799,26 +693,26 @@ def update(self, req, id, body): ctxt = req.environ['nova.context'] update_dict = {} - authorize(ctxt, action='update') + instance = self._get_server(ctxt, req, id, is_detail=True) + ctxt.can(server_policies.SERVERS % 'update', + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) - if 'name' in body['server']: + server = body['server'] + + if 'name' in server: update_dict['display_name'] = common.normalize_name( - body['server']['name']) + server['name']) - if 'description' in body['server']: + if 'description' in server: # This is allowed to be None (remove description) - update_dict['display_description'] = body['server']['description'] + update_dict['display_description'] = server['description'] - if list(self.update_extension_manager): - self.update_extension_manager.map(self._update_extension_point, - body['server'], update_dict) + helpers.translate_attributes(helpers.UPDATE, server, update_dict) - instance = self._get_server(ctxt, req, id, is_detail=True) try: - # NOTE(mikal): this try block needs to stay because save() still - # might throw an exception. - instance.update(update_dict) - instance.save() + instance = self.compute_api.update_instance(ctxt, instance, + update_dict) return self._view_builder.show(req, instance, extend_address=False) except exception.InstanceNotFound: @@ -833,7 +727,7 @@ def update(self, req, id, body): @wsgi.action('confirmResize') def _action_confirm_resize(self, req, id, body): context = req.environ['nova.context'] - authorize(context, action='confirm_resize') + context.can(server_policies.SERVERS % 'confirm_resize') instance = self._get_server(context, req, id) try: self.compute_api.confirm_resize(context, instance) @@ -853,7 +747,7 @@ def _action_confirm_resize(self, req, id, body): @wsgi.action('revertResize') def _action_revert_resize(self, req, id, body): context = req.environ['nova.context'] - authorize(context, action='revert_resize') + context.can(server_policies.SERVERS % 'revert_resize') instance = self._get_server(context, req, id) try: self.compute_api.revert_resize(context, instance) @@ -879,7 +773,7 @@ def _action_reboot(self, req, id, body): reboot_type = body['reboot']['type'].upper() context = req.environ['nova.context'] - authorize(context, action='reboot') + context.can(server_policies.SERVERS % 'reboot') instance = self._get_server(context, req, id) try: @@ -893,8 +787,10 @@ def _action_reboot(self, req, id, body): def _resize(self, req, instance_id, flavor_id, **kwargs): """Begin the resize process with given instance/flavor.""" context = req.environ["nova.context"] - authorize(context, action='resize') instance = self._get_server(context, req, instance_id) + context.can(server_policies.SERVERS % 'resize', + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) try: self.compute_api.resize(context, instance, flavor_id, **kwargs) @@ -920,7 +816,8 @@ def _resize(self, req, instance_id, flavor_id, **kwargs): exception.CannotResizeDisk, exception.CannotResizeToSameFlavor, exception.FlavorNotFound, - exception.NoValidHost) as e: + exception.NoValidHost, + exception.PciRequestAliasNotDefined) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.Invalid: msg = _("Invalid instance image.") @@ -943,17 +840,6 @@ def delete(self, req, id): common.raise_http_conflict_for_instance_invalid_state(state_error, 'delete', id) - def _image_uuid_from_href(self, image_href): - # If the image href was generated by nova api, strip image_href - # down to an id and use the default glance connection params - image_uuid = image_href.split('/').pop() - - if not uuidutils.is_uuid_like(image_uuid): - msg = _("Invalid imageRef provided.") - raise exc.HTTPBadRequest(explanation=msg) - - return image_uuid - def _image_from_req_data(self, server_dict, create_kwargs): """Get image data from the request or raise appropriate exceptions. @@ -966,7 +852,7 @@ def _image_from_req_data(self, server_dict, create_kwargs): if not image_href and create_kwargs.get('block_device_mapping'): return '' elif image_href: - return self._image_uuid_from_href(six.text_type(image_href)) + return image_href else: msg = _("Missing imageRef attribute") raise exc.HTTPBadRequest(explanation=msg) @@ -978,22 +864,19 @@ def _flavor_id_from_req_data(self, data): @wsgi.response(202) @extensions.expected_errors((400, 401, 403, 404, 409)) @wsgi.action('resize') - @validation.schema(schema_server_resize) + @validation.schema(schema_servers.resize) def _action_resize(self, req, id, body): """Resizes a given instance to the flavor size requested.""" resize_dict = body['resize'] flavor_ref = str(resize_dict["flavorRef"]) - resize_kwargs = {} - - if list(self.resize_extension_manager): - self.resize_extension_manager.map(self._resize_extension_point, - resize_dict, resize_kwargs) + kwargs = {} + helpers.translate_attributes(helpers.RESIZE, resize_dict, kwargs) - self._resize(req, id, flavor_ref, **resize_kwargs) + self._resize(req, id, flavor_ref, **kwargs) @wsgi.response(202) - @extensions.expected_errors((400, 403, 404, 409, 413)) + @extensions.expected_errors((400, 403, 404, 409)) @wsgi.action('rebuild') @validation.schema(schema_server_rebuild_v20, '2.0', '2.0') @validation.schema(schema_server_rebuild, '2.1', '2.18') @@ -1003,33 +886,31 @@ def _action_rebuild(self, req, id, body): rebuild_dict = body['rebuild'] image_href = rebuild_dict["imageRef"] - image_href = self._image_uuid_from_href(image_href) password = self._get_server_admin_password(rebuild_dict) context = req.environ['nova.context'] - authorize(context, action='rebuild') instance = self._get_server(context, req, id) - + context.can(server_policies.SERVERS % 'rebuild', + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) attr_map = { 'name': 'display_name', 'description': 'display_description', 'metadata': 'metadata', } - rebuild_kwargs = {} + kwargs = {} - if list(self.rebuild_extension_manager): - self.rebuild_extension_manager.map(self._rebuild_extension_point, - rebuild_dict, rebuild_kwargs) + helpers.translate_attributes(helpers.REBUILD, rebuild_dict, kwargs) for request_attribute, instance_attribute in attr_map.items(): try: if request_attribute == 'name': - rebuild_kwargs[instance_attribute] = common.normalize_name( + kwargs[instance_attribute] = common.normalize_name( rebuild_dict[request_attribute]) else: - rebuild_kwargs[instance_attribute] = rebuild_dict[ + kwargs[instance_attribute] = rebuild_dict[ request_attribute] except (KeyError, TypeError): pass @@ -1039,7 +920,7 @@ def _action_rebuild(self, req, id, body): instance, image_href, password, - **rebuild_kwargs) + **kwargs) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: @@ -1068,7 +949,7 @@ def _action_rebuild(self, req, id, body): # Add on the admin_password attribute since the view doesn't do it # unless instance passwords are disabled - if CONF.enable_instance_password: + if CONF.api.enable_instance_password: view['server']['adminPass'] = password robj = wsgi.ResponseObject(view) @@ -1083,13 +964,17 @@ def _action_rebuild(self, req, id, body): def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ['nova.context'] - authorize(context, action='create_image') + context.can(server_policies.SERVERS % 'create_image') entity = body["createImage"] image_name = common.normalize_name(entity["name"]) metadata = entity.get('metadata', {}) - common.check_img_metadata_properties_quota(context, metadata) + # Starting from microversion 2.39 we don't check quotas on createImage + if api_version_request.is_supported( + req, max_version= + api_version_request.MAX_IMAGE_META_PROXY_API_VERSION): + common.check_img_metadata_properties_quota(context, metadata) instance = self._get_server(context, req, id) @@ -1097,9 +982,10 @@ def _action_create_image(self, req, id, body): context, instance.uuid) try: - if self.compute_api.is_volume_backed_instance(context, instance, + if compute_utils.is_volume_backed_instance(context, instance, bdms): - authorize(context, action="create_image:allow_volume_backed") + context.can(server_policies.SERVERS % + 'create_image:allow_volume_backed') image = self.compute_api.snapshot_volume_backed( context, instance, @@ -1118,6 +1004,13 @@ def _action_create_image(self, req, id, body): 'createImage', id) except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) + except exception.OverQuota as e: + raise exc.HTTPForbidden(explanation=e.format_message()) + + # Starting with microversion 2.45 we return a response body containing + # the snapshot image id without the Location header. + if api_version_request.is_supported(req, '2.45'): + return {'image_id': image['id']} # build location of newly-created image entity image_id = str(image['id']) @@ -1129,9 +1022,9 @@ def _action_create_image(self, req, id, body): def _get_server_admin_password(self, server): """Determine the admin password for a server on creation.""" - try: + if 'adminPass' in server: password = server['adminPass'] - except KeyError: + else: password = utils.generate_password() return password @@ -1141,14 +1034,23 @@ def _get_server_search_options(self, req): 'ip', 'changes-since', 'all_tenants') if api_version_request.is_supported(req, min_version='2.5'): opt_list += ('ip6',) + if api_version_request.is_supported(req, min_version='2.26'): + opt_list += TAG_SEARCH_FILTERS return opt_list def _get_instance(self, context, instance_uuid): try: attrs = ['system_metadata', 'metadata'] - return objects.Instance.get_by_uuid(context, instance_uuid, - expected_attrs=attrs) - except exception.InstanceNotFound as e: + if not CONF.cells.enable: + # NOTE(danms): We can't target a cell database if we're + # in cellsv1 otherwise we'll short-circuit the replication. + mapping = objects.InstanceMapping.get_by_instance_uuid( + context, instance_uuid) + nova_context.set_target_cell(context, mapping.cell_mapping) + return objects.Instance.get_by_uuid( + context, instance_uuid, expected_attrs=attrs) + except (exception.InstanceNotFound, + exception.InstanceMappingNotFound) as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) @wsgi.response(202) @@ -1158,8 +1060,7 @@ def _start_server(self, req, id, body): """Start an instance.""" context = req.environ['nova.context'] instance = self._get_instance(context, id) - authorize(context, instance, 'start') - LOG.debug('start instance', instance=instance) + context.can(server_policies.SERVERS % 'start', instance) try: self.compute_api.start(context, instance) except (exception.InstanceNotReady, exception.InstanceIsLocked) as e: @@ -1177,8 +1078,9 @@ def _stop_server(self, req, id, body): """Stop an instance.""" context = req.environ['nova.context'] instance = self._get_instance(context, id) - authorize(context, instance, 'stop') - LOG.debug('stop instance', instance=instance) + context.can(server_policies.SERVERS % 'stop', + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) try: self.compute_api.stop(context, instance) except (exception.InstanceNotReady, exception.InstanceIsLocked) as e: @@ -1198,14 +1100,16 @@ def _action_trigger_crash_dump(self, req, id, body): """Trigger crash dump in an instance""" context = req.environ['nova.context'] instance = self._get_instance(context, id) - authorize(context, instance, 'trigger_crash_dump') + context.can(server_policies.SERVERS % 'trigger_crash_dump', + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) try: self.compute_api.trigger_crash_dump(context, instance) + except (exception.InstanceNotReady, exception.InstanceIsLocked) as e: + raise webob.exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'trigger_crash_dump', id) - except (exception.InstanceNotReady, exception.InstanceIsLocked) as e: - raise webob.exc.HTTPConflict(explanation=e.format_message()) except exception.TriggerCrashDumpNotSupported as e: raise webob.exc.HTTPBadRequest(explanation=e.format_message()) @@ -1227,24 +1131,24 @@ def remove_invalid_options(context, search_options, allowed_search_options): search_options.pop(opt, None) -class Servers(extensions.V21APIExtensionBase): - """Servers.""" - - name = "Servers" - alias = ALIAS - version = 1 - - def get_resources(self): - member_actions = {'action': 'POST'} - collection_actions = {'detail': 'GET'} - resources = [ - extensions.ResourceExtension( - ALIAS, - ServersController(extension_info=self.extension_info), - member_name='server', collection_actions=collection_actions, - member_actions=member_actions)] - - return resources - - def get_controller_extensions(self): - return [] +def remove_invalid_sort_keys(context, sort_keys, sort_dirs, + blacklist, admin_only_fields): + key_list = copy.deepcopy(sort_keys) + for key in key_list: + # NOTE(Kevin Zheng): We are intend to remove the sort_key + # in the blacklist and its' corresponding sort_dir, since + # the sort_key and sort_dir are not strict to be provide + # in pairs in the current implement, sort_dirs could be + # less than sort_keys, in order to avoid IndexError, we + # only pop sort_dir when number of sort_dirs is no less + # than the sort_key index. + if key in blacklist: + if len(sort_dirs) > sort_keys.index(key): + sort_dirs.pop(sort_keys.index(key)) + sort_keys.pop(sort_keys.index(key)) + elif key in admin_only_fields and not context.is_admin: + msg = _("Only administrators can sort servers " + "by %s") % key + raise exc.HTTPForbidden(explanation=msg) + + return sort_keys, sort_dirs diff --git a/nova/api/openstack/compute/services.py b/nova/api/openstack/compute/services.py index 0c21442dd1c..c911ced1f3d 100644 --- a/nova/api/openstack/compute/services.py +++ b/nova/api/openstack/compute/services.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_utils import strutils +from oslo_utils import uuidutils import webob.exc from nova.api.openstack import api_version_request @@ -19,34 +21,40 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api import validation +from nova import availability_zones from nova import compute from nova import exception from nova.i18n import _ +from nova.policies import services as services_policies from nova import servicegroup from nova import utils -ALIAS = "os-services" -authorize = extensions.os_compute_authorizer(ALIAS) +UUID_FOR_ID_MIN_VERSION = '2.53' class ServiceController(wsgi.Controller): def __init__(self): self.host_api = compute.HostAPI() + self.aggregate_api = compute.api.AggregateAPI() self.servicegroup_api = servicegroup.API() self.actions = {"enable": self._enable, "disable": self._disable, "disable-log-reason": self._disable_log_reason} def _get_services(self, req): + # The API services are filtered out since they are not RPC services + # and therefore their state is not reported through the service group + # API, so they would always be reported as 'down' (see bug 1543625). api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata') context = req.environ['nova.context'] - authorize(context) + context.can(services_policies.BASE_POLICY_NAME) _services = [ s - for s in self.host_api.service_get_all(context, set_zones=True) + for s in self.host_api.service_get_all(context, set_zones=True, + all_cells=True) if s['binary'] not in api_services ] @@ -63,19 +71,34 @@ def _get_services(self, req): return _services - def _get_service_detail(self, svc, additional_fields): + def _get_service_detail(self, svc, additional_fields, req): alive = self.servicegroup_api.service_is_up(svc) state = (alive and "up") or "down" active = 'enabled' if svc['disabled']: active = 'disabled' + updated_time = self.servicegroup_api.get_updated_time(svc) + + uuid_for_id = api_version_request.is_supported( + req, min_version=UUID_FOR_ID_MIN_VERSION) + + if 'availability_zone' not in svc: + # The service wasn't loaded with the AZ so we need to do it here. + # Yes this looks weird, but set_availability_zones makes a copy of + # the list passed in and mutates the objects within it, so we have + # to pull it back out from the resulting copied list. + svc.availability_zone = ( + availability_zones.set_availability_zones( + req.environ['nova.context'], + [svc])[0]['availability_zone']) + service_detail = {'binary': svc['binary'], 'host': svc['host'], - 'id': svc['id'], + 'id': svc['uuid' if uuid_for_id else 'id'], 'zone': svc['availability_zone'], 'status': active, 'state': state, - 'updated_at': svc['updated_at'], + 'updated_at': updated_time, 'disabled_reason': svc['disabled_reason']} for field in additional_fields: @@ -85,7 +108,7 @@ def _get_service_detail(self, svc, additional_fields): def _get_services_list(self, req, additional_fields=()): _services = self._get_services(req) - return [self._get_service_detail(svc, additional_fields) + return [self._get_service_detail(svc, additional_fields, req) for svc in _services] def _enable(self, body, context): @@ -131,7 +154,7 @@ def _enable_disable(self, body, context, status, params_to_update): def _forced_down(self, body, context): """Set or unset forced_down flag for the service""" try: - forced_down = body["forced_down"] + forced_down = strutils.bool_from_string(body["forced_down"]) except KeyError: msg = _('Missing forced_down field') raise webob.exc.HTTPBadRequest(explanation=msg) @@ -149,13 +172,14 @@ def _update(self, context, host, binary, payload): """Do the actual PUT/update""" try: self.host_api.service_update(context, host, binary, payload) - except exception.HostBinaryNotFound as exc: + except (exception.HostBinaryNotFound, + exception.HostMappingNotFound) as exc: raise webob.exc.HTTPNotFound(explanation=exc.format_message()) def _perform_action(self, req, id, body, actions): """Calculate action dictionary dependent on provided fields""" context = req.environ['nova.context'] - authorize(context) + context.can(services_policies.BASE_POLICY_NAME) try: action = actions[id] @@ -170,18 +194,38 @@ def _perform_action(self, req, id, body, actions): def delete(self, req, id): """Deletes the specified service.""" context = req.environ['nova.context'] - authorize(context) + context.can(services_policies.BASE_POLICY_NAME) - try: - utils.validate_integer(id, 'id') - except exception.InvalidInput as exc: - raise webob.exc.HTTPBadRequest(explanation=exc.format_message()) + if api_version_request.is_supported( + req, min_version=UUID_FOR_ID_MIN_VERSION): + if not uuidutils.is_uuid_like(id): + msg = _('Invalid uuid %s') % id + raise webob.exc.HTTPBadRequest(explanation=msg) + else: + try: + utils.validate_integer(id, 'id') + except exception.InvalidInput as exc: + raise webob.exc.HTTPBadRequest( + explanation=exc.format_message()) try: + service = self.host_api.service_get_by_id(context, id) + # remove the service from all the aggregates in which it's included + if service.binary == 'nova-compute': + aggrs = self.aggregate_api.get_aggregates_by_host(context, + service.host) + for ag in aggrs: + self.aggregate_api.remove_host_from_aggregate(context, + ag.id, + service.host) self.host_api.service_delete(context, id) + except exception.ServiceNotFound: explanation = _("Service %s not found.") % id raise webob.exc.HTTPNotFound(explanation=explanation) + except exception.ServiceNotUnique: + explanation = _("Service id %s refers to multiple services.") % id + raise webob.exc.HTTPBadRequest(explanation=explanation) @extensions.expected_errors(()) def index(self, req): @@ -195,11 +239,18 @@ def index(self, req): return {'services': _services} + @wsgi.Controller.api_version('2.1', '2.52') @extensions.expected_errors((400, 404)) @validation.schema(services.service_update, '2.0', '2.10') - @validation.schema(services.service_update_v211, '2.11') + @validation.schema(services.service_update_v211, '2.11', '2.52') def update(self, req, id, body): - """Perform service update""" + """Perform service update + + Before microversion 2.53, the body contains a host and binary value + to identify the service on which to perform the action. There is no + service ID passed on the path, just the action, for example + PUT /os-services/disable. + """ if api_version_request.is_supported(req, min_version='2.11'): actions = self.actions.copy() actions["force-down"] = self._forced_down @@ -208,18 +259,88 @@ def update(self, req, id, body): return self._perform_action(req, id, body, actions) + @wsgi.Controller.api_version(UUID_FOR_ID_MIN_VERSION) # noqa F811 + @extensions.expected_errors((400, 404)) + @validation.schema(services.service_update_v2_53, UUID_FOR_ID_MIN_VERSION) + def update(self, req, id, body): + """Perform service update + + Starting with microversion 2.53, the service uuid is passed in on the + path of the request to uniquely identify the service record on which to + perform a given update, which is defined in the body of the request. + """ + service_id = id + # Validate that the service ID is a UUID. + if not uuidutils.is_uuid_like(service_id): + msg = _('Invalid uuid %s') % service_id + raise webob.exc.HTTPBadRequest(explanation=msg) -class Services(extensions.V21APIExtensionBase): - """Services support.""" + # Validate the request context against the policy. + context = req.environ['nova.context'] + context.can(services_policies.BASE_POLICY_NAME) - name = "Services" - alias = ALIAS - version = 1 + # Get the service by uuid. + try: + service = self.host_api.service_get_by_id(context, service_id) + # At this point the context is targeted to the cell that the + # service was found in so we don't need to do any explicit cell + # targeting below. + except exception.ServiceNotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + # Return 400 if service.binary is not nova-compute. + # Before the earlier PUT handlers were made cells-aware, you could + # technically disable a nova-scheduler service, although that doesn't + # really do anything within Nova and is just confusing. Now trying to + # do that will fail as a nova-scheduler service won't have a host + # mapping so you'll get a 404. In this new microversion, we close that + # old gap and make sure you can only enable/disable and set forced_down + # on nova-compute services since those are the only ones that make + # sense to update for those operations. + if service.binary != 'nova-compute': + msg = (_('Updating a %(binary)s service is not supported. Only ' + 'nova-compute services can be updated.') % + {'binary': service.binary}) + raise webob.exc.HTTPBadRequest(explanation=msg) + + # Now determine the update to perform based on the body. We are + # intentionally not using _perform_action or the other old-style + # action functions. + if 'status' in body: + # This is a status update for either enabled or disabled. + if body['status'] == 'enabled': + + # Fail if 'disabled_reason' was requested when enabling the + # service since those two combined don't make sense. + if body.get('disabled_reason'): + msg = _("Specifying 'disabled_reason' with status " + "'enabled' is invalid.") + raise webob.exc.HTTPBadRequest(explanation=msg) + + service.disabled = False + service.disabled_reason = None + elif body['status'] == 'disabled': + service.disabled = True + # The disabled reason is optional. + service.disabled_reason = body.get('disabled_reason') + + # This is intentionally not an elif, i.e. it's in addition to the + # status update. + if 'forced_down' in body: + service.forced_down = strutils.bool_from_string( + body['forced_down'], strict=True) + + # Check to see if anything was actually updated since the schema does + # not define any required fields. + if not service.obj_what_changed(): + msg = _("No updates were requested. Fields 'status' or " + "'forced_down' should be specified.") + raise webob.exc.HTTPBadRequest(explanation=msg) - def get_resources(self): - resources = [extensions.ResourceExtension(ALIAS, - ServiceController())] - return resources + # Now save our updates to the service record in the database. + service.save() - def get_controller_extensions(self): - return [] + # Return the full service record details. + additional_fields = ['forced_down'] + return {'service': self._get_service_detail( + service, additional_fields, req)} diff --git a/nova/api/openstack/compute/shelve.py b/nova/api/openstack/compute/shelve.py index 6f9f8ae2d5d..bc9f0387187 100644 --- a/nova/api/openstack/compute/shelve.py +++ b/nova/api/openstack/compute/shelve.py @@ -21,16 +21,13 @@ from nova.api.openstack import wsgi from nova import compute from nova import exception - - -ALIAS = 'os-shelve' -authorize = exts.os_compute_authorizer(ALIAS) +from nova.policies import shelve as shelve_policies class ShelveController(wsgi.Controller): def __init__(self, *args, **kwargs): super(ShelveController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() @wsgi.response(202) @exts.expected_errors((404, 409)) @@ -38,9 +35,11 @@ def __init__(self, *args, **kwargs): def _shelve(self, req, id, body): """Move an instance into shelved mode.""" context = req.environ["nova.context"] - authorize(context, action='shelve') instance = common.get_instance(self.compute_api, context, id) + context.can(shelve_policies.POLICY_ROOT % 'shelve', + target={'user_id': instance.user_id, + 'project_id': instance.project_id}) try: self.compute_api.shelve(context, instance) except exception.InstanceUnknownCell as e: @@ -57,7 +56,7 @@ def _shelve(self, req, id, body): def _shelve_offload(self, req, id, body): """Force removal of a shelved instance from the compute node.""" context = req.environ["nova.context"] - authorize(context, action='shelve_offload') + context.can(shelve_policies.POLICY_ROOT % 'shelve_offload') instance = common.get_instance(self.compute_api, context, id) try: @@ -77,7 +76,7 @@ def _shelve_offload(self, req, id, body): def _unshelve(self, req, id, body): """Restore an instance from shelved mode.""" context = req.environ["nova.context"] - authorize(context, action='unshelve') + context.can(shelve_policies.POLICY_ROOT % 'unshelve') instance = common.get_instance(self.compute_api, context, id) try: self.compute_api.unshelve(context, instance) @@ -89,19 +88,3 @@ def _unshelve(self, req, id, body): common.raise_http_conflict_for_instance_invalid_state(state_error, 'unshelve', id) - - -class Shelve(exts.V21APIExtensionBase): - """Instance shelve mode.""" - - name = "Shelve" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = ShelveController() - extension = exts.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/simple_tenant_usage.py b/nova/api/openstack/compute/simple_tenant_usage.py index 62f45cddcd7..09dd3735d99 100644 --- a/nova/api/openstack/compute/simple_tenant_usage.py +++ b/nova/api/openstack/compute/simple_tenant_usage.py @@ -21,14 +21,18 @@ import six.moves.urllib.parse as urlparse from webob import exc +from nova.api.openstack import common +from nova.api.openstack.compute.views import usages as usages_view from nova.api.openstack import extensions from nova.api.openstack import wsgi +import nova.conf +from nova import context as nova_context from nova import exception from nova.i18n import _ from nova import objects +from nova.policies import simple_tenant_usage as stu_policies -ALIAS = "os-simple-tenant-usage" -authorize = extensions.os_compute_authorizer(ALIAS) +CONF = nova.conf.CONF def parse_strtime(dstr, fmt): @@ -39,6 +43,9 @@ def parse_strtime(dstr, fmt): class SimpleTenantUsageController(wsgi.Controller): + + _view_builder_class = usages_view.ViewBuilder + def _hours_for(self, instance, period_start, period_stop): launched_at = instance.launched_at terminated_at = instance.terminated_at @@ -97,14 +104,46 @@ def _get_flavor(self, context, instance, flavors_cache): return flavor_ref - def _tenant_usages_for_period(self, context, period_start, - period_stop, tenant_id=None, detailed=True): - - instances = objects.InstanceList.get_active_by_window_joined( - context, period_start, period_stop, tenant_id, - expected_attrs=['flavor']) + def _get_instances_all_cells(self, context, period_start, period_stop, + tenant_id, limit, marker): + all_instances = [] + cells = objects.CellMappingList.get_all(context) + for cell in cells: + with nova_context.target_cell(context, cell) as cctxt: + try: + instances = ( + objects.InstanceList.get_active_by_window_joined( + cctxt, period_start, period_stop, tenant_id, + expected_attrs=['flavor'], limit=limit, + marker=marker)) + except exception.MarkerNotFound: + # NOTE(danms): We need to keep looking through the later + # cells to find the marker + continue + all_instances.extend(instances) + # NOTE(danms): We must have found a marker if we had one, + # so make sure we don't require a marker in the next cell + marker = None + if limit: + limit -= len(instances) + if limit <= 0: + break + if marker is not None and len(all_instances) == 0: + # NOTE(danms): If we did not find the marker in any cell, + # mimic the db_api behavior here + raise exception.MarkerNotFound(marker=marker) + + return all_instances + + def _tenant_usages_for_period(self, context, period_start, period_stop, + tenant_id=None, detailed=True, limit=None, + marker=None): + instances = self._get_instances_all_cells(context, period_start, + period_stop, tenant_id, + limit, marker) rval = {} flavors = {} + all_server_usages = [] for instance in instances: info = {} @@ -119,13 +158,18 @@ def _tenant_usages_for_period(self, context, period_start, info['instance_id'] = instance.uuid info['name'] = instance.display_name - - info['memory_mb'] = instance.memory_mb - info['local_gb'] = instance.root_gb + instance.ephemeral_gb - info['vcpus'] = instance.vcpus - info['tenant_id'] = instance.project_id + try: + info['memory_mb'] = instance.flavor.memory_mb + info['local_gb'] = (instance.flavor.root_gb + + instance.flavor.ephemeral_gb) + info['vcpus'] = instance.flavor.vcpus + except exception.InstanceNotFound: + # This is rare case, instance disappear during analysis + # As it's just info collection, we can try next one + continue + # NOTE(mriedem): We need to normalize the start/end times back # to timezone-naive so the response doesn't change after the # conversion to objects. @@ -169,10 +213,11 @@ def _tenant_usages_for_period(self, context, period_start, info['hours']) summary['total_hours'] += info['hours'] + all_server_usages.append(info) if detailed: summary['server_usages'].append(info) - return rval.values() + return list(rval.values()), all_server_usages def _parse_datetime(self, dtstr): if not dtstr: @@ -215,12 +260,34 @@ def _get_datetime_range(self, req): detailed = env.get('detailed', ['0'])[0] == '1' return (period_start, period_stop, detailed) + @wsgi.Controller.api_version("2.40") + @extensions.expected_errors(400) + def index(self, req): + """Retrieve tenant_usage for all tenants.""" + return self._index(req, links=True) + + @wsgi.Controller.api_version("2.1", "2.39") # noqa @extensions.expected_errors(400) def index(self, req): """Retrieve tenant_usage for all tenants.""" + return self._index(req) + + @wsgi.Controller.api_version("2.40") + @extensions.expected_errors(400) + def show(self, req, id): + """Retrieve tenant_usage for a specified tenant.""" + return self._show(req, id, links=True) + + @wsgi.Controller.api_version("2.1", "2.39") # noqa + @extensions.expected_errors(400) + def show(self, req, id): + """Retrieve tenant_usage for a specified tenant.""" + return self._show(req, id) + + def _index(self, req, links=False): context = req.environ['nova.context'] - authorize(context, action='list') + context.can(stu_policies.POLICY_ROOT % 'list') try: (period_start, period_stop, detailed) = self._get_datetime_range( @@ -231,19 +298,34 @@ def index(self, req): now = timeutils.parse_isotime(timeutils.utcnow().isoformat()) if period_stop > now: period_stop = now - usages = self._tenant_usages_for_period(context, - period_start, - period_stop, - detailed=detailed) - return {'tenant_usages': usages} - @extensions.expected_errors(400) - def show(self, req, id): - """Retrieve tenant_usage for a specified tenant.""" + marker = None + limit = CONF.api.max_limit + if links: + limit, marker = common.get_limit_and_marker(req) + + try: + usages, server_usages = self._tenant_usages_for_period( + context, period_start, period_stop, detailed=detailed, + limit=limit, marker=marker) + except exception.MarkerNotFound as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) + + tenant_usages = {'tenant_usages': usages} + + if links: + usages_links = self._view_builder.get_links(req, server_usages) + if usages_links: + tenant_usages['tenant_usages_links'] = usages_links + + return tenant_usages + + def _show(self, req, id, links=False): tenant_id = id context = req.environ['nova.context'] - authorize(context, action='show', target={'project_id': tenant_id}) + context.can(stu_policies.POLICY_ROOT % 'show', + {'project_id': tenant_id}) try: (period_start, period_stop, ignore) = self._get_datetime_range( @@ -254,33 +336,30 @@ def show(self, req, id): now = timeutils.parse_isotime(timeutils.utcnow().isoformat()) if period_stop > now: period_stop = now - usage = self._tenant_usages_for_period(context, - period_start, - period_stop, - tenant_id=tenant_id, - detailed=True) + + marker = None + limit = CONF.api.max_limit + if links: + limit, marker = common.get_limit_and_marker(req) + + try: + usage, server_usages = self._tenant_usages_for_period( + context, period_start, period_stop, tenant_id=tenant_id, + detailed=True, limit=limit, marker=marker) + except exception.MarkerNotFound as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) + if len(usage): usage = list(usage)[0] else: usage = {} - return {'tenant_usage': usage} - - -class SimpleTenantUsage(extensions.V21APIExtensionBase): - """Simple tenant usage extension.""" - - name = "SimpleTenantUsage" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [] - res = extensions.ResourceExtension(ALIAS, - SimpleTenantUsageController()) - resources.append(res) + tenant_usage = {'tenant_usage': usage} - return resources + if links: + usages_links = self._view_builder.get_links( + req, server_usages, tenant_id=tenant_id) + if usages_links: + tenant_usage['tenant_usage_links'] = usages_links - def get_controller_extensions(self): - return [] + return tenant_usage diff --git a/nova/api/openstack/compute/suspend_server.py b/nova/api/openstack/compute/suspend_server.py index b01aaceaca2..cf0b9455275 100644 --- a/nova/api/openstack/compute/suspend_server.py +++ b/nova/api/openstack/compute/suspend_server.py @@ -19,17 +19,13 @@ from nova.api.openstack import wsgi from nova import compute from nova import exception - -ALIAS = "os-suspend-server" - - -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import suspend_server as ss_policies class SuspendServerController(wsgi.Controller): def __init__(self, *args, **kwargs): super(SuspendServerController, self).__init__(*args, **kwargs) - self.compute_api = compute.API(skip_policy_check=True) + self.compute_api = compute.API() @wsgi.response(202) @extensions.expected_errors((404, 409)) @@ -37,9 +33,11 @@ def __init__(self, *args, **kwargs): def _suspend(self, req, id, body): """Permit admins to suspend the server.""" context = req.environ['nova.context'] - authorize(context, action='suspend') + server = common.get_instance(self.compute_api, context, id) try: - server = common.get_instance(self.compute_api, context, id) + context.can(ss_policies.POLICY_ROOT % 'suspend', + target={'user_id': server.user_id, + 'project_id': server.project_id}) self.compute_api.suspend(context, server) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) @@ -55,9 +53,9 @@ def _suspend(self, req, id, body): def _resume(self, req, id, body): """Permit admins to resume the server from suspend.""" context = req.environ['nova.context'] - authorize(context, action='resume') + context.can(ss_policies.POLICY_ROOT % 'resume') + server = common.get_instance(self.compute_api, context, id) try: - server = common.get_instance(self.compute_api, context, id) self.compute_api.resume(context, server) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) @@ -66,19 +64,3 @@ def _resume(self, req, id, body): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'resume', id) - - -class SuspendServer(extensions.V21APIExtensionBase): - """Enable suspend/resume server actions.""" - - name = "SuspendServer" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = SuspendServerController() - extension = extensions.ControllerExtension(self, 'servers', controller) - return [extension] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/tenant_networks.py b/nova/api/openstack/compute/tenant_networks.py index c1f70f7402f..b1620adafb8 100644 --- a/nova/api/openstack/compute/tenant_networks.py +++ b/nova/api/openstack/compute/tenant_networks.py @@ -16,39 +16,30 @@ import netaddr import netaddr.core as netexc -from oslo_config import cfg from oslo_log import log as logging import six from webob import exc +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack.compute.schemas import tenant_networks as schema from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api import validation +import nova.conf from nova import context as nova_context from nova import exception from nova.i18n import _ -from nova.i18n import _LE import nova.network +from nova import objects +from nova.policies import tenant_networks as tn_policies from nova import quota -CONF = cfg.CONF -CONF.import_opt('enable_network_quota', 'nova.api.openstack.compute.' - 'legacy_v2.contrib.os_tenant_networks') -CONF.import_opt('use_neutron_default_nets', 'nova.api.openstack.compute.' - 'legacy_v2.contrib.os_tenant_networks') -CONF.import_opt('neutron_default_tenant_id', 'nova.api.openstack.compute.' - 'legacy_v2.contrib.os_tenant_networks') -CONF.import_opt('quota_networks', 'nova.api.openstack.compute.' - 'legacy_v2.contrib.os_tenant_networks') - - -ALIAS = 'os-tenant-networks' +CONF = nova.conf.CONF QUOTAS = quota.QUOTAS LOG = logging.getLogger(__name__) -authorize = extensions.os_compute_authorizer(ALIAS) def network_dict(network): @@ -62,40 +53,42 @@ def network_dict(network): class TenantNetworkController(wsgi.Controller): def __init__(self, network_api=None): - self.network_api = nova.network.API(skip_policy_check=True) + self.network_api = nova.network.API() self._default_networks = [] def _refresh_default_networks(self): self._default_networks = [] - if CONF.use_neutron_default_nets == "True": + if CONF.api.use_neutron_default_nets: try: self._default_networks = self._get_default_networks() except Exception: - LOG.exception(_LE("Failed to get default networks")) + LOG.exception("Failed to get default networks") def _get_default_networks(self): - project_id = CONF.neutron_default_tenant_id + project_id = CONF.api.neutron_default_tenant_id ctx = nova_context.RequestContext(user_id=None, project_id=project_id) networks = {} for n in self.network_api.get_all(ctx): networks[n['id']] = n['label'] - return [{'id': k, 'label': v} for k, v in six.iteritems(networks)] + return [{'id': k, 'label': v} for k, v in networks.items()] + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(()) def index(self, req): context = req.environ['nova.context'] - authorize(context) + context.can(tn_policies.BASE_POLICY_NAME) networks = list(self.network_api.get_all(context)) if not self._default_networks: self._refresh_default_networks() networks.extend(self._default_networks) return {'networks': [network_dict(n) for n in networks]} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(404) def show(self, req, id): context = req.environ['nova.context'] - authorize(context) + context.can(tn_policies.BASE_POLICY_NAME) try: network = self.network_api.get(context, id) except exception.NetworkNotFound: @@ -103,46 +96,30 @@ def show(self, req, id): raise exc.HTTPNotFound(explanation=msg) return {'network': network_dict(network)} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((403, 404, 409)) @wsgi.response(202) def delete(self, req, id): context = req.environ['nova.context'] - authorize(context) - reservation = None - try: - if CONF.enable_network_quota: - reservation = QUOTAS.reserve(context, networks=-1) - except Exception: - reservation = None - LOG.exception(_LE("Failed to update usages deallocating " - "network.")) - - def _rollback_quota(reservation): - if CONF.enable_network_quota and reservation: - QUOTAS.rollback(context, reservation) + context.can(tn_policies.BASE_POLICY_NAME) try: self.network_api.disassociate(context, id) self.network_api.delete(context, id) except exception.PolicyNotAuthorized as e: - _rollback_quota(reservation) raise exc.HTTPForbidden(explanation=six.text_type(e)) except exception.NetworkInUse as e: - _rollback_quota(reservation) raise exc.HTTPConflict(explanation=e.format_message()) except exception.NetworkNotFound: - _rollback_quota(reservation) msg = _("Network not found") raise exc.HTTPNotFound(explanation=msg) - if CONF.enable_network_quota and reservation: - QUOTAS.commit(context, reservation) - + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 403, 409, 503)) @validation.schema(schema.create) def create(self, req, body): context = req.environ["nova.context"] - authorize(context) + context.can(tn_policies.BASE_POLICY_NAME) network = body["network"] keys = ["cidr", "cidr_v6", "ipam", "vlan_start", "network_size", @@ -162,57 +139,56 @@ def create(self, req, body): msg = _("Address could not be converted.") raise exc.HTTPBadRequest(explanation=msg) - networks = [] try: if CONF.enable_network_quota: - reservation = QUOTAS.reserve(context, networks=1) + objects.Quotas.check_deltas(context, {'networks': 1}, + context.project_id) except exception.OverQuota: msg = _("Quota exceeded, too many networks.") - raise exc.HTTPBadRequest(explanation=msg) + raise exc.HTTPForbidden(explanation=msg) kwargs['project_id'] = context.project_id try: networks = self.network_api.create(context, label=label, **kwargs) - if CONF.enable_network_quota: - QUOTAS.commit(context, reservation) except exception.PolicyNotAuthorized as e: raise exc.HTTPForbidden(explanation=six.text_type(e)) except exception.CidrConflict as e: raise exc.HTTPConflict(explanation=e.format_message()) except Exception: - if CONF.enable_network_quota: - QUOTAS.rollback(context, reservation) msg = _("Create networks failed") LOG.exception(msg, extra=network) raise exc.HTTPServiceUnavailable(explanation=msg) - return {"network": network_dict(networks[0])} - - -class TenantNetworks(extensions.V21APIExtensionBase): - """Tenant-based Network Management Extension.""" - name = "OSTenantNetworks" - alias = ALIAS - version = 1 - - def get_resources(self): - ext = extensions.ResourceExtension(ALIAS, TenantNetworkController()) - return [ext] + # NOTE(melwitt): We recheck the quota after creating the object to + # prevent users from allocating more resources than their allowed quota + # in the event of a race. This is configurable because it can be + # expensive if strict quota limits are not required in a deployment. + if CONF.quota.recheck_quota and CONF.enable_network_quota: + try: + objects.Quotas.check_deltas(context, {'networks': 0}, + context.project_id) + except exception.OverQuota: + self.network_api.delete(context, + network_dict(networks[0])['id']) + msg = _("Quota exceeded, too many networks.") + raise exc.HTTPForbidden(explanation=msg) - def get_controller_extensions(self): - return [] + return {"network": network_dict(networks[0])} -def _sync_networks(context, project_id, session): +def _network_count(context, project_id): + # NOTE(melwitt): This assumes a single cell. ctx = nova_context.RequestContext(user_id=None, project_id=project_id) ctx = ctx.elevated() networks = nova.network.api.API().get_all(ctx) - return dict(networks=len(networks)) + return {'project': {'networks': len(networks)}} -if CONF.enable_network_quota: - QUOTAS.register_resource(quota.ReservableResource('networks', - _sync_networks, - 'quota_networks')) +def _register_network_quota(): + if CONF.enable_network_quota: + QUOTAS.register_resource(quota.CountableResource('networks', + _network_count, + 'quota_networks')) +_register_network_quota() diff --git a/nova/api/openstack/compute/used_limits.py b/nova/api/openstack/compute/used_limits.py index 0be25bc527e..1e373a64c69 100644 --- a/nova/api/openstack/compute/used_limits.py +++ b/nova/api/openstack/compute/used_limits.py @@ -12,49 +12,49 @@ # License for the specific language governing permissions and limitations # under the License. -import six +from nova.api.openstack import api_version_request +from nova.api.openstack.api_version_request \ + import MIN_WITHOUT_PROXY_API_SUPPORT_VERSION from nova.api.openstack import extensions from nova.api.openstack import wsgi +from nova.policies import used_limits as ul_policies from nova import quota QUOTAS = quota.QUOTAS -ALIAS = "os-used-limits" -authorize = extensions.os_compute_authorizer(ALIAS) - - class UsedLimitsController(wsgi.Controller): - @staticmethod - def _reserved(req): - try: - return int(req.GET['reserved']) - except (ValueError, KeyError): - return False - @wsgi.extends @extensions.expected_errors(()) def index(self, req, resp_obj): context = req.environ['nova.context'] project_id = self._project_id(context, req) quotas = QUOTAS.get_project_quotas(context, project_id, usages=True) - quota_map = { - 'totalRAMUsed': 'ram', - 'totalCoresUsed': 'cores', - 'totalInstancesUsed': 'instances', - 'totalFloatingIpsUsed': 'floating_ips', - 'totalSecurityGroupsUsed': 'security_groups', - 'totalServerGroupsUsed': 'server_groups', - } + if api_version_request.is_supported( + req, min_version=MIN_WITHOUT_PROXY_API_SUPPORT_VERSION): + quota_map = { + 'totalRAMUsed': 'ram', + 'totalCoresUsed': 'cores', + 'totalInstancesUsed': 'instances', + 'totalServerGroupsUsed': 'server_groups', + } + else: + quota_map = { + 'totalRAMUsed': 'ram', + 'totalCoresUsed': 'cores', + 'totalInstancesUsed': 'instances', + 'totalFloatingIpsUsed': 'floating_ips', + 'totalSecurityGroupsUsed': 'security_groups', + 'totalServerGroupsUsed': 'server_groups', + } + used_limits = {} - for display_name, key in six.iteritems(quota_map): + for display_name, key in quota_map.items(): if key in quotas: - reserved = (quotas[key]['reserved'] - if self._reserved(req) else 0) - used_limits[display_name] = quotas[key]['in_use'] + reserved + used_limits[display_name] = quotas[key]['in_use'] resp_obj.obj['limits']['absolute'].update(used_limits) @@ -65,23 +65,6 @@ def _project_id(self, context, req): 'project_id': tenant_id, 'user_id': context.user_id } - authorize(context, target=target) + context.can(ul_policies.BASE_POLICY_NAME, target) return tenant_id return context.project_id - - -class UsedLimits(extensions.V21APIExtensionBase): - """Provide data on limited resources that are being used.""" - - name = "UsedLimits" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - controller = UsedLimitsController() - limits_ext = extensions.ControllerExtension(self, 'limits', - controller=controller) - return [limits_ext] - - def get_resources(self): - return [] diff --git a/nova/api/openstack/compute/user_data.py b/nova/api/openstack/compute/user_data.py index fdcb1c0f7a4..f89fb03060f 100644 --- a/nova/api/openstack/compute/user_data.py +++ b/nova/api/openstack/compute/user_data.py @@ -13,30 +13,18 @@ # under the License. from nova.api.openstack.compute.schemas import user_data as schema_user_data -from nova.api.openstack import extensions -ALIAS = "os-user-data" ATTRIBUTE_NAME = 'user_data' -class UserData(extensions.V21APIExtensionBase): - """Add user_data to the Create Server API.""" +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + create_kwargs['user_data'] = server_dict.get(ATTRIBUTE_NAME) - name = "UserData" - alias = ALIAS - version = 1 - def get_controller_extensions(self): - return [] - - def get_resources(self): - return [] - - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - create_kwargs['user_data'] = server_dict.get(ATTRIBUTE_NAME) - - def get_server_create_schema(self, version): - return schema_user_data.server_create +def get_server_create_schema(version): + if version == '2.0': + return schema_user_data.server_create_v20 + return schema_user_data.server_create diff --git a/nova/api/openstack/compute/versions.py b/nova/api/openstack/compute/versions.py index 54daaeba638..4fe770936fb 100644 --- a/nova/api/openstack/compute/versions.py +++ b/nova/api/openstack/compute/versions.py @@ -13,16 +13,11 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg - from nova.api.openstack import api_version_request from nova.api.openstack.compute.views import versions as views_versions from nova.api.openstack import wsgi -CONF = cfg.CONF -CONF.import_opt('enabled', 'nova.api.openstack', group='osapi_v21') - LINKS = { 'v2.0': { 'html': 'http://docs.openstack.org/' @@ -80,8 +75,6 @@ class Versions(wsgi.Resource): def __init__(self): super(Versions, self).__init__(None) - if not CONF.osapi_v21.enabled: - del VERSIONS["v2.1"] def index(self, req, body=None): """Return all versions.""" diff --git a/nova/api/openstack/compute/versionsV21.py b/nova/api/openstack/compute/versionsV21.py index ffa5158e9da..aa7c4295405 100644 --- a/nova/api/openstack/compute/versionsV21.py +++ b/nova/api/openstack/compute/versionsV21.py @@ -21,9 +21,6 @@ from nova.api.openstack import wsgi -ALIAS = "versions" - - class VersionsController(wsgi.Controller): @extensions.expected_errors(404) def show(self, req, id='v2.1'): @@ -34,26 +31,3 @@ def show(self, req, id='v2.1'): raise webob.exc.HTTPNotFound() return builder.build_version(versions.VERSIONS[id]) - - -class Versions(extensions.V21APIExtensionBase): - """API Version information.""" - - name = "Versions" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [ - extensions.ResourceExtension(ALIAS, VersionsController(), - custom_routes_fn=self.version_map)] - return resources - - def get_controller_extensions(self): - return [] - - def version_map(self, mapper, wsgi_resource): - mapper.connect("versions", "/", - controller=wsgi_resource, - action='show', conditions={"method": ['GET']}) - mapper.redirect("", "/") diff --git a/nova/api/openstack/compute/views/addresses.py b/nova/api/openstack/compute/views/addresses.py index e12b661f1cd..1c4986f6dbd 100644 --- a/nova/api/openstack/compute/views/addresses.py +++ b/nova/api/openstack/compute/views/addresses.py @@ -26,10 +26,16 @@ class ViewBuilder(common.ViewBuilder): def basic(self, ip, extend_address=False): """Return a dictionary describing an IP address.""" - return { + address = { "version": ip["version"], "addr": ip["address"], } + if extend_address: + address.update({ + "OS-EXT-IPS:type": ip["type"], + "OS-EXT-IPS-MAC:mac_addr": ip['mac_address'], + }) + return address def show(self, network, label, extend_address=False): """Returns a dictionary describing a network.""" @@ -43,19 +49,3 @@ def index(self, networks, extend_address=False): network_dict = self.show(network, label, extend_address) addresses[label] = network_dict[label] return dict(addresses=addresses) - - -class ViewBuilderV21(ViewBuilder): - """Models server addresses as a dictionary.""" - def basic(self, ip, extend_address=False): - """Return a dictionary describing an IP address.""" - address = { - "version": ip["version"], - "addr": ip["address"], - } - if extend_address: - address.update({ - "OS-EXT-IPS:type": ip["type"], - "OS-EXT-IPS-MAC:mac_addr": ip['mac_address'], - }) - return address diff --git a/nova/api/openstack/compute/views/flavors.py b/nova/api/openstack/compute/views/flavors.py index 2c36fe6a1b5..0295929cf78 100644 --- a/nova/api/openstack/compute/views/flavors.py +++ b/nova/api/openstack/compute/views/flavors.py @@ -38,7 +38,10 @@ def show(self, request, flavor): "name": flavor["name"], "ram": flavor["memory_mb"], "disk": flavor["root_gb"], - "vcpus": flavor.get("vcpus") or "", + "swap": flavor["swap"] or "", + "OS-FLV-EXT-DATA:ephemeral": flavor["ephemeral_gb"], + "OS-FLV-DISABLED:disabled": flavor["disabled"], + "vcpus": flavor["vcpus"], "links": self._get_links(request, flavor["flavorid"], self._collection_name), @@ -79,15 +82,3 @@ def _list_view(self, func, request, flavors, coll_name): flavors_dict["flavors_links"] = flavors_links return flavors_dict - - -class ViewBuilderV21(ViewBuilder): - def show(self, request, flavor): - flavor_dict = super(ViewBuilderV21, self).show(request, flavor) - flavor_dict['flavor'].update({ - "swap": flavor["swap"] or "", - "OS-FLV-EXT-DATA:ephemeral": flavor["ephemeral_gb"], - "OS-FLV-DISABLED:disabled": flavor["disabled"], - "vcpus": flavor["vcpus"], - }) - return flavor_dict diff --git a/nova/api/openstack/compute/views/hypervisors.py b/nova/api/openstack/compute/views/hypervisors.py new file mode 100644 index 00000000000..2284245993f --- /dev/null +++ b/nova/api/openstack/compute/views/hypervisors.py @@ -0,0 +1,26 @@ +# Copyright 2016 Kylin Cloud +# 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. + +from nova.api.openstack import common + + +class ViewBuilder(common.ViewBuilder): + _collection_name = "hypervisors" + + def get_links(self, request, hypervisors, detail=False): + coll_name = (self._collection_name + '/detail' if detail else + self._collection_name) + return self._get_collection_links(request, hypervisors, coll_name, + 'id') diff --git a/nova/api/openstack/compute/views/images.py b/nova/api/openstack/compute/views/images.py index d954c63e7f0..0e73f8d4a86 100644 --- a/nova/api/openstack/compute/views/images.py +++ b/nova/api/openstack/compute/views/images.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_utils import strutils + from nova.api.openstack import common from nova.image import glance from nova import utils @@ -70,6 +72,12 @@ def show(self, request, image): }], } + auto_disk_config = image_dict['metadata'].get("auto_disk_config", None) + if auto_disk_config is not None: + value = strutils.bool_from_string(auto_disk_config) + image_dict["OS-DCF:diskConfig"] = ( + 'AUTO' if value else 'MANUAL') + return dict(image=image_dict) def detail(self, request, images): diff --git a/nova/api/openstack/compute/views/keypairs.py b/nova/api/openstack/compute/views/keypairs.py new file mode 100644 index 00000000000..020c7a0ac86 --- /dev/null +++ b/nova/api/openstack/compute/views/keypairs.py @@ -0,0 +1,25 @@ +# Copyright 2016 Mirantis 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. + +from nova.api.openstack import common + + +class ViewBuilder(common.ViewBuilder): + + _collection_name = "keypairs" + + def get_links(self, request, keypairs): + return self._get_collection_links(request, keypairs, + self._collection_name, 'name') diff --git a/nova/api/openstack/compute/views/limits.py b/nova/api/openstack/compute/views/limits.py index cb3b724bf2f..ae93349ed64 100644 --- a/nova/api/openstack/compute/views/limits.py +++ b/nova/api/openstack/compute/views/limits.py @@ -13,10 +13,11 @@ # License for the specific language governing permissions and limitations # under the License. -import datetime -import six -from nova import utils +# This is a list of limits which needs to filter out from the API response. +# This is due to the deprecation of network related proxy APIs, the related +# limit should be removed from the API also. +FILTERED_LIMITS = ['floating_ips', 'security_groups', 'security_group_rules'] class ViewBuilder(object): @@ -36,77 +37,41 @@ def __init__(self): "injected_file_content_bytes": ["maxPersonalitySize"], "security_groups": ["maxSecurityGroups"], "security_group_rules": ["maxSecurityGroupRules"], + "server_groups": ["maxServerGroups"], + "server_group_members": ["maxServerGroupMembers"] } - def build(self, rate_limits, absolute_limits): - rate_limits = self._build_rate_limits(rate_limits) - absolute_limits = self._build_absolute_limits(absolute_limits) + def build(self, absolute_limits, filter_result=False, max_image_meta=True): + absolute_limits = self._build_absolute_limits( + absolute_limits, filter_result=filter_result, + max_image_meta=max_image_meta) output = { "limits": { - "rate": rate_limits, + "rate": [], "absolute": absolute_limits, }, } return output - def _build_absolute_limits(self, absolute_limits): + def _build_absolute_limits(self, absolute_limits, filter_result=False, + max_image_meta=True): """Builder for absolute limits absolute_limits should be given as a dict of limits. For example: {"ram": 512, "gigabytes": 1024}. """ + filtered_limits = [] + if filter_result: + filtered_limits = FILTERED_LIMITS limits = {} - for name, value in six.iteritems(absolute_limits): - if name in self.limit_names and value is not None: + for name, value in absolute_limits.items(): + if (name in self.limit_names and + value is not None and name not in filtered_limits): for limit_name in self.limit_names[name]: + if not max_image_meta and limit_name == "maxImageMeta": + continue limits[limit_name] = value return limits - - def _build_rate_limits(self, rate_limits): - limits = [] - for rate_limit in rate_limits: - _rate_limit_key = None - _rate_limit = self._build_rate_limit(rate_limit) - - # check for existing key - for limit in limits: - if (limit["uri"] == rate_limit["URI"] and - limit["regex"] == rate_limit["regex"]): - _rate_limit_key = limit - break - - # ensure we have a key if we didn't find one - if not _rate_limit_key: - _rate_limit_key = { - "uri": rate_limit["URI"], - "regex": rate_limit["regex"], - "limit": [], - } - limits.append(_rate_limit_key) - - _rate_limit_key["limit"].append(_rate_limit) - - return limits - - def _build_rate_limit(self, rate_limit): - _get_utc = datetime.datetime.utcfromtimestamp - next_avail = _get_utc(rate_limit["resetTime"]) - return { - "verb": rate_limit["verb"], - "value": rate_limit["value"], - "remaining": int(rate_limit["remaining"]), - "unit": rate_limit["unit"], - "next-available": utils.isotime(next_avail), - } - - -class ViewBuilderV21(ViewBuilder): - - def __init__(self): - super(ViewBuilderV21, self).__init__() - # NOTE In v2.0 these are added by a specific extension - self.limit_names["server_groups"] = ["maxServerGroups"] - self.limit_names["server_group_members"] = ["maxServerGroupMembers"] diff --git a/nova/api/openstack/compute/views/server_diagnostics.py b/nova/api/openstack/compute/views/server_diagnostics.py new file mode 100644 index 00000000000..860ec2b9925 --- /dev/null +++ b/nova/api/openstack/compute/views/server_diagnostics.py @@ -0,0 +1,62 @@ +# 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 nova.api.openstack import common + +INSTANCE_DIAGNOSTICS_PRIMITIVE_FIELDS = ( + 'state', 'driver', 'hypervisor', 'hypervisor_os', 'uptime', 'config_drive', + 'num_cpus', 'num_nics', 'num_disks' +) + +INSTANCE_DIAGNOSTICS_LIST_FIELDS = { + 'disk_details': ('read_bytes', 'read_requests', 'write_bytes', + 'write_requests', 'errors_count'), + 'cpu_details': ('id', 'time', 'utilisation'), + 'nic_details': ('mac_address', 'rx_octets', 'rx_errors', 'rx_drop', + 'rx_packets', 'rx_rate', 'tx_octets', 'tx_errors', + 'tx_drop', 'tx_packets', 'tx_rate') +} + +INSTANCE_DIAGNOSTICS_OBJECT_FIELDS = {'memory_details': ('maximum', 'used')} + + +class ViewBuilder(common.ViewBuilder): + @staticmethod + def _get_obj_field(obj, field): + if obj and obj.obj_attr_is_set(field): + return getattr(obj, field) + return None + + def instance_diagnostics(self, diagnostics): + """Return a dictionary with instance diagnostics.""" + diagnostics_dict = {} + for field in INSTANCE_DIAGNOSTICS_PRIMITIVE_FIELDS: + diagnostics_dict[field] = self._get_obj_field(diagnostics, field) + + for list_field in INSTANCE_DIAGNOSTICS_LIST_FIELDS: + diagnostics_dict[list_field] = [] + list_obj = getattr(diagnostics, list_field) + + for obj in list_obj: + obj_dict = {} + for field in INSTANCE_DIAGNOSTICS_LIST_FIELDS[list_field]: + obj_dict[field] = self._get_obj_field(obj, field) + diagnostics_dict[list_field].append(obj_dict) + + for obj_field in INSTANCE_DIAGNOSTICS_OBJECT_FIELDS: + diagnostics_dict[obj_field] = {} + obj = self._get_obj_field(diagnostics, obj_field) + for field in INSTANCE_DIAGNOSTICS_OBJECT_FIELDS[obj_field]: + diagnostics_dict[obj_field][field] = self._get_obj_field( + obj, field) + + return diagnostics_dict diff --git a/nova/api/openstack/compute/views/server_tags.py b/nova/api/openstack/compute/views/server_tags.py new file mode 100644 index 00000000000..27a53f9af23 --- /dev/null +++ b/nova/api/openstack/compute/views/server_tags.py @@ -0,0 +1,30 @@ +# Copyright 2016 Mirantis 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. + +from nova.api.openstack import common +from nova.api.openstack.compute.views import servers + + +class ViewBuilder(common.ViewBuilder): + _collection_name = "tags" + + def __init__(self): + super(ViewBuilder, self).__init__() + self._server_builder = servers.ViewBuilder() + + def get_location(self, request, server_id, tag_name): + server_location = self._server_builder._get_href_link( + request, server_id, "servers") + return "%s/%s/%s" % (server_location, self._collection_name, tag_name) diff --git a/nova/api/openstack/compute/views/servers.py b/nova/api/openstack/compute/views/servers.py index 14947f51734..703e0e44b6f 100644 --- a/nova/api/openstack/compute/views/servers.py +++ b/nova/api/openstack/compute/views/servers.py @@ -23,8 +23,11 @@ from nova.api.openstack.compute.views import addresses as views_addresses from nova.api.openstack.compute.views import flavors as views_flavors from nova.api.openstack.compute.views import images as views_images -from nova.i18n import _LW +from nova import context as nova_context +from nova import exception +from nova import objects from nova.objects import base as obj_base +from nova.policies import flavor_extra_specs as fes_policies from nova import utils @@ -58,8 +61,8 @@ def __init__(self): """Initialize view builder.""" super(ViewBuilder, self).__init__() self._address_builder = views_addresses.ViewBuilder() - self._flavor_builder = views_flavors.ViewBuilder() self._image_builder = views_images.ViewBuilder() + self._flavor_builder = views_flavors.ViewBuilder() def create(self, request, instance): """View that should be returned when an instance is created.""" @@ -69,10 +72,15 @@ def create(self, request, instance): "links": self._get_links(request, instance["uuid"], self._collection_name), + # NOTE(sdague): historically this was the + # os-disk-config extension, but now that extensions + # are gone, we merge these attributes here. + "OS-DCF:diskConfig": ( + 'AUTO' if instance.get('auto_disk_config') else 'MANUAL'), }, } - def basic(self, request, instance): + def basic(self, request, instance, show_extra_specs=False): """Generic, non-detailed view of an instance.""" return { "server": { @@ -104,10 +112,21 @@ def get_show_expected_attrs(self, expected_attrs=None): # results. return sorted(list(set(self._show_expected_attrs + expected_attrs))) - def show(self, request, instance): + def show(self, request, instance, extend_address=True, + show_extra_specs=None): """Detailed view of a single instance.""" ip_v4 = instance.get('access_ip_v4') ip_v6 = instance.get('access_ip_v6') + + if show_extra_specs is None: + # detail will pre-calculate this for us. If we're doing show, + # then figure it out here. + show_extra_specs = False + if api_version_request.is_supported(request, min_version='2.47'): + context = request.environ['nova.context'] + show_extra_specs = context.can( + fes_policies.POLICY_ROOT % 'index', fatal=False) + server = { "server": { "id": instance["uuid"], @@ -118,15 +137,22 @@ def show(self, request, instance): "metadata": self._get_metadata(instance), "hostId": self._get_host_id(instance) or "", "image": self._get_image(request, instance), - "flavor": self._get_flavor(request, instance), + "flavor": self._get_flavor(request, instance, + show_extra_specs), "created": utils.isotime(instance["created_at"]), "updated": utils.isotime(instance["updated_at"]), - "addresses": self._get_addresses(request, instance), + "addresses": self._get_addresses(request, instance, + extend_address), "accessIPv4": str(ip_v4) if ip_v4 is not None else '', "accessIPv6": str(ip_v6) if ip_v6 is not None else '', "links": self._get_links(request, instance["uuid"], self._collection_name), + # NOTE(sdague): historically this was the + # os-disk-config extension, but now that extensions + # are gone, we merge these attributes here. + "OS-DCF:diskConfig": ( + 'AUTO' if instance.get('auto_disk_config') else 'MANUAL'), }, } if server["server"]["status"] in self._fault_statuses: @@ -137,19 +163,42 @@ def show(self, request, instance): if server["server"]["status"] in self._progress_statuses: server["server"]["progress"] = instance.get("progress", 0) + if api_version_request.is_supported(request, min_version="2.9"): + server["server"]["locked"] = (True if instance["locked_by"] + else False) + + if api_version_request.is_supported(request, min_version="2.19"): + server["server"]["description"] = instance.get( + "display_description") + + if api_version_request.is_supported(request, min_version="2.26"): + server["server"]["tags"] = [t.tag for t in instance.tags] + return server def index(self, request, instances): """Show a list of servers without many details.""" coll_name = self._collection_name - return self._list_view(self.basic, request, instances, coll_name) + return self._list_view(self.basic, request, instances, coll_name, + False) def detail(self, request, instances): """Detailed view of a list of instance.""" coll_name = self._collection_name + '/detail' - return self._list_view(self.show, request, instances, coll_name) - def _list_view(self, func, request, servers, coll_name): + if api_version_request.is_supported(request, min_version='2.47'): + # Determine if we should show extra_specs in the inlined flavor + # once before we iterate the list of instances + context = request.environ['nova.context'] + show_extra_specs = context.can(fes_policies.POLICY_ROOT % 'index', + fatal=False) + else: + show_extra_specs = False + + return self._list_view(self.show, request, instances, coll_name, + show_extra_specs) + + def _list_view(self, func, request, servers, coll_name, show_extra_specs): """Provide a view for a list of servers. :param func: Function used to format the server data @@ -159,7 +208,9 @@ def _list_view(self, func, request, servers, coll_name): for a pagination query :returns: Server data in dictionary format """ - server_list = [func(request, server)["server"] for server in servers] + server_list = [func(request, server, + show_extra_specs=show_extra_specs)["server"] + for server in servers] servers_links = self._get_collection_links(request, servers, coll_name) @@ -192,7 +243,8 @@ def _get_host_id(instance): host = instance.get("host") project = str(instance.get("project_id")) if host: - sha_hash = hashlib.sha224(project + host) + data = (project + host).encode('utf-8') + sha_hash = hashlib.sha224(data) return sha_hash.hexdigest() def _get_addresses(self, request, instance, extend_address=False): @@ -218,12 +270,30 @@ def _get_image(self, request, instance): else: return "" - def _get_flavor(self, request, instance): + def _get_flavor_dict(self, request, instance_type, show_extra_specs): + flavordict = { + "vcpus": instance_type.vcpus, + "ram": instance_type.memory_mb, + "disk": instance_type.root_gb, + "ephemeral": instance_type.ephemeral_gb, + "swap": instance_type.swap, + "original_name": instance_type.name + } + if show_extra_specs: + flavordict['extra_specs'] = instance_type.extra_specs + return flavordict + + def _get_flavor(self, request, instance, show_extra_specs): instance_type = instance.get_flavor() if not instance_type: - LOG.warning(_LW("Instance has had its instance_type removed " - "from the DB"), instance=instance) + LOG.warning("Instance has had its instance_type removed " + "from the DB", instance=instance) return {} + + if api_version_request.is_supported(request, min_version="2.47"): + return self._get_flavor_dict(request, instance_type, + show_extra_specs) + flavor_id = instance_type["flavorid"] flavor_bookmark = self._flavor_builder._get_bookmark_link(request, flavor_id, @@ -236,9 +306,26 @@ def _get_flavor(self, request, instance): }], } + def _load_fault(self, request, instance): + try: + mapping = objects.InstanceMapping.get_by_instance_uuid( + request.environ['nova.context'], instance.uuid) + if mapping.cell_mapping is not None: + with nova_context.target_cell(instance._context, + mapping.cell_mapping): + return instance.fault + except exception.InstanceMappingNotFound: + pass + + # NOTE(danms): No instance mapping at all, or a mapping with no cell, + # which means a legacy environment or instance. + return instance.fault + def _get_fault(self, request, instance): - # This can result in a lazy load of the fault information - fault = instance.fault + if 'fault' in instance: + fault = instance.fault + else: + fault = self._load_fault(request, instance) if not fault: return None @@ -259,58 +346,3 @@ def _get_fault(self, request, instance): fault_dict['details'] = fault["details"] return fault_dict - - -class ViewBuilderV21(ViewBuilder): - """Model a server v2.1 API response as a python dictionary.""" - - def __init__(self): - """Initialize view builder.""" - super(ViewBuilderV21, self).__init__() - self._address_builder = views_addresses.ViewBuilderV21() - # TODO(alex_xu): In V3 API, we correct the image bookmark link to - # use glance endpoint. We revert back it to use nova endpoint for v2.1. - self._image_builder = views_images.ViewBuilder() - - def show(self, request, instance, extend_address=True): - """Detailed view of a single instance.""" - server = { - "server": { - "id": instance["uuid"], - "name": instance["display_name"], - "status": self._get_vm_status(instance), - "tenant_id": instance.get("project_id") or "", - "user_id": instance.get("user_id") or "", - "metadata": self._get_metadata(instance), - "hostId": self._get_host_id(instance) or "", - # TODO(alex_xu): '_get_image' return {} when there image_ref - # isn't existed in V3 API, we revert it back to return "" in - # V2.1. - "image": self._get_image(request, instance), - "flavor": self._get_flavor(request, instance), - "created": utils.isotime(instance["created_at"]), - "updated": utils.isotime(instance["updated_at"]), - "addresses": self._get_addresses(request, instance, - extend_address), - "links": self._get_links(request, - instance["uuid"], - self._collection_name), - }, - } - if server["server"]["status"] in self._fault_statuses: - _inst_fault = self._get_fault(request, instance) - if _inst_fault: - server['server']['fault'] = _inst_fault - - if server["server"]["status"] in self._progress_statuses: - server["server"]["progress"] = instance.get("progress", 0) - - if api_version_request.is_supported(request, min_version="2.9"): - server["server"]["locked"] = (True if instance["locked_by"] - else False) - - if api_version_request.is_supported(request, min_version="2.19"): - server["server"]["description"] = instance.get( - "display_description") - - return server diff --git a/nova/api/openstack/compute/views/usages.py b/nova/api/openstack/compute/views/usages.py new file mode 100644 index 00000000000..cb3f1180f40 --- /dev/null +++ b/nova/api/openstack/compute/views/usages.py @@ -0,0 +1,28 @@ +# Copyright 2016 OpenStack Foundation +# 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. + +from nova.api.openstack import common + + +class ViewBuilder(common.ViewBuilder): + + _collection_name = "os-simple-tenant-usage" + + def get_links(self, request, server_usages, tenant_id=None): + coll_name = self._collection_name + if tenant_id: + coll_name = self._collection_name + '/{}'.format(tenant_id) + return self._get_collection_links( + request, server_usages, coll_name, 'instance_id') diff --git a/nova/api/openstack/compute/virtual_interfaces.py b/nova/api/openstack/compute/virtual_interfaces.py index 974b2a59f87..5319a4855fa 100644 --- a/nova/api/openstack/compute/virtual_interfaces.py +++ b/nova/api/openstack/compute/virtual_interfaces.py @@ -24,10 +24,7 @@ from nova import compute from nova.i18n import _ from nova import network - - -ALIAS = 'os-virtual-interfaces' -authorize = extensions.os_compute_authorizer(ALIAS) +from nova.policies import virtual_interfaces as vif_policies def _translate_vif_summary_view(req, vif): @@ -46,17 +43,19 @@ def _translate_vif_summary_view(req, vif): class ServerVirtualInterfaceController(wsgi.Controller): """The instance VIF API controller for the OpenStack API. + + This API is deprecated from the Microversion '2.44'. """ def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) - self.network_api = network.API(skip_policy_check=True) + self.compute_api = compute.API() + self.network_api = network.API() super(ServerVirtualInterfaceController, self).__init__() def _items(self, req, server_id, entity_maker): """Returns a list of VIFs, transformed through entity_maker.""" context = req.environ['nova.context'] - authorize(context) + context.can(vif_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, server_id) try: @@ -69,30 +68,9 @@ def _items(self, req, server_id, entity_maker): res = [entity_maker(req, vif) for vif in limited_list] return {'virtual_interfaces': res} + @wsgi.Controller.api_version("2.1", "2.43") @extensions.expected_errors((400, 404)) def index(self, req, server_id): """Returns the list of VIFs for a given instance.""" return self._items(req, server_id, entity_maker=_translate_vif_summary_view) - - -class VirtualInterfaces(extensions.V21APIExtensionBase): - """Virtual interface support.""" - - name = "VirtualInterfaces" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension( - ALIAS, - controller=ServerVirtualInterfaceController(), - parent=dict(member_name='server', collection_name='servers')) - resources.append(res) - - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/volumes.py b/nova/api/openstack/compute/volumes.py index 7dc2caaebed..ab97279117b 100644 --- a/nova/api/openstack/compute/volumes.py +++ b/nova/api/openstack/compute/volumes.py @@ -19,6 +19,8 @@ from webob import exc from nova.api.openstack import api_version_request +from nova.api.openstack.api_version_request \ + import MAX_PROXY_API_SUPPORT_VERSION from nova.api.openstack import common from nova.api.openstack.compute.schemas import volumes as volumes_schema from nova.api.openstack import extensions @@ -29,11 +31,11 @@ from nova import exception from nova.i18n import _ from nova import objects -from nova import volume +from nova.policies import volumes as vol_policies +from nova.policies import volumes_attachments as va_policies +from nova.volume import cinder ALIAS = "os-volumes" -authorize = extensions.os_compute_authorizer(ALIAS) -authorize_attach = extensions.os_compute_authorizer('os-volumes-attachments') def _translate_volume_detail_view(context, vol): @@ -68,7 +70,7 @@ def _translate_volume_summary_view(context, vol): # 'mountpoint': '/dev/sda/ # } # } - attachment = vol['attachments'].items()[0] + attachment = list(vol['attachments'].items())[0] d['attachments'] = [_translate_attachment_detail_view(vol['id'], attachment[0], attachment[1].get('mountpoint'))] @@ -97,14 +99,15 @@ class VolumeController(wsgi.Controller): """The Volumes API controller for the OpenStack API.""" def __init__(self): - self.volume_api = volume.API() + self.volume_api = cinder.API() super(VolumeController, self).__init__() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(404) def show(self, req, id): """Return data about the given volume.""" context = req.environ['nova.context'] - authorize(context) + context.can(vol_policies.BASE_POLICY_NAME) try: vol = self.volume_api.get(context, id) @@ -113,23 +116,28 @@ def show(self, req, id): return {'volume': _translate_volume_detail_view(context, vol)} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.response(202) - @extensions.expected_errors(404) + @extensions.expected_errors((400, 404)) def delete(self, req, id): """Delete a volume.""" context = req.environ['nova.context'] - authorize(context) + context.can(vol_policies.BASE_POLICY_NAME) try: self.volume_api.delete(context, id) + except exception.InvalidInput as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.VolumeNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(()) def index(self, req): """Returns a summary list of volumes.""" return self._items(req, entity_maker=_translate_volume_summary_view) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(()) def detail(self, req): """Returns a detailed list of volumes.""" @@ -138,19 +146,20 @@ def detail(self, req): def _items(self, req, entity_maker): """Returns a list of volumes, transformed through entity_maker.""" context = req.environ['nova.context'] - authorize(context) + context.can(vol_policies.BASE_POLICY_NAME) volumes = self.volume_api.get_all(context) limited_list = common.limited(volumes, req) res = [entity_maker(context, vol) for vol in limited_list] return {'volumes': res} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors((400, 403, 404)) @validation.schema(volumes_schema.create) def create(self, req, body): """Creates a new volume.""" context = req.environ['nova.context'] - authorize(context) + context.can(vol_policies.BASE_POLICY_NAME) vol = body['volume'] @@ -248,24 +257,37 @@ class VolumeAttachmentController(wsgi.Controller): """ def __init__(self): - self.compute_api = compute.API(skip_policy_check=True) - self.volume_api = volume.API() + self.compute_api = compute.API() + self.volume_api = cinder.API() super(VolumeAttachmentController, self).__init__() @extensions.expected_errors(404) def index(self, req, server_id): """Returns the list of volume attachments for a given instance.""" context = req.environ['nova.context'] - authorize_attach(context, action='index') - return self._items(req, server_id, - entity_maker=_translate_attachment_summary_view) + context.can(va_policies.POLICY_ROOT % 'index') + + instance = common.get_instance(self.compute_api, context, server_id) + + bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( + context, instance.uuid) + limited_list = common.limited(bdms, req) + + results = [] + for bdm in limited_list: + if bdm.volume_id: + va = _translate_attachment_summary_view(bdm.volume_id, + bdm.instance_uuid, + bdm.device_name) + results.append(va) + + return {'volumeAttachments': results} @extensions.expected_errors(404) def show(self, req, server_id, id): """Return data about the given volume attachment.""" context = req.environ['nova.context'] - authorize(context) - authorize_attach(context, action='show') + context.can(va_policies.POLICY_ROOT % 'show') volume_id = id instance = common.get_instance(self.compute_api, context, server_id) @@ -293,16 +315,18 @@ def show(self, req, server_id, id): instance.uuid, assigned_mountpoint)} + # TODO(mriedem): This API should return a 202 instead of a 200 response. @extensions.expected_errors((400, 404, 409)) - @validation.schema(volumes_schema.create_volume_attachment) + @validation.schema(volumes_schema.create_volume_attachment, '2.0', '2.48') + @validation.schema(volumes_schema.create_volume_attachment_v249, '2.49') def create(self, req, server_id, body): """Attach a volume to an instance.""" context = req.environ['nova.context'] - authorize(context) - authorize_attach(context, action='create') + context.can(va_policies.POLICY_ROOT % 'create') volume_id = body['volumeAttachment']['volumeId'] device = body['volumeAttachment'].get('device') + tag = body['volumeAttachment'].get('tag') instance = common.get_instance(self.compute_api, context, server_id) @@ -313,18 +337,20 @@ def create(self, req, server_id, body): try: device = self.compute_api.attach_volume(context, instance, - volume_id, device) - except exception.InstanceUnknownCell as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.VolumeNotFound as e: + volume_id, device, tag=tag) + except (exception.InstanceUnknownCell, + exception.VolumeNotFound) as e: raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.InstanceIsLocked as e: + except (exception.InstanceIsLocked, + exception.DevicePathInUse) as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'attach_volume', server_id) except (exception.InvalidVolume, - exception.InvalidDevicePath) as e: + exception.InvalidDevicePath, + exception.InvalidInput, + exception.TaggedAttachmentNotSupported) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) # The attach is async @@ -350,17 +376,26 @@ def create(self, req, server_id, body): @validation.schema(volumes_schema.update_volume_attachment) def update(self, req, server_id, id, body): context = req.environ['nova.context'] - authorize(context) - authorize_attach(context, action='update') + context.can(va_policies.POLICY_ROOT % 'update') old_volume_id = id try: old_volume = self.volume_api.get(context, old_volume_id) + except exception.VolumeNotFound as e: + raise exc.HTTPNotFound(explanation=e.format_message()) - new_volume_id = body['volumeAttachment']['volumeId'] + new_volume_id = body['volumeAttachment']['volumeId'] + try: new_volume = self.volume_api.get(context, new_volume_id) except exception.VolumeNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) + # NOTE: This BadRequest is different from the above NotFound even + # though the same VolumeNotFound exception. This is intentional + # because new_volume_id is specified in a request body and if a + # nonexistent resource in the body (not URI) the code should be + # 400 Bad Request as API-WG guideline. On the other hand, + # old_volume_id is specified with URI. So it is valid to return + # NotFound response if that is not existent. + raise exc.HTTPBadRequest(explanation=e.format_message()) instance = common.get_instance(self.compute_api, context, server_id) @@ -376,7 +411,7 @@ def update(self, req, server_id, id, body): new_volume) found = True break - except exception.VolumeUnattached: + except (exception.VolumeBDMNotFound): # The volume is not attached. Treat it as NotFound # by falling through. pass @@ -398,12 +433,12 @@ def update(self, req, server_id, id, body): def delete(self, req, server_id, id): """Detach a volume from an instance.""" context = req.environ['nova.context'] - authorize(context) - authorize_attach(context, action='delete') + context.can(va_policies.POLICY_ROOT % 'delete') volume_id = id - instance = common.get_instance(self.compute_api, context, server_id) + instance = common.get_instance(self.compute_api, context, server_id, + expected_attrs=['device_metadata']) if instance.vm_state in (vm_states.SHELVED, vm_states.SHELVED_OFFLOADED): _check_request_version(req, '2.20', 'detach_volume', @@ -431,10 +466,6 @@ def delete(self, req, server_id, id): self.compute_api.detach_volume(context, instance, volume) found = True break - except exception.VolumeUnattached: - # The volume is not attached. Treat it as NotFound - # by falling through. - pass except exception.InvalidVolume as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InstanceUnknownCell as e: @@ -452,26 +483,6 @@ def delete(self, req, server_id, id): msg = _("volume_id not found: %s") % volume_id raise exc.HTTPNotFound(explanation=msg) - def _items(self, req, server_id, entity_maker): - """Returns a list of attachments, transformed through entity_maker.""" - context = req.environ['nova.context'] - authorize(context) - - instance = common.get_instance(self.compute_api, context, server_id) - - bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( - context, instance.uuid) - limited_list = common.limited(bdms, req) - results = [] - - for bdm in limited_list: - if bdm.volume_id: - results.append(entity_maker(bdm.volume_id, - bdm.instance_uuid, - bdm.device_name)) - - return {'volumeAttachments': results} - def _translate_snapshot_detail_view(context, vol): """Maps keys for snapshots details view.""" @@ -501,14 +512,15 @@ class SnapshotController(wsgi.Controller): """The Snapshots API controller for the OpenStack API.""" def __init__(self): - self.volume_api = volume.API() + self.volume_api = cinder.API() super(SnapshotController, self).__init__() + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(404) def show(self, req, id): """Return data about the given snapshot.""" context = req.environ['nova.context'] - authorize(context) + context.can(vol_policies.BASE_POLICY_NAME) try: vol = self.volume_api.get_snapshot(context, id) @@ -517,23 +529,26 @@ def show(self, req, id): return {'snapshot': _translate_snapshot_detail_view(context, vol)} + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.response(202) @extensions.expected_errors(404) def delete(self, req, id): """Delete a snapshot.""" context = req.environ['nova.context'] - authorize(context) + context.can(vol_policies.BASE_POLICY_NAME) try: self.volume_api.delete_snapshot(context, id) except exception.SnapshotNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(()) def index(self, req): """Returns a summary list of snapshots.""" return self._items(req, entity_maker=_translate_snapshot_summary_view) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @extensions.expected_errors(()) def detail(self, req): """Returns a detailed list of snapshots.""" @@ -542,19 +557,20 @@ def detail(self, req): def _items(self, req, entity_maker): """Returns a list of snapshots, transformed through entity_maker.""" context = req.environ['nova.context'] - authorize(context) + context.can(vol_policies.BASE_POLICY_NAME) snapshots = self.volume_api.get_all_snapshots(context) limited_list = common.limited(snapshots, req) res = [entity_maker(context, snapshot) for snapshot in limited_list] return {'snapshots': res} - @extensions.expected_errors(400) + @wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) + @extensions.expected_errors((400, 403)) @validation.schema(volumes_schema.snapshot_create) def create(self, req, body): """Creates a new snapshot.""" context = req.environ['nova.context'] - authorize(context) + context.can(vol_policies.BASE_POLICY_NAME) snapshot = body['snapshot'] volume_id = snapshot['volume_id'] @@ -566,45 +582,12 @@ def create(self, req, body): else: create_func = self.volume_api.create_snapshot - new_snapshot = create_func(context, volume_id, - snapshot.get('display_name'), - snapshot.get('display_description')) + try: + new_snapshot = create_func(context, volume_id, + snapshot.get('display_name'), + snapshot.get('display_description')) + except exception.OverQuota as e: + raise exc.HTTPForbidden(explanation=e.format_message()) retval = _translate_snapshot_detail_view(context, new_snapshot) return {'snapshot': retval} - - -class Volumes(extensions.V21APIExtensionBase): - """Volumes support.""" - - name = "Volumes" - alias = ALIAS - version = 1 - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension( - ALIAS, VolumeController(), collection_actions={'detail': 'GET'}) - resources.append(res) - - res = extensions.ResourceExtension('os-volumes_boot', - inherits='servers') - resources.append(res) - - res = extensions.ResourceExtension('os-volume_attachments', - VolumeAttachmentController(), - parent=dict( - member_name='server', - collection_name='servers')) - resources.append(res) - - res = extensions.ResourceExtension( - 'os-snapshots', SnapshotController(), - collection_actions={'detail': 'GET'}) - resources.append(res) - - return resources - - def get_controller_extensions(self): - return [] diff --git a/nova/api/openstack/compute/wsgi.py b/nova/api/openstack/compute/wsgi.py new file mode 100644 index 00000000000..0ecb5e8d52b --- /dev/null +++ b/nova/api/openstack/compute/wsgi.py @@ -0,0 +1,20 @@ +# 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. +"""WSGI application entry-point for Nova Compute API, installed by pbr.""" + +from nova.api.openstack import wsgi_app + +NAME = "osapi_compute" + + +def init_application(): + return wsgi_app.init_application(NAME) diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 4af3c6bacad..8dbb1b6220c 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -16,7 +16,6 @@ import abc import functools -import os from oslo_log import log as logging from oslo_utils import importutils @@ -24,13 +23,9 @@ import webob.dec import webob.exc -import nova.api.openstack from nova.api.openstack import wsgi from nova import exception from nova.i18n import _ -from nova.i18n import _LE -from nova.i18n import _LW -import nova.policy LOG = logging.getLogger(__name__) @@ -140,7 +135,7 @@ class ExtensionManager(object): """ def sorted_extensions(self): if self.sorted_ext_list is None: - self.sorted_ext_list = sorted(six.iteritems(self.extensions)) + self.sorted_ext_list = sorted(self.extensions.items()) for _alias, ext in self.sorted_ext_list: yield ext @@ -193,7 +188,7 @@ def _check_extension(self, extension): try: extension.is_valid() except AttributeError: - LOG.exception(_LE("Exception loading extension")) + LOG.exception("Exception loading extension") return False return True @@ -210,13 +205,6 @@ def load_extension(self, ext_factory): LOG.debug("Loading extension %s", ext_factory) if isinstance(ext_factory, six.string_types): - if ext_factory.startswith('nova.api.openstack.compute.contrib'): - LOG.warn(_LW("The legacy v2 API module already moved into" - "'nova.api.openstack.compute.legacy_v2.contrib'. " - "Use new path instead of old path %s"), - ext_factory) - ext_factory = ext_factory.replace('contrib', - 'legacy_v2.contrib') # Load the factory factory = importutils.import_class(ext_factory) else: @@ -235,9 +223,9 @@ def _load_extensions(self): try: self.load_extension(ext_factory) except Exception as exc: - LOG.warning(_LW('Failed to load extension %(ext_factory)s: ' - '%(exc)s'), - {'ext_factory': ext_factory, 'exc': exc}) + LOG.warning( + 'Failed to load extension %(ext_factory)s: %(exc)s', + {'ext_factory': ext_factory, 'exc': exc}) class ControllerExtension(object): @@ -273,136 +261,6 @@ def __init__(self, collection, controller=None, parent=None, self.member_name = member_name -def load_standard_extensions(ext_mgr, logger, path, package, ext_list=None): - """Registers all standard API extensions.""" - - # Walk through all the modules in our directory... - our_dir = path[0] - for dirpath, dirnames, filenames in os.walk(our_dir): - # Compute the relative package name from the dirpath - relpath = os.path.relpath(dirpath, our_dir) - if relpath == '.': - relpkg = '' - else: - relpkg = '.%s' % '.'.join(relpath.split(os.sep)) - - # Now, consider each file in turn, only considering .py files - for fname in filenames: - root, ext = os.path.splitext(fname) - - # Skip __init__ and anything that's not .py - if ext != '.py' or root == '__init__': - continue - - # Try loading it - classname = "%s%s" % (root[0].upper(), root[1:]) - classpath = ("%s%s.%s.%s" % - (package, relpkg, root, classname)) - - if ext_list is not None and classname not in ext_list: - logger.debug("Skipping extension: %s" % classpath) - continue - - try: - ext_mgr.load_extension(classpath) - except Exception as exc: - logger.warn(_LW('Failed to load extension %(classpath)s: ' - '%(exc)s'), - {'classpath': classpath, 'exc': exc}) - - # Now, let's consider any subdirectories we may have... - subdirs = [] - for dname in dirnames: - # Skip it if it does not have __init__.py - if not os.path.exists(os.path.join(dirpath, dname, '__init__.py')): - continue - - # If it has extension(), delegate... - ext_name = "%s%s.%s.extension" % (package, relpkg, dname) - try: - ext = importutils.import_class(ext_name) - except ImportError: - # extension() doesn't exist on it, so we'll explore - # the directory for ourselves - subdirs.append(dname) - else: - try: - ext(ext_mgr) - except Exception as exc: - logger.warn(_LW('Failed to load extension %(ext_name)s:' - '%(exc)s'), - {'ext_name': ext_name, 'exc': exc}) - - # Update the list of directories we'll explore... - # using os.walk 'the caller can modify the dirnames list in-place, - # and walk() will only recurse into the subdirectories whose names - # remain in dirnames' - # https://docs.python.org/2/library/os.html#os.walk - dirnames[:] = subdirs - - -# This will be deprecated after policy cleanup finished -def core_authorizer(api_name, extension_name): - def authorize(context, target=None, action=None): - if target is None: - target = {'project_id': context.project_id, - 'user_id': context.user_id} - if action is None: - act = '%s:%s' % (api_name, extension_name) - else: - act = '%s:%s:%s' % (api_name, extension_name, action) - nova.policy.enforce(context, act, target) - return authorize - - -# This is only used for Nova V2 API, after v2 API depreciated, this will be -# deprecated also. -def extension_authorizer(api_name, extension_name): - return core_authorizer('%s_extension' % api_name, extension_name) - - -def _soft_authorizer(hard_authorizer, api_name, extension_name): - hard_authorize = hard_authorizer(api_name, extension_name) - - def authorize(context, target=None, action=None): - try: - hard_authorize(context, target=target, action=action) - return True - except exception.Forbidden: - return False - return authorize - - -# This is only used for Nova V2 API, after V2 API depreciated, this will be -# deprecated also. -def soft_extension_authorizer(api_name, extension_name): - return _soft_authorizer(extension_authorizer, api_name, extension_name) - - -# This will be deprecated after policy cleanup finished -def soft_core_authorizer(api_name, extension_name): - return _soft_authorizer(core_authorizer, api_name, extension_name) - - -# This will be deprecated after ec2 old style policy removed in later release -def check_compute_policy(context, action, target, scope='compute'): - _action = '%s:%s' % (scope, action) - nova.policy.enforce(context, _action, target) - - -# NOTE(alex_xu): The functions os_compute_authorizer and -# os_compute_soft_authorizer are used to policy enforcement for OpenStack -# Compute API, now Nova V2.1 REST API will invoke it. -# - -def os_compute_authorizer(extension_name): - return core_authorizer('os_compute_api', extension_name) - - -def os_compute_soft_authorizer(extension_name): - return soft_core_authorizer('os_compute_api', extension_name) - - @six.add_metaclass(abc.ABCMeta) class V21APIExtensionBase(object): """Abstract base class for all v2.1 API extensions. @@ -484,9 +342,9 @@ def wrapped(*args, **kwargs): t_errors = errors if exc.code in t_errors: raise - elif isinstance(exc, exception.PolicyNotAuthorized): + elif isinstance(exc, exception.Forbidden): # Note(cyeoh): Special case to handle - # PolicyNotAuthorized exceptions so every + # Forbidden exceptions so every # extension method does not need to wrap authorize # calls. ResourceExceptionHandler silently # converts NotAuthorized to HTTPForbidden @@ -496,8 +354,14 @@ def wrapped(*args, **kwargs): # happens due to invalid API parameters, as an # expected error. raise + elif isinstance(exc, exception.Unauthorized): + # Handle an authorized exception, will be + # automatically converted to a HTTP 401, clients + # like python-novaclient handle this error to + # generate new token and do another attempt. + raise - LOG.exception(_LE("Unexpected exception in API method")) + LOG.exception("Unexpected exception in API method") msg = _('Unexpected API Error. Please report this at ' 'http://bugs.launchpad.net/nova/ and attach the Nova ' 'API log if possible.\n%s') % type(exc) diff --git a/nova/api/openstack/identity.py b/nova/api/openstack/identity.py new file mode 100644 index 00000000000..4a63e76b1fa --- /dev/null +++ b/nova/api/openstack/identity.py @@ -0,0 +1,83 @@ +# Copyright 2017 IBM +# +# 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 keystoneauth1 import exceptions as kse +from keystoneauth1 import loading as ks_loading +from oslo_log import log as logging +import webob + +import nova.conf +from nova.i18n import _ + + +CONF = nova.conf.CONF +LOG = logging.getLogger(__name__) + + +def verify_project_id(context, project_id): + """verify that a project_id exists. + + This attempts to verify that a project id exists. If it does not, + an HTTPBadRequest is emitted. + + """ + sess = ks_loading.load_session_from_conf_options( + CONF, 'keystone', auth=context.get_auth_plugin()) + + failure = webob.exc.HTTPBadRequest( + explanation=_("Project ID %s is not a valid project.") % + project_id) + try: + resp = sess.get('/projects/%s' % project_id, + endpoint_filter={ + 'service_type': 'identity', + 'version': (3, 0) + }, + raise_exc=False) + except kse.EndpointNotFound: + LOG.error( + "Keystone identity service version 3.0 was not found. This might " + "be because your endpoint points to the v2.0 versioned endpoint " + "which is not supported. Please fix this.") + raise failure + except kse.ClientException: + # something is wrong, like there isn't a keystone v3 endpoint, + # or nova isn't configured for the interface to talk to it; + # we'll take the pass and default to everything being ok. + LOG.info("Unable to contact keystone to verify project_id") + return True + + if resp: + # All is good with this 20x status + return True + elif resp.status_code == 404: + # we got access, and we know this project is not there + raise failure + elif resp.status_code == 403: + # we don't have enough permission to verify this, so default + # to "it's ok". + LOG.info( + "Insufficient permissions for user %(user)s to verify " + "existence of project_id %(pid)s", + {"user": context.user_id, "pid": project_id}) + return True + else: + LOG.warning( + "Unexpected response from keystone trying to " + "verify project_id %(pid)s - resp: %(code)s %(content)s", + {"pid": project_id, + "code": resp.status_code, + "content": resp.content}) + # realize we did something wrong, but move on with a warning + return True diff --git a/nova/api/openstack/compute/legacy_v2/__init__.py b/nova/api/openstack/placement/__init__.py similarity index 100% rename from nova/api/openstack/compute/legacy_v2/__init__.py rename to nova/api/openstack/placement/__init__.py diff --git a/nova/api/openstack/placement/auth.py b/nova/api/openstack/placement/auth.py new file mode 100644 index 00000000000..13fae4af9b3 --- /dev/null +++ b/nova/api/openstack/placement/auth.py @@ -0,0 +1,76 @@ +# 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 oslo_context import context +from oslo_db.sqlalchemy import enginefacade +from oslo_log import log as logging +from oslo_middleware import request_id +import webob.dec +import webob.exc + + +LOG = logging.getLogger(__name__) + + +class Middleware(object): + + def __init__(self, application, **kwargs): + self.application = application + + +# NOTE(cdent): Only to be used in tests where auth is being faked. +class NoAuthMiddleware(Middleware): + """Require a token if one isn't present.""" + + def __init__(self, application): + self.application = application + + @webob.dec.wsgify + def __call__(self, req): + if 'X-Auth-Token' not in req.headers: + return webob.exc.HTTPUnauthorized() + + token = req.headers['X-Auth-Token'] + user_id, _sep, project_id = token.partition(':') + project_id = project_id or user_id + if user_id == 'admin': + roles = ['admin'] + else: + roles = [] + req.headers['X_USER_ID'] = user_id + req.headers['X_TENANT_ID'] = project_id + req.headers['X_ROLES'] = ','.join(roles) + return self.application + + +@enginefacade.transaction_context_provider +class RequestContext(context.RequestContext): + pass + + +class PlacementKeystoneContext(Middleware): + """Make a request context from keystone headers.""" + + @webob.dec.wsgify + def __call__(self, req): + req_id = req.environ.get(request_id.ENV_REQUEST_ID) + + ctx = RequestContext.from_environ( + req.environ, request_id=req_id) + + if ctx.user_id is None: + LOG.debug("Neither X_USER_ID nor X_USER found in request") + return webob.exc.HTTPUnauthorized() + + req.environ['placement.context'] = ctx + return self.application diff --git a/nova/api/openstack/placement/deploy.py b/nova/api/openstack/placement/deploy.py new file mode 100644 index 00000000000..821aeb61f8e --- /dev/null +++ b/nova/api/openstack/placement/deploy.py @@ -0,0 +1,89 @@ +# 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. +"""Deployment handling for Placmenent API.""" + +from keystonemiddleware import auth_token +import oslo_middleware +from oslo_middleware import cors + +from nova.api import openstack as common_api +from nova.api.openstack.placement import auth +from nova.api.openstack.placement import handler +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import requestlog +from nova import objects + + +# TODO(cdent): NAME points to the config project being used, so for +# now this is "nova" but we probably want "placement" eventually. +NAME = "nova" + + +# Make sure that objects are registered for this running of the +# placement API. +objects.register_all() + + +def deploy(conf, project_name): + """Assemble the middleware pipeline leading to the placement app.""" + if conf.api.auth_strategy == 'noauth2': + auth_middleware = auth.NoAuthMiddleware + else: + # Do not use 'oslo_config_project' param here as the conf + # location may have been overridden earlier in the deployment + # process with OS_PLACEMENT_CONFIG_DIR in wsgi.py. + auth_middleware = auth_token.filter_factory( + {}, oslo_config_config=conf) + + # Pass in our CORS config, if any, manually as that's a) + # explicit, b) makes testing more straightfoward, c) let's + # us control the use of cors by the presence of its config. + conf.register_opts(cors.CORS_OPTS, 'cors') + if conf.cors.allowed_origin: + cors_middleware = oslo_middleware.CORS.factory( + {}, **conf.cors) + else: + cors_middleware = None + + context_middleware = auth.PlacementKeystoneContext + req_id_middleware = oslo_middleware.RequestId + microversion_middleware = microversion.MicroversionMiddleware + fault_wrap = common_api.FaultWrapper + request_log = requestlog.RequestLog + + application = handler.PlacementHandler() + + # NOTE(cdent): The ordering here is important. The list is ordered + # from the inside out. For a single request req_id_middleware is called + # first and microversion_middleware last. Then the request is finally + # passed to the application (the PlacementHandler). At that point + # the response ascends the middleware in the reverse of the + # order the request went in. This order ensures that log messages + # all see the same contextual information including request id and + # authentication information. + for middleware in (microversion_middleware, + fault_wrap, + request_log, + context_middleware, + auth_middleware, + cors_middleware, + req_id_middleware, + ): + if middleware: + application = middleware(application) + + return application + + +def loadapp(config, project_name=NAME): + application = deploy(config, project_name) + return application diff --git a/nova/api/openstack/placement/handler.py b/nova/api/openstack/placement/handler.py new file mode 100644 index 00000000000..a54360ded55 --- /dev/null +++ b/nova/api/openstack/placement/handler.py @@ -0,0 +1,231 @@ +# 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. +"""Handlers for placement API. + +Individual handlers are associated with URL paths in the +ROUTE_DECLARATIONS dictionary. At the top level each key is a Routes +compliant path. The value of that key is a dictionary mapping +individual HTTP request methods to a Python function representing a +simple WSGI application for satisfying that request. + +The ``make_map`` method processes ROUTE_DECLARATIONS to create a +Routes.Mapper, including automatic handlers to respond with a +405 when a request is made against a valid URL with an invalid +method. +""" + +import routes +import webob + +from oslo_log import log as logging + +from nova.api.openstack.placement.handlers import aggregate +from nova.api.openstack.placement.handlers import allocation +from nova.api.openstack.placement.handlers import allocation_candidate +from nova.api.openstack.placement.handlers import inventory +from nova.api.openstack.placement.handlers import resource_class +from nova.api.openstack.placement.handlers import resource_provider +from nova.api.openstack.placement.handlers import root +from nova.api.openstack.placement.handlers import trait +from nova.api.openstack.placement.handlers import usage +from nova.api.openstack.placement import policy +from nova.api.openstack.placement import util +from nova import exception +from nova.i18n import _ + +LOG = logging.getLogger(__name__) + +# URLs and Handlers +# NOTE(cdent): When adding URLs here, do not use regex patterns in +# the path parameters (e.g. {uuid:[0-9a-zA-Z-]+}) as that will lead +# to 404s that are controlled outside of the individual resources +# and thus do not include specific information on the why of the 404. +ROUTE_DECLARATIONS = { + '/': { + 'GET': root.home, + }, + # NOTE(cdent): This allows '/placement/' and '/placement' to + # both work as the root of the service, which we probably want + # for those situations where the service is mounted under a + # prefix (as it is in devstack). While weird, an empty string is + # a legit key in a dictionary and matches as desired in Routes. + '': { + 'GET': root.home, + }, + '/resource_classes': { + 'GET': resource_class.list_resource_classes, + 'POST': resource_class.create_resource_class + }, + '/resource_classes/{name}': { + 'GET': resource_class.get_resource_class, + 'PUT': resource_class.update_resource_class, + 'DELETE': resource_class.delete_resource_class, + }, + '/resource_providers': { + 'GET': resource_provider.list_resource_providers, + 'POST': resource_provider.create_resource_provider + }, + '/resource_providers/{uuid}': { + 'GET': resource_provider.get_resource_provider, + 'DELETE': resource_provider.delete_resource_provider, + 'PUT': resource_provider.update_resource_provider + }, + '/resource_providers/{uuid}/inventories': { + 'GET': inventory.get_inventories, + 'POST': inventory.create_inventory, + 'PUT': inventory.set_inventories, + 'DELETE': inventory.delete_inventories + }, + '/resource_providers/{uuid}/inventories/{resource_class}': { + 'GET': inventory.get_inventory, + 'PUT': inventory.update_inventory, + 'DELETE': inventory.delete_inventory + }, + '/resource_providers/{uuid}/usages': { + 'GET': usage.list_usages + }, + '/resource_providers/{uuid}/aggregates': { + 'GET': aggregate.get_aggregates, + 'PUT': aggregate.set_aggregates + }, + '/resource_providers/{uuid}/allocations': { + 'GET': allocation.list_for_resource_provider, + }, + '/allocations/{consumer_uuid}': { + 'GET': allocation.list_for_consumer, + 'PUT': allocation.set_allocations, + 'DELETE': allocation.delete_allocations, + }, + '/allocation_candidates': { + 'GET': allocation_candidate.list_allocation_candidates, + }, + '/traits': { + 'GET': trait.list_traits, + }, + '/traits/{name}': { + 'GET': trait.get_trait, + 'PUT': trait.put_trait, + 'DELETE': trait.delete_trait, + }, + '/resource_providers/{uuid}/traits': { + 'GET': trait.list_traits_for_resource_provider, + 'PUT': trait.update_traits_for_resource_provider, + 'DELETE': trait.delete_traits_for_resource_provider + }, + '/usages': { + 'GET': usage.get_total_usages, + }, +} + + +def dispatch(environ, start_response, mapper): + """Find a matching route for the current request. + + If no match is found, raise a 404 response. + If there is a matching route, but no matching handler + for the given method, raise a 405. + """ + result = mapper.match(environ=environ) + if result is None: + raise webob.exc.HTTPNotFound( + json_formatter=util.json_error_formatter) + # We can't reach this code without action being present. + handler = result.pop('action') + environ['wsgiorg.routing_args'] = ((), result) + return handler(environ, start_response) + + +def handle_405(environ, start_response): + """Return a 405 response when method is not allowed. + + If _methods are in routing_args, send an allow header listing + the methods that are possible on the provided URL. + """ + _methods = util.wsgi_path_item(environ, '_methods') + headers = {} + if _methods: + # Ensure allow header is a python 2 or 3 native string (thus + # not unicode in python 2 but stay a string in python 3) + # In the process done by Routes to save the allowed methods + # to its routing table they become unicode in py2. + headers['allow'] = str(_methods) + # Use Exception class as WSGI Application. We don't want to raise here. + response = webob.exc.HTTPMethodNotAllowed( + _('The method specified is not allowed for this resource.'), + headers=headers, json_formatter=util.json_error_formatter) + return response(environ, start_response) + + +def make_map(declarations): + """Process route declarations to create a Route Mapper.""" + mapper = routes.Mapper() + for route, targets in declarations.items(): + allowed_methods = [] + for method in targets: + mapper.connect(route, action=targets[method], + conditions=dict(method=[method])) + allowed_methods.append(method) + allowed_methods = ', '.join(allowed_methods) + mapper.connect(route, action=handle_405, _methods=allowed_methods) + return mapper + + +class PlacementHandler(object): + """Serve Placement API. + + Dispatch to handlers defined in ROUTE_DECLARATIONS. + """ + + def __init__(self, **local_config): + # NOTE(cdent): Local config currently unused. + self._map = make_map(ROUTE_DECLARATIONS) + + def __call__(self, environ, start_response): + # All requests but '/' require admin. + if environ['PATH_INFO'] != '/': + context = environ['placement.context'] + # TODO(cdent): Using is_admin everywhere (except /) is + # insufficiently flexible for future use case but is + # convenient for initial exploration. + if not policy.placement_authorize(context, 'placement'): + raise webob.exc.HTTPForbidden( + _('admin required'), + json_formatter=util.json_error_formatter) + # Check that an incoming request with a content-length header + # that is an integer > 0 and not empty, also has a content-type + # header that is not empty. If not raise a 400. + clen = environ.get('CONTENT_LENGTH') + try: + if clen and (int(clen) > 0) and not environ.get('CONTENT_TYPE'): + raise webob.exc.HTTPBadRequest( + _('content-type header required when content-length > 0'), + json_formatter=util.json_error_formatter) + except ValueError as exc: + raise webob.exc.HTTPBadRequest( + _('content-length header must be an integer'), + json_formatter=util.json_error_formatter) + try: + return dispatch(environ, start_response, self._map) + # Trap the NotFound exceptions raised by the objects used + # with the API and transform them into webob.exc.HTTPNotFound. + except exception.NotFound as exc: + raise webob.exc.HTTPNotFound( + exc, json_formatter=util.json_error_formatter) + # Trap the HTTPNotFound that can be raised by dispatch() + # when no route is found. The exception is passed through to + # the FaultWrap middleware without causing an alarming log + # message. + except webob.exc.HTTPNotFound: + raise + except Exception as exc: + LOG.exception("Uncaught exception") + raise diff --git a/nova/cert/__init__.py b/nova/api/openstack/placement/handlers/__init__.py similarity index 100% rename from nova/cert/__init__.py rename to nova/api/openstack/placement/handlers/__init__.py diff --git a/nova/api/openstack/placement/handlers/aggregate.py b/nova/api/openstack/placement/handlers/aggregate.py new file mode 100644 index 00000000000..063b4fe149a --- /dev/null +++ b/nova/api/openstack/placement/handlers/aggregate.py @@ -0,0 +1,76 @@ +# 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. +"""Aggregate handlers for Placement API.""" + +from oslo_serialization import jsonutils +from oslo_utils import encodeutils + +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import util +from nova.api.openstack.placement import wsgi_wrapper +from nova import objects + + +PUT_AGGREGATES_SCHEMA = { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "uniqueItems": True +} + + +def _send_aggregates(response, aggregate_uuids): + response.status = 200 + response.body = encodeutils.to_utf8( + jsonutils.dumps(_serialize_aggregates(aggregate_uuids))) + response.content_type = 'application/json' + return response + + +def _serialize_aggregates(aggregate_uuids): + return {'aggregates': aggregate_uuids} + + +@wsgi_wrapper.PlacementWsgify +@util.check_accept('application/json') +def get_aggregates(req): + """GET a list of aggregates associated with a resource provider. + + If the resource provider does not exist return a 404. + + On success return a 200 with an application/json body containing a + list of aggregate uuids. + """ + microversion.raise_http_status_code_if_not_version(req, 404, (1, 1)) + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + aggregate_uuids = resource_provider.get_aggregates() + + return _send_aggregates(req.response, aggregate_uuids) + + +@wsgi_wrapper.PlacementWsgify +@util.require_content('application/json') +def set_aggregates(req): + microversion.raise_http_status_code_if_not_version(req, 404, (1, 1)) + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + aggregate_uuids = util.extract_json(req.body, PUT_AGGREGATES_SCHEMA) + resource_provider.set_aggregates(aggregate_uuids) + + return _send_aggregates(req.response, aggregate_uuids) diff --git a/nova/api/openstack/placement/handlers/allocation.py b/nova/api/openstack/placement/handlers/allocation.py new file mode 100644 index 00000000000..40ab62bd525 --- /dev/null +++ b/nova/api/openstack/placement/handlers/allocation.py @@ -0,0 +1,316 @@ +# 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. +"""Placement API handlers for setting and deleting allocations.""" + +import collections +import copy + +from oslo_log import log as logging +from oslo_serialization import jsonutils +from oslo_utils import encodeutils +import webob + +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import util +from nova.api.openstack.placement import wsgi_wrapper +from nova import exception +from nova.i18n import _ +from nova import objects + + +LOG = logging.getLogger(__name__) + +ALLOCATION_SCHEMA = { + "type": "object", + "properties": { + "allocations": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "resource_provider": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "additionalProperties": False, + "required": ["uuid"] + }, + "resources": { + "type": "object", + "minProperties": 1, + "patternProperties": { + "^[0-9A-Z_]+$": { + "type": "integer", + "minimum": 1, + } + }, + "additionalProperties": False + } + }, + "required": [ + "resource_provider", + "resources" + ], + "additionalProperties": False + } + } + }, + "required": ["allocations"], + "additionalProperties": False +} + +ALLOCATION_SCHEMA_V1_8 = copy.deepcopy(ALLOCATION_SCHEMA) +ALLOCATION_SCHEMA_V1_8['properties']['project_id'] = {'type': 'string', + 'minLength': 1, + 'maxLength': 255} +ALLOCATION_SCHEMA_V1_8['properties']['user_id'] = {'type': 'string', + 'minLength': 1, + 'maxLength': 255} +ALLOCATION_SCHEMA_V1_8['required'].extend(['project_id', 'user_id']) + + +def _allocations_dict(allocations, key_fetcher, resource_provider=None): + """Turn allocations into a dict of resources keyed by key_fetcher.""" + allocation_data = collections.defaultdict(dict) + + for allocation in allocations: + key = key_fetcher(allocation) + if 'resources' not in allocation_data[key]: + allocation_data[key]['resources'] = {} + + resource_class = allocation.resource_class + allocation_data[key]['resources'][resource_class] = allocation.used + + if not resource_provider: + generation = allocation.resource_provider.generation + allocation_data[key]['generation'] = generation + + result = {'allocations': allocation_data} + if resource_provider: + result['resource_provider_generation'] = resource_provider.generation + return result + + +def _serialize_allocations_for_consumer(allocations): + """Turn a list of allocations into a dict by resource provider uuid. + + { + 'allocations': { + RP_UUID_1: { + 'generation': GENERATION, + 'resources': { + 'DISK_GB': 4, + 'VCPU': 2 + } + }, + RP_UUID_2: { + 'generation': GENERATION, + 'resources': { + 'DISK_GB': 6, + 'VCPU': 3 + } + } + } + } + """ + return _allocations_dict(allocations, + lambda x: x.resource_provider.uuid) + + +def _serialize_allocations_for_resource_provider(allocations, + resource_provider): + """Turn a list of allocations into a dict by consumer id. + + {'resource_provider_generation': GENERATION, + 'allocations': + CONSUMER_ID_1: { + 'resources': { + 'DISK_GB': 4, + 'VCPU': 2 + } + }, + CONSUMER_ID_2: { + 'resources': { + 'DISK_GB': 6, + 'VCPU': 3 + } + } + } + """ + return _allocations_dict(allocations, lambda x: x.consumer_id, + resource_provider=resource_provider) + + +@wsgi_wrapper.PlacementWsgify +@util.check_accept('application/json') +def list_for_consumer(req): + """List allocations associated with a consumer.""" + context = req.environ['placement.context'] + consumer_id = util.wsgi_path_item(req.environ, 'consumer_uuid') + + # NOTE(cdent): There is no way for a 404 to be returned here, + # only an empty result. We do not have a way to validate a + # consumer id. + allocations = objects.AllocationList.get_all_by_consumer_id( + context, consumer_id) + + allocations_json = jsonutils.dumps( + _serialize_allocations_for_consumer(allocations)) + + req.response.status = 200 + req.response.body = encodeutils.to_utf8(allocations_json) + req.response.content_type = 'application/json' + return req.response + + +@wsgi_wrapper.PlacementWsgify +@util.check_accept('application/json') +def list_for_resource_provider(req): + """List allocations associated with a resource provider.""" + # TODO(cdent): On a shared resource provider (for example a + # giant disk farm) this list could get very long. At the moment + # we have no facility for limiting the output. Given that we are + # using a dict of dicts for the output we are potentially limiting + # ourselves in terms of sorting and filtering. + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + + # confirm existence of resource provider so we get a reasonable + # 404 instead of empty list + try: + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + except exception.NotFound as exc: + raise webob.exc.HTTPNotFound( + _("Resource provider '%(rp_uuid)s' not found: %(error)s") % + {'rp_uuid': uuid, 'error': exc}) + + allocations = objects.AllocationList.get_all_by_resource_provider_uuid( + context, uuid) + + allocations_json = jsonutils.dumps( + _serialize_allocations_for_resource_provider( + allocations, resource_provider)) + + req.response.status = 200 + req.response.body = encodeutils.to_utf8(allocations_json) + req.response.content_type = 'application/json' + return req.response + + +def _set_allocations(req, schema): + context = req.environ['placement.context'] + consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid') + data = util.extract_json(req.body, schema) + allocation_data = data['allocations'] + + # If the body includes an allocation for a resource provider + # that does not exist, raise a 400. + allocation_objects = [] + for allocation in allocation_data: + resource_provider_uuid = allocation['resource_provider']['uuid'] + + try: + resource_provider = objects.ResourceProvider.get_by_uuid( + context, resource_provider_uuid) + except exception.NotFound: + raise webob.exc.HTTPBadRequest( + _("Allocation for resource provider '%(rp_uuid)s' " + "that does not exist.") % + {'rp_uuid': resource_provider_uuid}) + + resources = allocation['resources'] + for resource_class in resources: + allocation = objects.Allocation( + resource_provider=resource_provider, + consumer_id=consumer_uuid, + resource_class=resource_class, + used=resources[resource_class]) + allocation_objects.append(allocation) + + allocations = objects.AllocationList( + context, + objects=allocation_objects, + project_id=data.get('project_id'), + user_id=data.get('user_id'), + ) + + try: + allocations.create_all() + LOG.debug("Successfully wrote allocations %s", allocations) + # InvalidInventory is a parent for several exceptions that + # indicate either that Inventory is not present, or that + # capacity limits have been exceeded. + except exception.NotFound as exc: + raise webob.exc.HTTPBadRequest( + _("Unable to allocate inventory for resource provider " + "%(rp_uuid)s: %(error)s") % + {'rp_uuid': resource_provider_uuid, 'error': exc}) + except exception.InvalidInventory as exc: + raise webob.exc.HTTPConflict( + _('Unable to allocate inventory: %(error)s') % {'error': exc}) + except exception.ConcurrentUpdateDetected as exc: + raise webob.exc.HTTPConflict( + _('Inventory changed while attempting to allocate: %(error)s') % + {'error': exc}) + + req.response.status = 204 + req.response.content_type = None + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.0', '1.7') +@util.require_content('application/json') +def set_allocations(req): + return _set_allocations(req, ALLOCATION_SCHEMA) + + +@wsgi_wrapper.PlacementWsgify # noqa +@microversion.version_handler('1.8') +@util.require_content('application/json') +def set_allocations(req): + return _set_allocations(req, ALLOCATION_SCHEMA_V1_8) + + +@wsgi_wrapper.PlacementWsgify +def delete_allocations(req): + context = req.environ['placement.context'] + consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid') + + allocations = objects.AllocationList.get_all_by_consumer_id( + context, consumer_uuid) + if allocations: + try: + allocations.delete_all() + # NOTE(pumaranikar): Following NotFound exception added in the case + # when allocation is deleted from allocations list by some other + # activity. In that case, delete_all() will throw a NotFound exception. + except exception.NotFound as exc: + raise webob.exc.HTPPNotFound( + _("Allocation for consumer with id %(id)s not found." + "error: %(error)s") % + {'id': consumer_uuid, 'error': exc}) + else: + raise webob.exc.HTTPNotFound( + _("No allocations for consumer '%(consumer_uuid)s'") % + {'consumer_uuid': consumer_uuid}) + LOG.debug("Successfully deleted allocations %s", allocations) + + req.response.status = 204 + req.response.content_type = None + return req.response diff --git a/nova/api/openstack/placement/handlers/allocation_candidate.py b/nova/api/openstack/placement/handlers/allocation_candidate.py new file mode 100644 index 00000000000..3940408522e --- /dev/null +++ b/nova/api/openstack/placement/handlers/allocation_candidate.py @@ -0,0 +1,183 @@ +# 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. + +"""Placement API handlers for getting allocation candidates.""" + +import collections + +from oslo_log import log as logging +from oslo_serialization import jsonutils +from oslo_utils import encodeutils +import webob + +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import util +from nova.api.openstack.placement import wsgi_wrapper +from nova import exception +from nova.i18n import _ +from nova.objects import resource_provider as rp_obj + + +LOG = logging.getLogger(__name__) + +# Represents the allowed query string parameters to the GET +# /allocation_candidates API call +_GET_SCHEMA_1_10 = { + "type": "object", + "properties": { + "resources": { + "type": "string" + }, + }, + "required": [ + "resources", + ], + "additionalProperties": False, +} + + +def _transform_allocation_requests(alloc_reqs): + """Turn supplied list of AllocationRequest objects into a list of dicts of + resources involved in the allocation request. The returned results is + intended to be able to be used as the body of a PUT + /allocations/{consumer_uuid} HTTP request, so therefore we return a list of + JSON objects that looks like the following: + + [ + { + "allocations": [ + { + "resource_provider": { + "uuid": $rp_uuid, + } + "resources": { + $resource_class: $requested_amount, ... + }, + }, ... + ], + }, ... + ] + """ + results = [] + for ar in alloc_reqs: + provider_resources = collections.defaultdict(dict) + for rr in ar.resource_requests: + res_dict = provider_resources[rr.resource_provider.uuid] + res_dict[rr.resource_class] = rr.amount + + allocs = [ + { + "resource_provider": { + "uuid": rp_uuid, + }, + "resources": resources, + } for rp_uuid, resources in provider_resources.items() + ] + alloc = { + "allocations": allocs + } + results.append(alloc) + return results + + +def _transform_provider_summaries(p_sums): + """Turn supplied list of ProviderSummary objects into a dict, keyed by + resource provider UUID, of dicts of provider and inventory information. + + { + RP_UUID_1: { + 'resources': { + 'DISK_GB': { + 'capacity': 100, + 'used': 0, + }, + 'VCPU': { + 'capacity': 4, + 'used': 0, + } + } + }, + RP_UUID_2: { + 'resources': { + 'DISK_GB': { + 'capacity': 100, + 'used': 0, + }, + 'VCPU': { + 'capacity': 4, + 'used': 0, + } + } + } + } + """ + return { + ps.resource_provider.uuid: { + 'resources': { + psr.resource_class: { + 'capacity': psr.capacity, + 'used': psr.used, + } for psr in ps.resources + } + } for ps in p_sums + } + + +def _transform_allocation_candidates(alloc_cands): + """Turn supplied AllocationCandidates object into a dict containing + allocation requests and provider summaries. + + { + 'allocation_requests': , + 'provider_summaries': , + } + """ + a_reqs = _transform_allocation_requests(alloc_cands.allocation_requests) + p_sums = _transform_provider_summaries(alloc_cands.provider_summaries) + return { + 'allocation_requests': a_reqs, + 'provider_summaries': p_sums, + } + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.10') +@util.check_accept('application/json') +def list_allocation_candidates(req): + """GET a JSON object with a list of allocation requests and a JSON object + of provider summary objects + + On success return a 200 and an application/json body representing + a collection of allocation requests and provider summaries + """ + context = req.environ['placement.context'] + schema = _GET_SCHEMA_1_10 + util.validate_query_params(req, schema) + + resources = util.normalize_resources_qs_param(req.GET['resources']) + filters = { + 'resources': resources, + } + + try: + cands = rp_obj.AllocationCandidates.get_by_filters(context, filters) + except exception.ResourceClassNotFound as exc: + raise webob.exc.HTTPBadRequest( + _('Invalid resource class in resources parameter: %(error)s') % + {'error': exc}) + + response = req.response + trx_cands = _transform_allocation_candidates(cands) + json_data = jsonutils.dumps(trx_cands) + response.body = encodeutils.to_utf8(json_data) + response.content_type = 'application/json' + return response diff --git a/nova/api/openstack/placement/handlers/inventory.py b/nova/api/openstack/placement/handlers/inventory.py new file mode 100644 index 00000000000..8d819ac6ab2 --- /dev/null +++ b/nova/api/openstack/placement/handlers/inventory.py @@ -0,0 +1,469 @@ +# 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. +"""Inventory handlers for Placement API.""" + +import copy + +from oslo_db import exception as db_exc +from oslo_serialization import jsonutils +from oslo_utils import encodeutils +import webob + +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import util +from nova.api.openstack.placement import wsgi_wrapper +from nova import db +from nova import exception +from nova.i18n import _ +from nova import objects + +RESOURCE_CLASS_IDENTIFIER = "^[A-Z0-9_]+$" +BASE_INVENTORY_SCHEMA = { + "type": "object", + "properties": { + "resource_provider_generation": { + "type": "integer" + }, + "total": { + "type": "integer", + "maximum": db.MAX_INT, + "minimum": 1, + }, + "reserved": { + "type": "integer", + "maximum": db.MAX_INT, + "minimum": 0, + }, + "min_unit": { + "type": "integer", + "maximum": db.MAX_INT, + "minimum": 1 + }, + "max_unit": { + "type": "integer", + "maximum": db.MAX_INT, + "minimum": 1 + }, + "step_size": { + "type": "integer", + "maximum": db.MAX_INT, + "minimum": 1 + }, + "allocation_ratio": { + "type": "number", + "maximum": db.SQL_SP_FLOAT_MAX + }, + }, + "required": [ + "total", + "resource_provider_generation" + ], + "additionalProperties": False +} +POST_INVENTORY_SCHEMA = copy.deepcopy(BASE_INVENTORY_SCHEMA) +POST_INVENTORY_SCHEMA['properties']['resource_class'] = { + "type": "string", + "pattern": RESOURCE_CLASS_IDENTIFIER, +} +POST_INVENTORY_SCHEMA['required'].append('resource_class') +POST_INVENTORY_SCHEMA['required'].remove('resource_provider_generation') +PUT_INVENTORY_RECORD_SCHEMA = copy.deepcopy(BASE_INVENTORY_SCHEMA) +PUT_INVENTORY_RECORD_SCHEMA['required'].remove('resource_provider_generation') +PUT_INVENTORY_SCHEMA = { + "type": "object", + "properties": { + "resource_provider_generation": { + "type": "integer" + }, + "inventories": { + "type": "object", + "patternProperties": { + RESOURCE_CLASS_IDENTIFIER: PUT_INVENTORY_RECORD_SCHEMA, + } + } + }, + "required": [ + "resource_provider_generation", + "inventories" + ], + "additionalProperties": False +} + +# NOTE(cdent): We keep our own representation of inventory defaults +# and output fields, separate from the versioned object to avoid +# inadvertent API changes when the object defaults are changed. +OUTPUT_INVENTORY_FIELDS = [ + 'total', + 'reserved', + 'min_unit', + 'max_unit', + 'step_size', + 'allocation_ratio', +] +INVENTORY_DEFAULTS = { + 'reserved': 0, + 'min_unit': 1, + 'max_unit': db.MAX_INT, + 'step_size': 1, + 'allocation_ratio': 1.0 +} + + +def _extract_inventory(body, schema): + """Extract and validate inventory from JSON body.""" + data = util.extract_json(body, schema) + + inventory_data = copy.copy(INVENTORY_DEFAULTS) + inventory_data.update(data) + + return inventory_data + + +def _extract_inventories(body, schema): + """Extract and validate multiple inventories from JSON body.""" + data = util.extract_json(body, schema) + + inventories = {} + for res_class, raw_inventory in data['inventories'].items(): + inventory_data = copy.copy(INVENTORY_DEFAULTS) + inventory_data.update(raw_inventory) + inventories[res_class] = inventory_data + + data['inventories'] = inventories + return data + + +def _make_inventory_object(resource_provider, resource_class, **data): + """Single place to catch malformed Inventories.""" + # TODO(cdent): Some of the validation checks that are done here + # could be done via JSONschema (using, for example, "minimum": + # 0) for non-negative integers. It's not clear if that is + # duplication or decoupling so leaving it as this for now. + try: + inventory = objects.Inventory( + resource_provider=resource_provider, + resource_class=resource_class, **data) + except (ValueError, TypeError) as exc: + raise webob.exc.HTTPBadRequest( + _('Bad inventory %(class)s for resource provider ' + '%(rp_uuid)s: %(error)s') % {'class': resource_class, + 'rp_uuid': resource_provider.uuid, + 'error': exc}) + return inventory + + +def _send_inventories(response, resource_provider, inventories): + """Send a JSON representation of a list of inventories.""" + response.status = 200 + response.body = encodeutils.to_utf8(jsonutils.dumps( + _serialize_inventories(inventories, resource_provider.generation))) + response.content_type = 'application/json' + return response + + +def _send_inventory(response, resource_provider, inventory, status=200): + """Send a JSON representation of one single inventory.""" + response.status = status + response.body = encodeutils.to_utf8(jsonutils.dumps(_serialize_inventory( + inventory, generation=resource_provider.generation))) + response.content_type = 'application/json' + return response + + +def _serialize_inventory(inventory, generation=None): + """Turn a single inventory into a dictionary.""" + data = { + field: getattr(inventory, field) + for field in OUTPUT_INVENTORY_FIELDS + } + if generation: + data['resource_provider_generation'] = generation + return data + + +def _serialize_inventories(inventories, generation): + """Turn a list of inventories in a dict by resource class.""" + inventories_by_class = {inventory.resource_class: inventory + for inventory in inventories} + inventories_dict = {} + for resource_class, inventory in inventories_by_class.items(): + inventories_dict[resource_class] = _serialize_inventory( + inventory, generation=None) + return {'resource_provider_generation': generation, + 'inventories': inventories_dict} + + +@wsgi_wrapper.PlacementWsgify +@util.require_content('application/json') +def create_inventory(req): + """POST to create one inventory. + + On success return a 201 response, a location header pointing + to the newly created inventory and an application/json representation + of the inventory. + """ + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + data = _extract_inventory(req.body, POST_INVENTORY_SCHEMA) + resource_class = data.pop('resource_class') + + inventory = _make_inventory_object(resource_provider, + resource_class, + **data) + + try: + resource_provider.add_inventory(inventory) + except (exception.ConcurrentUpdateDetected, + db_exc.DBDuplicateEntry) as exc: + raise webob.exc.HTTPConflict( + _('Update conflict: %(error)s') % {'error': exc}) + except (exception.InvalidInventoryCapacity, + exception.NotFound) as exc: + raise webob.exc.HTTPBadRequest( + _('Unable to create inventory for resource provider ' + '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, + 'error': exc}) + + response = req.response + response.location = util.inventory_url( + req.environ, resource_provider, resource_class) + return _send_inventory(response, resource_provider, inventory, + status=201) + + +@wsgi_wrapper.PlacementWsgify +def delete_inventory(req): + """DELETE to destroy a single inventory. + + If the inventory is in use or resource provider generation is out + of sync return a 409. + + On success return a 204 and an empty body. + """ + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + resource_class = util.wsgi_path_item(req.environ, 'resource_class') + + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + try: + resource_provider.delete_inventory(resource_class) + except (exception.ConcurrentUpdateDetected, + exception.InventoryInUse) as exc: + raise webob.exc.HTTPConflict( + _('Unable to delete inventory of class %(class)s: %(error)s') % + {'class': resource_class, 'error': exc}) + except exception.NotFound as exc: + raise webob.exc.HTTPNotFound( + _('No inventory of class %(class)s found for delete: %(error)s') % + {'class': resource_class, 'error': exc}) + + response = req.response + response.status = 204 + response.content_type = None + return response + + +@wsgi_wrapper.PlacementWsgify +@util.check_accept('application/json') +def get_inventories(req): + """GET a list of inventories. + + On success return a 200 with an application/json body representing + a collection of inventories. + """ + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + try: + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + except exception.NotFound as exc: + raise webob.exc.HTTPNotFound( + _("No resource provider with uuid %(uuid)s found : %(error)s") % + {'uuid': uuid, 'error': exc}) + + inventories = objects.InventoryList.get_all_by_resource_provider_uuid( + context, resource_provider.uuid) + + return _send_inventories(req.response, resource_provider, inventories) + + +@wsgi_wrapper.PlacementWsgify +@util.check_accept('application/json') +def get_inventory(req): + """GET one inventory. + + On success return a 200 an application/json body representing one + inventory. + """ + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + resource_class = util.wsgi_path_item(req.environ, 'resource_class') + + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + inventory = objects.InventoryList.get_all_by_resource_provider_uuid( + context, resource_provider.uuid).find(resource_class) + + if not inventory: + raise webob.exc.HTTPNotFound( + _('No inventory of class %(class)s for %(rp_uuid)s') % + {'class': resource_class, 'rp_uuid': resource_provider.uuid}) + + return _send_inventory(req.response, resource_provider, inventory) + + +@wsgi_wrapper.PlacementWsgify +@util.require_content('application/json') +def set_inventories(req): + """PUT to set all inventory for a resource provider. + + Create, update and delete inventory as required to reset all + the inventory. + + If the resource generation is out of sync, return a 409. + If an inventory to be deleted is in use, return a 409. + If any inventory to be created or updated has settings which are + invalid (for example reserved exceeds capacity), return a 400. + + On success return a 200 with an application/json body representing + the inventories. + """ + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + + data = _extract_inventories(req.body, PUT_INVENTORY_SCHEMA) + if data['resource_provider_generation'] != resource_provider.generation: + raise webob.exc.HTTPConflict( + _('resource provider generation conflict')) + + inv_list = [] + for res_class, inventory_data in data['inventories'].items(): + inventory = _make_inventory_object( + resource_provider, res_class, **inventory_data) + inv_list.append(inventory) + inventories = objects.InventoryList(objects=inv_list) + + try: + resource_provider.set_inventory(inventories) + except exception.ResourceClassNotFound as exc: + raise webob.exc.HTTPBadRequest( + _('Unknown resource class in inventory for resource provider ' + '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, + 'error': exc}) + except exception.InventoryWithResourceClassNotFound as exc: + raise webob.exc.HTTPConflict( + _('Race condition detected when setting inventory. No inventory ' + 'record with resource class for resource provider ' + '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, + 'error': exc}) + except (exception.ConcurrentUpdateDetected, + exception.InventoryInUse, + db_exc.DBDuplicateEntry) as exc: + raise webob.exc.HTTPConflict( + _('update conflict: %(error)s') % {'error': exc}) + except exception.InvalidInventoryCapacity as exc: + raise webob.exc.HTTPBadRequest( + _('Unable to update inventory for resource provider ' + '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, + 'error': exc}) + + return _send_inventories(req.response, resource_provider, inventories) + + +@wsgi_wrapper.PlacementWsgify +def delete_inventories(req): + """DELETE all inventory for a resource provider. + + Delete inventory as required to reset all the inventory. + If an inventory to be deleted is in use, return a 409 Conflict. + On success return a 204 No content. + Return 405 Method Not Allowed if the wanted microversion does not match. + """ + microversion.raise_http_status_code_if_not_version(req, 405, (1, 5)) + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + + inventories = objects.InventoryList(objects=[]) + + try: + resource_provider.set_inventory(inventories) + except exception.ConcurrentUpdateDetected: + raise webob.exc.HTTPConflict( + _('Unable to delete inventory for resource provider ' + '%(rp_uuid)s because the inventory was updated by ' + 'another process. Please retry your request.') + % {'rp_uuid': resource_provider.uuid}) + except exception.InventoryInUse as ex: + # NOTE(mriedem): This message cannot change without impacting the + # nova.scheduler.client.report._RE_INV_IN_USE regex. + raise webob.exc.HTTPConflict(explanation=ex.format_message()) + + response = req.response + response.status = 204 + response.content_type = None + + return response + + +@wsgi_wrapper.PlacementWsgify +@util.require_content('application/json') +def update_inventory(req): + """PUT to update one inventory. + + If the resource generation is out of sync, return a 409. + If the inventory has settings which are invalid (for example + reserved exceeds capacity), return a 400. + + On success return a 200 with an application/json body representing + the inventory. + """ + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + resource_class = util.wsgi_path_item(req.environ, 'resource_class') + + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + + data = _extract_inventory(req.body, BASE_INVENTORY_SCHEMA) + if data['resource_provider_generation'] != resource_provider.generation: + raise webob.exc.HTTPConflict( + _('resource provider generation conflict')) + + inventory = _make_inventory_object(resource_provider, + resource_class, + **data) + + try: + resource_provider.update_inventory(inventory) + except (exception.ConcurrentUpdateDetected, + db_exc.DBDuplicateEntry) as exc: + raise webob.exc.HTTPConflict( + _('update conflict: %(error)s') % {'error': exc}) + except exception.InventoryWithResourceClassNotFound as exc: + raise webob.exc.HTTPBadRequest( + _('No inventory record with resource class for resource provider ' + '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, + 'error': exc}) + except exception.InvalidInventoryCapacity as exc: + raise webob.exc.HTTPBadRequest( + _('Unable to update inventory for resource provider ' + '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, + 'error': exc}) + + return _send_inventory(req.response, resource_provider, inventory) diff --git a/nova/api/openstack/placement/handlers/resource_class.py b/nova/api/openstack/placement/handlers/resource_class.py new file mode 100644 index 00000000000..32b56fe1aed --- /dev/null +++ b/nova/api/openstack/placement/handlers/resource_class.py @@ -0,0 +1,236 @@ +# 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. +"""Placement API handlers for resource classes.""" + +import copy + +from oslo_serialization import jsonutils +from oslo_utils import encodeutils +import webob + +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import util +from nova.api.openstack.placement import wsgi_wrapper +from nova import exception +from nova.i18n import _ +from nova import objects + + +POST_RC_SCHEMA_V1_2 = { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^CUSTOM\_[A-Z0-9_]+$", + "maxLength": 255, + }, + }, + "required": [ + "name" + ], + "additionalProperties": False, +} +PUT_RC_SCHEMA_V1_2 = copy.deepcopy(POST_RC_SCHEMA_V1_2) + + +def _serialize_links(environ, rc): + url = util.resource_class_url(environ, rc) + links = [{'rel': 'self', 'href': url}] + return links + + +def _serialize_resource_class(environ, rc): + data = { + 'name': rc.name, + 'links': _serialize_links(environ, rc) + } + return data + + +def _serialize_resource_classes(environ, rcs): + output = [] + for rc in rcs: + data = _serialize_resource_class(environ, rc) + output.append(data) + return {"resource_classes": output} + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.2') +@util.require_content('application/json') +def create_resource_class(req): + """POST to create a resource class. + + On success return a 201 response with an empty body and a location + header pointing to the newly created resource class. + """ + context = req.environ['placement.context'] + data = util.extract_json(req.body, POST_RC_SCHEMA_V1_2) + + try: + rc = objects.ResourceClass(context, name=data['name']) + rc.create() + except exception.ResourceClassExists: + raise webob.exc.HTTPConflict( + _('Conflicting resource class already exists: %(name)s') % + {'name': data['name']}) + except exception.MaxDBRetriesExceeded: + raise webob.exc.HTTPConflict( + _('Max retries of DB transaction exceeded attempting ' + 'to create resource class: %(name)s, please' + 'try again.') % + {'name': data['name']}) + + req.response.location = util.resource_class_url(req.environ, rc) + req.response.status = 201 + req.response.content_type = None + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.2') +def delete_resource_class(req): + """DELETE to destroy a single resource class. + + On success return a 204 and an empty body. + """ + name = util.wsgi_path_item(req.environ, 'name') + context = req.environ['placement.context'] + # The containing application will catch a not found here. + rc = objects.ResourceClass.get_by_name(context, name) + try: + rc.destroy() + except exception.ResourceClassCannotDeleteStandard as exc: + raise webob.exc.HTTPBadRequest( + _('Cannot delete standard resource class %(rp_name)s: %(error)s') % + {'rp_name': name, 'error': exc}) + except exception.ResourceClassInUse as exc: + raise webob.exc.HTTPConflict( + _('Unable to delete resource class %(rp_name)s: %(error)s') % + {'rp_name': name, 'error': exc}) + req.response.status = 204 + req.response.content_type = None + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.2') +@util.check_accept('application/json') +def get_resource_class(req): + """Get a single resource class. + + On success return a 200 with an application/json body representing + the resource class. + """ + name = util.wsgi_path_item(req.environ, 'name') + context = req.environ['placement.context'] + # The containing application will catch a not found here. + rc = objects.ResourceClass.get_by_name(context, name) + + req.response.body = encodeutils.to_utf8(jsonutils.dumps( + _serialize_resource_class(req.environ, rc)) + ) + req.response.content_type = 'application/json' + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.2') +@util.check_accept('application/json') +def list_resource_classes(req): + """GET a list of resource classes. + + On success return a 200 and an application/json body representing + a collection of resource classes. + """ + context = req.environ['placement.context'] + rcs = objects.ResourceClassList.get_all(context) + + response = req.response + response.body = encodeutils.to_utf8(jsonutils.dumps( + _serialize_resource_classes(req.environ, rcs)) + ) + response.content_type = 'application/json' + return response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.2', '1.6') +@util.require_content('application/json') +def update_resource_class(req): + """PUT to update a single resource class. + + On success return a 200 response with a representation of the updated + resource class. + """ + name = util.wsgi_path_item(req.environ, 'name') + context = req.environ['placement.context'] + + data = util.extract_json(req.body, PUT_RC_SCHEMA_V1_2) + + # The containing application will catch a not found here. + rc = objects.ResourceClass.get_by_name(context, name) + + rc.name = data['name'] + + try: + rc.save() + except exception.ResourceClassExists: + raise webob.exc.HTTPConflict( + _('Resource class already exists: %(name)s') % + {'name': rc.name}) + except exception.ResourceClassCannotUpdateStandard: + raise webob.exc.HTTPBadRequest( + _('Cannot update standard resource class %(rp_name)s') % + {'rp_name': name}) + + req.response.body = encodeutils.to_utf8(jsonutils.dumps( + _serialize_resource_class(req.environ, rc)) + ) + req.response.status = 200 + req.response.content_type = 'application/json' + return req.response + + +@wsgi_wrapper.PlacementWsgify # noqa +@microversion.version_handler('1.7') +def update_resource_class(req): + """PUT to create or validate the existence of single resource class. + + On a successful create return 201. Return 204 if the class already + exists. If the resource class is not a custom resource class, return + a 400. 409 might be a better choice, but 400 aligns with previous code. + """ + name = util.wsgi_path_item(req.environ, 'name') + context = req.environ['placement.context'] + + # Use JSON validation to validation resource class name. + util.extract_json('{"name": "%s"}' % name, PUT_RC_SCHEMA_V1_2) + + status = 204 + try: + rc = objects.ResourceClass.get_by_name(context, name) + except exception.NotFound: + try: + rc = objects.ResourceClass(context, name=name) + rc.create() + status = 201 + # We will not see ResourceClassCannotUpdateStandard because + # that was already caught when validating the {name}. + except exception.ResourceClassExists: + # Someone just now created the class, so stick with 204 + pass + + req.response.status = status + req.response.content_type = None + req.response.location = util.resource_class_url(req.environ, rc) + return req.response diff --git a/nova/api/openstack/placement/handlers/resource_provider.py b/nova/api/openstack/placement/handlers/resource_provider.py new file mode 100644 index 00000000000..d9636b102ea --- /dev/null +++ b/nova/api/openstack/placement/handlers/resource_provider.py @@ -0,0 +1,286 @@ +# 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. +"""Placement API handlers for resource providers.""" + +import copy + +from oslo_db import exception as db_exc +from oslo_serialization import jsonutils +from oslo_utils import encodeutils +from oslo_utils import uuidutils +import webob + +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import util +from nova.api.openstack.placement import wsgi_wrapper +from nova import exception +from nova.i18n import _ +from nova import objects + + +POST_RESOURCE_PROVIDER_SCHEMA = { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 200 + }, + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "name" + ], + "additionalProperties": False, +} +# Remove uuid to create the schema for PUTting a resource provider +PUT_RESOURCE_PROVIDER_SCHEMA = copy.deepcopy(POST_RESOURCE_PROVIDER_SCHEMA) +PUT_RESOURCE_PROVIDER_SCHEMA['properties'].pop('uuid') + +# Represents the allowed query string parameters to the GET /resource_providers +# API call +GET_RPS_SCHEMA_1_0 = { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "additionalProperties": False, +} + +# Placement API microversion 1.3 adds support for a member_of attribute +GET_RPS_SCHEMA_1_3 = copy.deepcopy(GET_RPS_SCHEMA_1_0) +GET_RPS_SCHEMA_1_3['properties']['member_of'] = { + "type": "string" +} + +# Placement API microversion 1.4 adds support for requesting resource providers +# having some set of capacity for some resources. The query string is a +# comma-delimited set of "$RESOURCE_CLASS_NAME:$AMOUNT" strings. The validation +# of the string is left up to the helper code in the +# normalize_resources_qs_param() function. +GET_RPS_SCHEMA_1_4 = copy.deepcopy(GET_RPS_SCHEMA_1_3) +GET_RPS_SCHEMA_1_4['properties']['resources'] = { + "type": "string" +} + + +def _serialize_links(environ, resource_provider): + url = util.resource_provider_url(environ, resource_provider) + links = [{'rel': 'self', 'href': url}] + rel_types = ['inventories', 'usages'] + want_version = environ[microversion.MICROVERSION_ENVIRON] + if want_version >= (1, 1): + rel_types.append('aggregates') + if want_version >= (1, 6): + rel_types.append('traits') + for rel in rel_types: + links.append({'rel': rel, 'href': '%s/%s' % (url, rel)}) + return links + + +def _serialize_provider(environ, resource_provider): + data = { + 'uuid': resource_provider.uuid, + 'name': resource_provider.name, + 'generation': resource_provider.generation, + 'links': _serialize_links(environ, resource_provider) + } + return data + + +def _serialize_providers(environ, resource_providers): + output = [] + for provider in resource_providers: + provider_data = _serialize_provider(environ, provider) + output.append(provider_data) + return {"resource_providers": output} + + +@wsgi_wrapper.PlacementWsgify +@util.require_content('application/json') +def create_resource_provider(req): + """POST to create a resource provider. + + On success return a 201 response with an empty body and a location + header pointing to the newly created resource provider. + """ + context = req.environ['placement.context'] + data = util.extract_json(req.body, POST_RESOURCE_PROVIDER_SCHEMA) + + try: + uuid = data.get('uuid', uuidutils.generate_uuid()) + resource_provider = objects.ResourceProvider( + context, name=data['name'], uuid=uuid) + resource_provider.create() + except db_exc.DBDuplicateEntry as exc: + raise webob.exc.HTTPConflict( + _('Conflicting resource provider %(name)s already exists.') % + {'name': data['name']}) + except exception.ObjectActionError as exc: + raise webob.exc.HTTPBadRequest( + _('Unable to create resource provider %(rp_uuid)s: %(error)s') % + {'rp_uuid': uuid, 'error': exc}) + + req.response.location = util.resource_provider_url( + req.environ, resource_provider) + req.response.status = 201 + req.response.content_type = None + return req.response + + +@wsgi_wrapper.PlacementWsgify +def delete_resource_provider(req): + """DELETE to destroy a single resource provider. + + On success return a 204 and an empty body. + """ + uuid = util.wsgi_path_item(req.environ, 'uuid') + context = req.environ['placement.context'] + # The containing application will catch a not found here. + try: + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + resource_provider.destroy() + except exception.ResourceProviderInUse as exc: + raise webob.exc.HTTPConflict( + _('Unable to delete resource provider %(rp_uuid)s: %(error)s') % + {'rp_uuid': uuid, 'error': exc}) + except exception.NotFound as exc: + raise webob.exc.HTTPNotFound( + _("No resource provider with uuid %s found for delete") % uuid) + req.response.status = 204 + req.response.content_type = None + return req.response + + +@wsgi_wrapper.PlacementWsgify +@util.check_accept('application/json') +def get_resource_provider(req): + """Get a single resource provider. + + On success return a 200 with an application/json body representing + the resource provider. + """ + uuid = util.wsgi_path_item(req.environ, 'uuid') + # The containing application will catch a not found here. + context = req.environ['placement.context'] + + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + + req.response.body = encodeutils.to_utf8(jsonutils.dumps( + _serialize_provider(req.environ, resource_provider))) + req.response.content_type = 'application/json' + return req.response + + +@wsgi_wrapper.PlacementWsgify +@util.check_accept('application/json') +def list_resource_providers(req): + """GET a list of resource providers. + + On success return a 200 and an application/json body representing + a collection of resource providers. + """ + context = req.environ['placement.context'] + want_version = req.environ[microversion.MICROVERSION_ENVIRON] + + schema = GET_RPS_SCHEMA_1_0 + if want_version == (1, 3): + schema = GET_RPS_SCHEMA_1_3 + if want_version >= (1, 4): + schema = GET_RPS_SCHEMA_1_4 + + util.validate_query_params(req, schema) + + filters = {} + for attr in ['uuid', 'name', 'member_of']: + if attr in req.GET: + value = req.GET[attr] + # special case member_of to always make its value a + # list, either by accepting the single value, or if it + # starts with 'in:' splitting on ','. + # NOTE(cdent): This will all change when we start using + # JSONSchema validation of query params. + if attr == 'member_of': + if value.startswith('in:'): + value = value[3:].split(',') + else: + value = [value] + # Make sure the values are actually UUIDs. + for aggr_uuid in value: + if not uuidutils.is_uuid_like(aggr_uuid): + raise webob.exc.HTTPBadRequest( + _('Invalid uuid value: %(uuid)s') % + {'uuid': aggr_uuid}) + filters[attr] = value + if 'resources' in req.GET: + resources = util.normalize_resources_qs_param(req.GET['resources']) + filters['resources'] = resources + try: + resource_providers = objects.ResourceProviderList.get_all_by_filters( + context, filters) + except exception.ResourceClassNotFound as exc: + raise webob.exc.HTTPBadRequest( + _('Invalid resource class in resources parameter: %(error)s') % + {'error': exc}) + + response = req.response + response.body = encodeutils.to_utf8( + jsonutils.dumps(_serialize_providers(req.environ, resource_providers))) + response.content_type = 'application/json' + return response + + +@wsgi_wrapper.PlacementWsgify +@util.require_content('application/json') +def update_resource_provider(req): + """PUT to update a single resource provider. + + On success return a 200 response with a representation of the updated + resource provider. + """ + uuid = util.wsgi_path_item(req.environ, 'uuid') + context = req.environ['placement.context'] + + # The containing application will catch a not found here. + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + + data = util.extract_json(req.body, PUT_RESOURCE_PROVIDER_SCHEMA) + + resource_provider.name = data['name'] + + try: + resource_provider.save() + except db_exc.DBDuplicateEntry as exc: + raise webob.exc.HTTPConflict( + _('Conflicting resource provider %(name)s already exists.') % + {'name': data['name']}) + except exception.ObjectActionError as exc: + raise webob.exc.HTTPBadRequest( + _('Unable to save resource provider %(rp_uuid)s: %(error)s') % + {'rp_uuid': uuid, 'error': exc}) + + req.response.body = encodeutils.to_utf8(jsonutils.dumps( + _serialize_provider(req.environ, resource_provider))) + req.response.status = 200 + req.response.content_type = 'application/json' + return req.response diff --git a/nova/api/openstack/placement/handlers/root.py b/nova/api/openstack/placement/handlers/root.py new file mode 100644 index 00000000000..9b5b1bb948d --- /dev/null +++ b/nova/api/openstack/placement/handlers/root.py @@ -0,0 +1,37 @@ +# 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. +"""Handler for the root of the Placement API.""" + +from oslo_serialization import jsonutils +from oslo_utils import encodeutils + + +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import wsgi_wrapper + + +@wsgi_wrapper.PlacementWsgify +def home(req): + min_version = microversion.min_version_string() + max_version = microversion.max_version_string() + # NOTE(cdent): As sections of the api are added, links can be + # added to this output to align with the guidelines at + # http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html#version-discovery + version_data = { + 'id': 'v%s' % min_version, + 'max_version': max_version, + 'min_version': min_version, + } + version_json = jsonutils.dumps({'versions': [version_data]}) + req.response.body = encodeutils.to_utf8(version_json) + req.response.content_type = 'application/json' + return req.response diff --git a/nova/api/openstack/placement/handlers/trait.py b/nova/api/openstack/placement/handlers/trait.py new file mode 100644 index 00000000000..4cdfc0e352f --- /dev/null +++ b/nova/api/openstack/placement/handlers/trait.py @@ -0,0 +1,265 @@ +# 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. +"""Traits handlers for Placement API.""" + +import copy + +import jsonschema +from oslo_serialization import jsonutils +from oslo_utils import encodeutils +import webob + +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import util +from nova.api.openstack.placement import wsgi_wrapper +from nova import exception +from nova.i18n import _ +from nova import objects + +TRAIT = { + "type": "string", + 'minLength': 1, 'maxLength': 255, +} + +CUSTOM_TRAIT = copy.deepcopy(TRAIT) +CUSTOM_TRAIT.update({"pattern": "^CUSTOM_[A-Z0-9_]+$"}) + +PUT_TRAITS_SCHEMA = { + "type": "object", + "properties": { + "traits": { + "type": "array", + "items": CUSTOM_TRAIT, + } + }, + 'required': ['traits'], + 'additionalProperties': False +} + +SET_TRAITS_FOR_RP_SCHEMA = copy.deepcopy(PUT_TRAITS_SCHEMA) +SET_TRAITS_FOR_RP_SCHEMA['properties']['traits']['items'] = TRAIT +SET_TRAITS_FOR_RP_SCHEMA['properties'][ + 'resource_provider_generation'] = {'type': 'integer'} +SET_TRAITS_FOR_RP_SCHEMA['required'].append('resource_provider_generation') + + +LIST_TRAIT_SCHEMA = { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "associated": { + "type": "string", + } + }, + "additionalProperties": False +} + + +def _normalize_traits_qs_param(qs): + try: + op, value = qs.split(':', 1) + except ValueError: + msg = _('Badly formatted name parameter. Expected name query string ' + 'parameter in form: ' + '?name=[in|startswith]:[name1,name2|prefix]. Got: "%s"') + msg = msg % qs + raise webob.exc.HTTPBadRequest(msg) + + filters = {} + if op == 'in': + filters['name_in'] = value.split(',') + elif op == 'startswith': + filters['prefix'] = value + + return filters + + +def _serialize_traits(traits): + return {'traits': [trait.name for trait in traits]} + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.6') +def put_trait(req): + context = req.environ['placement.context'] + name = util.wsgi_path_item(req.environ, 'name') + + try: + jsonschema.validate(name, CUSTOM_TRAIT) + except jsonschema.ValidationError: + raise webob.exc.HTTPBadRequest( + _('The trait is invalid. A valid trait must be no longer than ' + '255 characters, start with the prefix "CUSTOM_" and use ' + 'following characters: "A"-"Z", "0"-"9" and "_"')) + + trait = objects.Trait(context) + trait.name = name + + try: + trait.create() + req.response.status = 201 + except exception.TraitExists: + req.response.status = 204 + + req.response.content_type = None + req.response.location = util.trait_url(req.environ, trait) + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.6') +def get_trait(req): + context = req.environ['placement.context'] + name = util.wsgi_path_item(req.environ, 'name') + + try: + objects.Trait.get_by_name(context, name) + except exception.TraitNotFound as ex: + raise webob.exc.HTTPNotFound( + explanation=ex.format_message()) + + req.response.status = 204 + req.response.content_type = None + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.6') +def delete_trait(req): + context = req.environ['placement.context'] + name = util.wsgi_path_item(req.environ, 'name') + + try: + trait = objects.Trait.get_by_name(context, name) + trait.destroy() + except exception.TraitNotFound as ex: + raise webob.exc.HTTPNotFound( + explanation=ex.format_message()) + except exception.TraitCannotDeleteStandard as ex: + raise webob.exc.HTTPBadRequest( + explanation=ex.format_message()) + except exception.TraitInUse as ex: + raise webob.exc.HTTPConflict( + explanation=ex.format_message()) + + req.response.status = 204 + req.response.content_type = None + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.6') +@util.check_accept('application/json') +def list_traits(req): + context = req.environ['placement.context'] + filters = {} + + try: + jsonschema.validate(dict(req.GET), LIST_TRAIT_SCHEMA, + format_checker=jsonschema.FormatChecker()) + except jsonschema.ValidationError as exc: + raise webob.exc.HTTPBadRequest( + _('Invalid query string parameters: %(exc)s') % + {'exc': exc}) + + if 'name' in req.GET: + filters = _normalize_traits_qs_param(req.GET['name']) + if 'associated' in req.GET: + if req.GET['associated'].lower() not in ['true', 'false']: + raise webob.exc.HTTPBadRequest( + explanation=_('The query parameter "associated" only accepts ' + '"true" or "false"')) + filters['associated'] = ( + True if req.GET['associated'].lower() == 'true' else False) + + traits = objects.TraitList.get_all(context, filters) + req.response.status = 200 + req.response.body = encodeutils.to_utf8( + jsonutils.dumps(_serialize_traits(traits))) + req.response.content_type = 'application/json' + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.6') +@util.check_accept('application/json') +def list_traits_for_resource_provider(req): + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + + response_body = _serialize_traits(resource_provider.get_traits()) + response_body[ + "resource_provider_generation"] = resource_provider.generation + + req.response.status = 200 + req.response.body = encodeutils.to_utf8(jsonutils.dumps(response_body)) + req.response.content_type = 'application/json' + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.6') +@util.require_content('application/json') +def update_traits_for_resource_provider(req): + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + data = util.extract_json(req.body, SET_TRAITS_FOR_RP_SCHEMA) + rp_gen = data['resource_provider_generation'] + traits = data['traits'] + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + + if resource_provider.generation != rp_gen: + raise webob.exc.HTTPConflict( + _("Resource provider's generation already changed. Please update " + "the generation and try again."), + json_formatter=util.json_error_formatter) + + trait_objs = objects.TraitList.get_all( + context, filters={'name_in': traits}) + traits_name = set([obj.name for obj in trait_objs]) + non_existed_trait = set(traits) - set(traits_name) + if non_existed_trait: + raise webob.exc.HTTPBadRequest( + _("No such trait %s") % ', '.join(non_existed_trait)) + + resource_provider.set_traits(trait_objs) + + response_body = _serialize_traits(trait_objs) + response_body[ + 'resource_provider_generation'] = resource_provider.generation + req.response.status = 200 + req.response.body = encodeutils.to_utf8(jsonutils.dumps(response_body)) + req.response.content_type = 'application/json' + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.6') +def delete_traits_for_resource_provider(req): + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + + resource_provider = objects.ResourceProvider.get_by_uuid(context, uuid) + try: + resource_provider.set_traits(objects.TraitList(objects=[])) + except exception.ConcurrentUpdateDetected as e: + raise webob.exc.HTTPConflict(explanation=e.format_message()) + + req.response.status = 204 + req.response.content_type = None + return req.response diff --git a/nova/api/openstack/placement/handlers/usage.py b/nova/api/openstack/placement/handlers/usage.py new file mode 100644 index 00000000000..68c3eb39895 --- /dev/null +++ b/nova/api/openstack/placement/handlers/usage.py @@ -0,0 +1,118 @@ +# 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. +"""Placement API handlers for usage information.""" + +from oslo_serialization import jsonutils +from oslo_utils import encodeutils +import webob + +from nova.api.openstack.placement import microversion +from nova.api.openstack.placement import util +from nova.api.openstack.placement import wsgi_wrapper +from nova import exception +from nova.i18n import _ +from nova import objects + + +# Represents the allowed query string parameters to GET /usages +GET_USAGES_SCHEMA_1_9 = { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "minLength": 1, + "maxLength": 255, + }, + "user_id": { + "type": "string", + "minLength": 1, + "maxLength": 255, + }, + }, + "required": [ + "project_id" + ], + "additionalProperties": False, +} + + +def _serialize_usages(resource_provider, usage): + usage_dict = {resource.resource_class: resource.usage + for resource in usage} + return {'resource_provider_generation': resource_provider.generation, + 'usages': usage_dict} + + +@wsgi_wrapper.PlacementWsgify +@util.check_accept('application/json') +def list_usages(req): + """GET a dictionary of resource provider usage by resource class. + + If the resource provider does not exist return a 404. + + On success return a 200 with an application/json representation of + the usage dictionary. + """ + context = req.environ['placement.context'] + uuid = util.wsgi_path_item(req.environ, 'uuid') + + # Resource provider object needed for two things: If it is + # NotFound we'll get a 404 here, which needs to happen because + # get_all_by_resource_provider_uuid can return an empty list. + # It is also needed for the generation, used in the outgoing + # representation. + try: + resource_provider = objects.ResourceProvider.get_by_uuid( + context, uuid) + except exception.NotFound as exc: + raise webob.exc.HTTPNotFound( + _("No resource provider with uuid %(uuid)s found: %(error)s") % + {'uuid': uuid, 'error': exc}) + + usage = objects.UsageList.get_all_by_resource_provider_uuid( + context, uuid) + + response = req.response + response.body = encodeutils.to_utf8(jsonutils.dumps( + _serialize_usages(resource_provider, usage))) + req.response.content_type = 'application/json' + return req.response + + +@wsgi_wrapper.PlacementWsgify +@microversion.version_handler('1.9') +@util.check_accept('application/json') +def get_total_usages(req): + """GET the sum of usages for a project or a project/user. + + On success return a 200 and an application/json body representing the + sum/total of usages. + Return 404 Not Found if the wanted microversion does not match. + """ + context = req.environ['placement.context'] + + schema = GET_USAGES_SCHEMA_1_9 + + util.validate_query_params(req, schema) + + project_id = req.GET.get('project_id') + user_id = req.GET.get('user_id') + + usages = objects.UsageList.get_all_by_project_user(context, project_id, + user_id=user_id) + + response = req.response + usages_dict = {'usages': {resource.resource_class: resource.usage + for resource in usages}} + response.body = encodeutils.to_utf8(jsonutils.dumps(usages_dict)) + req.response.content_type = 'application/json' + return req.response diff --git a/nova/api/openstack/placement/microversion.py b/nova/api/openstack/placement/microversion.py new file mode 100644 index 00000000000..41406293609 --- /dev/null +++ b/nova/api/openstack/placement/microversion.py @@ -0,0 +1,274 @@ +# 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. +"""Microversion handling.""" + +# NOTE(cdent): This code is taken from enamel: +# https://github.com/jaypipes/enamel and was the original source of +# the code now used in microversion_parse library. + +import collections +import inspect + +import microversion_parse +import webob + +# NOTE(cdent): avoid cyclical import conflict between util and +# microversion +import nova.api.openstack.placement.util +from nova.i18n import _ + + +SERVICE_TYPE = 'placement' +MICROVERSION_ENVIRON = '%s.microversion' % SERVICE_TYPE +VERSIONED_METHODS = collections.defaultdict(list) + +# The Canonical Version List +VERSIONS = [ + '1.0', + '1.1', # initial support for aggregate.get_aggregates and set_aggregates + '1.2', # Adds /resource_classes resource endpoint + '1.3', # Adds 'member_of' query parameter to get resource providers + # that are members of any of the listed aggregates + '1.4', # Adds resources query string parameter in GET /resource_providers + '1.5', # Adds DELETE /resource_providers/{uuid}/inventories + '1.6', # Adds /traits and /resource_providers{uuid}/traits resource + # endpoints + '1.7', # PUT /resource_classes/{name} is bodiless create or update + '1.8', # Adds 'project_id' and 'user_id' required request parameters to + # PUT /allocations + '1.9', # Adds GET /usages + '1.10', # Adds GET /allocation_candidates resource endpoint +] + + +def max_version_string(): + return VERSIONS[-1] + + +def min_version_string(): + return VERSIONS[0] + + +def parse_version_string(version_string): + """Turn a version string into a Version + + :param version_string: A string of two numerals, X.Y, or 'latest' + :returns: a Version + :raises: TypeError + """ + if version_string == 'latest': + version_string = max_version_string() + try: + # The combination of int and a limited split with the + # named tuple means that this incantation will raise + # ValueError or TypeError when the incoming data is + # poorly formed but will, however, naturally adapt to + # extraneous whitespace. + return Version(*(int(value) for value + in version_string.split('.', 1))) + except (ValueError, TypeError) as exc: + raise TypeError('invalid version string: %s; %s' % ( + version_string, exc)) + + +def raise_http_status_code_if_not_version(req, status_code, min_version, + max_version=None): + """Utility to raise a http status code if the wanted microversion does not + match. + + :param req: The HTTP request for the placement api + :param status_code: HTTP status code (integer value) to be raised + :param min_version: Minimum placement microversion level + :param max_version: Maximum placement microversion level + :returns: None + :raises: HTTP status code if the specified microversion does not match + :raises: KeyError if status_code is not a valid HTTP status code + """ + if not isinstance(min_version, tuple): + min_version = parse_version_string(min_version) + if max_version and not isinstance(max_version, tuple): + max_version = parse_version_string(max_version) + want_version = req.environ[MICROVERSION_ENVIRON] + if not want_version.matches(min_version, max_version): + raise webob.exc.status_map[status_code] + + +class MicroversionMiddleware(object): + """WSGI middleware for getting microversion info.""" + + def __init__(self, application): + self.application = application + + @webob.dec.wsgify + def __call__(self, req): + util = nova.api.openstack.placement.util + try: + microversion = extract_version(req.headers) + except ValueError as exc: + raise webob.exc.HTTPNotAcceptable( + _('Invalid microversion: %(error)s') % {'error': exc}, + json_formatter=util.json_error_formatter) + except TypeError as exc: + raise webob.exc.HTTPBadRequest( + _('Invalid microversion: %(error)s') % {'error': exc}, + json_formatter=util.json_error_formatter) + + req.environ[MICROVERSION_ENVIRON] = microversion + microversion_header = '%s %s' % (SERVICE_TYPE, microversion) + + try: + response = req.get_response(self.application) + except webob.exc.HTTPError as exc: + # If there was an error in the application we still need + # to send the microversion header, so add the header and + # re-raise the exception. + exc.headers.add(Version.HEADER, microversion_header) + raise exc + + response.headers.add(Version.HEADER, microversion_header) + response.headers.add('vary', Version.HEADER) + return response + + +class Version(collections.namedtuple('Version', 'major minor')): + """A namedtuple containing major and minor values. + + Since it is a tuple is automatically comparable. + """ + + HEADER = 'OpenStack-API-Version' + + MIN_VERSION = None + MAX_VERSION = None + + def __str__(self): + return '%s.%s' % (self.major, self.minor) + + @property + def max_version(self): + if not self.MAX_VERSION: + self.MAX_VERSION = parse_version_string(max_version_string()) + return self.MAX_VERSION + + @property + def min_version(self): + if not self.MIN_VERSION: + self.MIN_VERSION = parse_version_string(min_version_string()) + return self.MIN_VERSION + + def matches(self, min_version=None, max_version=None): + if min_version is None: + min_version = self.min_version + if max_version is None: + max_version = self.max_version + return min_version <= self <= max_version + + +def extract_version(headers): + """Extract the microversion from Version.HEADER + + There may be multiple headers and some which don't match our + service. + """ + found_version = microversion_parse.get_version(headers, + service_type=SERVICE_TYPE) + + version_string = found_version or min_version_string() + request_version = parse_version_string(version_string) + # We need a version that is in VERSION and within MIX and MAX. + # This gives us the option to administratively disable a + # version if we really need to. + if (str(request_version) in VERSIONS and request_version.matches()): + return request_version + raise ValueError('Unacceptable version header: %s' % version_string) + + +# From twisted +# https://github.com/twisted/twisted/blob/trunk/twisted/python/deprecate.py +def _fully_qualified_name(obj): + """Return the fully qualified name of a module, class, method or function. + + Classes and functions need to be module level ones to be correctly + qualified. + """ + try: + name = obj.__qualname__ + except AttributeError: + name = obj.__name__ + + if inspect.isclass(obj) or inspect.isfunction(obj): + moduleName = obj.__module__ + return "%s.%s" % (moduleName, name) + elif inspect.ismethod(obj): + try: + cls = obj.im_class + except AttributeError: + # Python 3 eliminates im_class, substitutes __module__ and + # __qualname__ to provide similar information. + return "%s.%s" % (obj.__module__, obj.__qualname__) + else: + className = _fully_qualified_name(cls) + return "%s.%s" % (className, name) + return name + + +def _find_method(f, version): + """Look in VERSIONED_METHODS for method with right name matching version. + + If no match is found raise a 404. + """ + qualified_name = _fully_qualified_name(f) + # A KeyError shouldn't be possible here, but let's be robust + # just in case. + method_list = VERSIONED_METHODS.get(qualified_name, []) + for min_version, max_version, func in method_list: + if min_version <= version <= max_version: + return func + + raise webob.exc.HTTPNotFound() + + +def version_handler(min_ver, max_ver=None): + """Decorator for versioning API methods. + + Add as a decorator to a placement API handler to constrain + the microversions at which it will run. Add after the + ``wsgify`` decorator. + + This does not check for version intersections. That's the + domain of tests. + + :param min_ver: A string of two numerals, X.Y indicating the + minimum version allowed for the decorated method. + :param min_ver: A string of two numerals, X.Y, indicating the + maximum version allowed for the decorated method. + """ + def decorator(f): + min_version = parse_version_string(min_ver) + if max_ver: + max_version = parse_version_string(max_ver) + else: + max_version = parse_version_string(max_version_string()) + qualified_name = _fully_qualified_name(f) + VERSIONED_METHODS[qualified_name].append( + (min_version, max_version, f)) + + def decorated_func(req, *args, **kwargs): + version = req.environ[MICROVERSION_ENVIRON] + return _find_method(f, version)(req, *args, **kwargs) + + # Sort highest min version to beginning of list. + VERSIONED_METHODS[qualified_name].sort(key=lambda x: x[0], + reverse=True) + return decorated_func + return decorator diff --git a/nova/api/openstack/placement/policy.py b/nova/api/openstack/placement/policy.py new file mode 100644 index 00000000000..fd856635b68 --- /dev/null +++ b/nova/api/openstack/placement/policy.py @@ -0,0 +1,75 @@ +# 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. +"""Policy Enforcement for placement API.""" + +from oslo_config import cfg +from oslo_log import log as logging +from oslo_policy import policy +from oslo_serialization import jsonutils + + +CONF = cfg.CONF +LOG = logging.getLogger(__name__) +_ENFORCER_PLACEMENT = None + + +def placement_init(): + """Init an Enforcer class for placement policy. + + This method uses a different list of policies than other parts of Nova. + This is done to facilitate a split out of the placement service later. + """ + global _ENFORCER_PLACEMENT + if not _ENFORCER_PLACEMENT: + # TODO(cdent): Using is_admin everywhere (except /) is + # insufficiently flexible for future use case but is + # convenient for initial exploration. We will need to + # determine how to manage authorization/policy and + # implement that, probably per handler. + rules = policy.Rules.load(jsonutils.dumps({'placement': 'role:admin'})) + # Enforcer is initialized so that the above rule is loaded in and no + # policy file is read. + # TODO(alaski): Register a default rule rather than loading it in like + # this. That requires that a policy file is specified to be read. When + # this is split out such that a placement policy file makes sense then + # change to rule registration. + _ENFORCER_PLACEMENT = policy.Enforcer(CONF, rules=rules, + use_conf=False) + + +def placement_authorize(context, action, target=None): + """Verifies that the action is valid on the target in this context. + + :param context: RequestContext object + :param action: string representing the action to be checked + :param target: dictionary representing the object of the action + for object creation this should be a dictionary representing the + location of the object e.g. ``{'project_id': context.project_id}`` + + :return: returns a non-False value (not necessarily "True") if + authorized, and the exact value False if not authorized. + """ + placement_init() + if target is None: + target = {'project_id': context.project_id, + 'user_id': context.user_id} + credentials = context.to_policy_values() + # TODO(alaski): Change this to use authorize() when rules are registered. + # noqa the following line because a hacking check disallows using enforce. + result = _ENFORCER_PLACEMENT.enforce(action, target, credentials, + do_raise=False, exc=None, + action=action) + if result is False: + LOG.debug('Policy check for %(action)s failed with credentials ' + '%(credentials)s', + {'action': action, 'credentials': credentials}) + return result diff --git a/nova/api/openstack/placement/requestlog.py b/nova/api/openstack/placement/requestlog.py new file mode 100644 index 00000000000..ad071d78fc6 --- /dev/null +++ b/nova/api/openstack/placement/requestlog.py @@ -0,0 +1,82 @@ +# 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. +"""Simple middleware for request logging.""" + +from oslo_log import log as logging + +from nova.api.openstack.placement import microversion + +LOG = logging.getLogger(__name__) + + +class RequestLog(object): + """WSGI Middleware to write a simple request log to. + + Borrowed from Paste Translogger + """ + + format = ('%(REMOTE_ADDR)s "%(REQUEST_METHOD)s %(REQUEST_URI)s" ' + 'status: %(status)s len: %(bytes)s ' + 'microversion: %(microversion)s') + + def __init__(self, application): + self.application = application + + def __call__(self, environ, start_response): + LOG.debug('Starting request: %s "%s %s"', + environ['REMOTE_ADDR'], environ['REQUEST_METHOD'], + self._get_uri(environ)) + if LOG.isEnabledFor(logging.INFO): + return self._log_app(environ, start_response) + else: + return self.application(environ, start_response) + + @staticmethod + def _get_uri(environ): + req_uri = (environ.get('SCRIPT_NAME', '') + + environ.get('PATH_INFO', '')) + if environ.get('QUERY_STRING'): + req_uri += '?' + environ['QUERY_STRING'] + return req_uri + + def _log_app(self, environ, start_response): + req_uri = self._get_uri(environ) + + def replacement_start_response(status, headers, exc_info=None): + """We need to gaze at the content-length, if set, to + write log info. + """ + size = None + for name, value in headers: + if name.lower() == 'content-length': + size = value + self.write_log(environ, req_uri, status, size) + return start_response(status, headers, exc_info) + + return self.application(environ, replacement_start_response) + + def write_log(self, environ, req_uri, status, size): + """Write the log info out in a formatted form to ``LOG.info``. + """ + if size is None: + size = '-' + log_format = { + 'REMOTE_ADDR': environ.get('REMOTE_ADDR', '-'), + 'REQUEST_METHOD': environ['REQUEST_METHOD'], + 'REQUEST_URI': req_uri, + 'status': status.split(None, 1)[0], + 'bytes': size, + 'microversion': environ.get( + microversion.MICROVERSION_ENVIRON, '-'), + } + LOG.info(self.format, log_format) diff --git a/nova/api/openstack/placement/rest_api_version_history.rst b/nova/api/openstack/placement/rest_api_version_history.rst new file mode 100644 index 00000000000..4c832f37a4c --- /dev/null +++ b/nova/api/openstack/placement/rest_api_version_history.rst @@ -0,0 +1,154 @@ +REST API Version History +~~~~~~~~~~~~~~~~~~~~~~~~ + +This documents the changes made to the REST API with every +microversion change. The description for each version should be a +verbose one which has enough information to be suitable for use in +user documentation. + +1.0 (Maximum in Newton) +----------------------- + +This is the initial version of the placement REST API that was released in +Nova 14.0.0 (Newton). This contains the following routes: + +* /resource_providers +* /resource_providers/allocations +* /resource_providers/inventories +* /resource_providers/usages +* /allocations + +1.1 Resource provider aggregates +-------------------------------- + +The 1.1 version adds support for associating aggregates with +resource providers with ``GET`` and ``PUT`` methods on one new +route: + +* /resource_providers/{uuid}/aggregates + +1.2 Custom resource classes +--------------------------- + +Placement API version 1.2 adds basic operations allowing an admin to create, +list and delete custom resource classes. + +The following new routes are added: + +* GET /resource_classes: return all resource classes +* POST /resource_classes: create a new custom resource class +* PUT /resource_classes/{name}: update name of custom resource class +* DELETE /resource_classes/{name}: deletes a custom resource class +* GET /resource_classes/{name}: get a single resource class + +Custom resource classes must begin with the prefix "CUSTOM\_" and contain only +the letters A through Z, the numbers 0 through 9 and the underscore "\_" +character. + +1.3 member_of query parameter +----------------------------- + +Version 1.3 adds support for listing resource providers that are members of +any of the list of aggregates provided using a ``member_of`` query parameter: + +* /resource_providers?member_of=in:{agg1_uuid},{agg2_uuid},{agg3_uuid} + +1.4 Filter resource providers by requested resource capacity (Maximum in Ocata) +------------------------------------------------------------------------------- + +The 1.4 version adds support for querying resource providers that have the +ability to serve a requested set of resources. A new "resources" query string +parameter is now accepted to the `GET /resource_providers` API call. This +parameter indicates the requested amounts of various resources that a provider +must have the capacity to serve. The "resources" query string parameter takes +the form: + +``?resources=$RESOURCE_CLASS_NAME:$AMOUNT,$RESOURCE_CLASS_NAME:$AMOUNT`` + +For instance, if the user wishes to see resource providers that can service a +request for 2 vCPUs, 1024 MB of RAM and 50 GB of disk space, the user can issue +a request to: + +`GET /resource_providers?resources=VCPU:2,MEMORY_MB:1024,DISK_GB:50` + +If the resource class does not exist, then it will return a HTTP 400. + +.. note:: The resources filtering is also based on the `min_unit`, `max_unit` + and `step_size` of the inventory record. For example, if the `max_unit` is + 512 for the DISK_GB inventory for a particular resource provider and a + GET request is made for `DISK_GB:1024`, that resource provider will not be + returned. The `min_unit` is the minimum amount of resource that can be + requested for a given inventory and resource provider. The `step_size` is + the increment of resource that can be requested for a given resource on a + given provider. + +1.5 DELETE all inventory for a resource provider +------------------------------------------------ + +Placement API version 1.5 adds DELETE method for deleting all inventory for a +resource provider. The following new method is supported: + +* DELETE /resource_providers/{uuid}/inventories + +1.6 Traits API +-------------- + +The 1.6 version adds basic operations allowing an admin to create, list, and +delete custom traits, also adds basic operations allowing an admin to attach +traits to a resource provider. + +The following new routes are added: + +* GET /traits: Returns all resource classes. +* PUT /traits/{name}: To insert a single custom trait. +* GET /traits/{name}: To check if a trait name exists. +* DELETE /traits/{name}: To delete the specified trait. +* GET /resource_providers/{uuid}/traits: a list of traits associated + with a specific resource provider +* PUT /resource_providers/{uuid}/traits: Set all the traits for a + specific resource provider +* DELETE /resource_providers/{uuid}/traits: Remove any existing trait + associations for a specific resource provider + +Custom traits must begin with the prefix "CUSTOM\_" and contain only +the letters A through Z, the numbers 0 through 9 and the underscore "\_" +character. + +1.7 Idempotent PUT /resource_classes/{name} +------------------------------------------- + +The 1.7 version changes handling of `PUT /resource_classes/{name}` to be a +create or verification of the resource class with `{name}`. If the resource +class is a custom resource class and does not already exist it will be created +and a ``201`` response code returned. If the class already exists the response +code will be ``204``. This makes it possible to check or create a resource +class in one request. + +1.8 Require placement 'project_id', 'user_id' in PUT /allocations +----------------------------------------------------------------- + +The 1.8 version adds ``project_id`` and ``user_id`` required request parameters +to ``PUT /allocations``. + +1.9 Add GET /usages +-------------------- + +The 1.9 version adds usages that can be queried by a project or project/user. + +The following new routes are added: + +``GET /usages?project_id=`` + + Returns all usages for a given project. + +``GET /usages?project_id=&user_id=`` + + Returns all usages for a given project and user. + +1.10 Allocation candidates +------------------------------------------- + +The 1.10 version brings a new REST resource endpoint for getting a list of +allocation candidates. Allocation candidates are collections of possible +allocations against resource providers that can satisfy a particular request +for resources. diff --git a/nova/api/openstack/placement/util.py b/nova/api/openstack/placement/util.py new file mode 100644 index 00000000000..7a1c1e4c0b5 --- /dev/null +++ b/nova/api/openstack/placement/util.py @@ -0,0 +1,259 @@ +# 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. +"""Utility methods for placement API.""" + +import functools + +import jsonschema +from oslo_middleware import request_id +from oslo_serialization import jsonutils +from oslo_utils import uuidutils +import webob + +# NOTE(cdent): avoid cyclical import conflict between util and +# microversion +import nova.api.openstack.placement.microversion +from nova.i18n import _ + + +# NOTE(cdent): This registers a FormatChecker on the jsonschema +# module. Do not delete this code! Although it appears that nothing +# is using the decorated method it is being used in JSON schema +# validations to check uuid formatted strings. +@jsonschema.FormatChecker.cls_checks('uuid') +def _validate_uuid_format(instance): + return uuidutils.is_uuid_like(instance) + + +def check_accept(*types): + """If accept is set explicitly, try to follow it. + + If there is no match for the incoming accept header + send a 406 response code. + + If accept is not set send our usual content-type in + response. + """ + def decorator(f): + @functools.wraps(f) + def decorated_function(req): + if req.accept: + best_match = req.accept.best_match(types) + if not best_match: + type_string = ', '.join(types) + raise webob.exc.HTTPNotAcceptable( + _('Only %(type)s is provided') % {'type': type_string}, + json_formatter=json_error_formatter) + return f(req) + return decorated_function + return decorator + + +def extract_json(body, schema): + """Extract JSON from a body and validate with the provided schema.""" + try: + data = jsonutils.loads(body) + except ValueError as exc: + raise webob.exc.HTTPBadRequest( + _('Malformed JSON: %(error)s') % {'error': exc}, + json_formatter=json_error_formatter) + try: + jsonschema.validate(data, schema, + format_checker=jsonschema.FormatChecker()) + except jsonschema.ValidationError as exc: + raise webob.exc.HTTPBadRequest( + _('JSON does not validate: %(error)s') % {'error': exc}, + json_formatter=json_error_formatter) + return data + + +def inventory_url(environ, resource_provider, resource_class=None): + url = '%s/inventories' % resource_provider_url(environ, resource_provider) + if resource_class: + url = '%s/%s' % (url, resource_class) + return url + + +def json_error_formatter(body, status, title, environ): + """A json_formatter for webob exceptions. + + Follows API-WG guidelines at + http://specs.openstack.org/openstack/api-wg/guidelines/errors.html + """ + # Clear out the html that webob sneaks in. + body = webob.exc.strip_tags(body) + # Get status code out of status message. webob's error formatter + # only passes entire status string. + status_code = int(status.split(None, 1)[0]) + error_dict = { + 'status': status_code, + 'title': title, + 'detail': body + } + # If the request id middleware has had a chance to add an id, + # put it in the error response. + if request_id.ENV_REQUEST_ID in environ: + error_dict['request_id'] = environ[request_id.ENV_REQUEST_ID] + + # When there is a no microversion in the environment and a 406, + # microversion parsing failed so we need to include microversion + # min and max information in the error response. + microversion = nova.api.openstack.placement.microversion + if status_code == 406 and microversion.MICROVERSION_ENVIRON not in environ: + error_dict['max_version'] = microversion.max_version_string() + error_dict['min_version'] = microversion.min_version_string() + + return {'errors': [error_dict]} + + +def require_content(content_type): + """Decorator to require a content type in a handler.""" + def decorator(f): + @functools.wraps(f) + def decorated_function(req): + if req.content_type != content_type: + # webob's unset content_type is the empty string so + # set it the error message content to 'None' to make + # a useful message in that case. This also avoids a + # KeyError raised when webob.exc eagerly fills in a + # Template for output we will never use. + if not req.content_type: + req.content_type = 'None' + raise webob.exc.HTTPUnsupportedMediaType( + _('The media type %(bad_type)s is not supported, ' + 'use %(good_type)s') % + {'bad_type': req.content_type, + 'good_type': content_type}, + json_formatter=json_error_formatter) + else: + return f(req) + return decorated_function + return decorator + + +def resource_class_url(environ, resource_class): + """Produce the URL for a resource class. + + If SCRIPT_NAME is present, it is the mount point of the placement + WSGI app. + """ + prefix = environ.get('SCRIPT_NAME', '') + return '%s/resource_classes/%s' % (prefix, resource_class.name) + + +def resource_provider_url(environ, resource_provider): + """Produce the URL for a resource provider. + + If SCRIPT_NAME is present, it is the mount point of the placement + WSGI app. + """ + prefix = environ.get('SCRIPT_NAME', '') + return '%s/resource_providers/%s' % (prefix, resource_provider.uuid) + + +def trait_url(environ, trait): + """Produce the URL for a trait. + + If SCRIPT_NAME is present, it is the mount point of the placement + WSGI app. + """ + prefix = environ.get('SCRIPT_NAME', '') + return '%s/traits/%s' % (prefix, trait.name) + + +def validate_query_params(req, schema): + try: + jsonschema.validate(dict(req.GET), schema, + format_checker=jsonschema.FormatChecker()) + except jsonschema.ValidationError as exc: + raise webob.exc.HTTPBadRequest( + _('Invalid query string parameters: %(exc)s') % + {'exc': exc}) + + +def wsgi_path_item(environ, name): + """Extract the value of a named field in a URL. + + Return None if the name is not present or there are no path items. + """ + # NOTE(cdent): For the time being we don't need to urldecode + # the value as the entire placement API has paths that accept no + # encoded values. + try: + return environ['wsgiorg.routing_args'][1][name] + except (KeyError, IndexError): + return None + + +def normalize_resources_qs_param(qs): + """Given a query string parameter for resources, validate it meets the + expected format and return a dict of amounts, keyed by resource class name. + + The expected format of the resources parameter looks like so: + + $RESOURCE_CLASS_NAME:$AMOUNT,$RESOURCE_CLASS_NAME:$AMOUNT + + So, if the user was looking for resource providers that had room for an + instance that will consume 2 vCPUs, 1024 MB of RAM and 50GB of disk space, + they would use the following query string: + + ?resources=VCPU:2,MEMORY_MB:1024,DISK_GB:50 + + The returned value would be: + + { + "VCPU": 2, + "MEMORY_MB": 1024, + "DISK_GB": 50, + } + + :param qs: The value of the 'resources' query string parameter + :raises `webob.exc.HTTPBadRequest` if the parameter's value isn't in the + expected format. + """ + if qs.strip() == "": + msg = _('Badly formed resources parameter. Expected resources ' + 'query string parameter in form: ' + '?resources=VCPU:2,MEMORY_MB:1024. Got: empty string.') + raise webob.exc.HTTPBadRequest(msg) + + result = {} + resource_tuples = qs.split(',') + for rt in resource_tuples: + try: + rc_name, amount = rt.split(':') + except ValueError: + msg = _('Badly formed resources parameter. Expected resources ' + 'query string parameter in form: ' + '?resources=VCPU:2,MEMORY_MB:1024. Got: %s.') + msg = msg % rt + raise webob.exc.HTTPBadRequest(msg) + try: + amount = int(amount) + except ValueError: + msg = _('Requested resource %(resource_name)s expected positive ' + 'integer amount. Got: %(amount)s.') + msg = msg % { + 'resource_name': rc_name, + 'amount': amount, + } + raise webob.exc.HTTPBadRequest(msg) + if amount < 1: + msg = _('Requested resource %(resource_name)s requires ' + 'amount >= 1. Got: %(amount)d.') + msg = msg % { + 'resource_name': rc_name, + 'amount': amount, + } + raise webob.exc.HTTPBadRequest(msg) + result[rc_name] = amount + return result diff --git a/nova/api/openstack/placement/wsgi.py b/nova/api/openstack/placement/wsgi.py new file mode 100644 index 00000000000..c982f90f9d9 --- /dev/null +++ b/nova/api/openstack/placement/wsgi.py @@ -0,0 +1,65 @@ +# 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. +"""WSGI script for Placement API + +WSGI handler for running Placement API under Apache2, nginx, gunicorn etc. +""" + +import logging as py_logging +import os +import os.path + +from oslo_log import log as logging + +from nova.api.openstack.placement import deploy +from nova import conf +from nova import config + +CONFIG_FILE = 'nova.conf' + + +def setup_logging(config): + # Any dependent libraries that have unhelp debug levels should be + # pinned to a higher default. + extra_log_level_defaults = [ + 'routes=INFO', + ] + logging.set_defaults(default_log_levels=logging.get_default_log_levels() + + extra_log_level_defaults) + logging.setup(config, 'nova') + py_logging.captureWarnings(True) + + +def _get_config_file(env=None): + if env is None: + env = os.environ + + dirname = env.get('OS_PLACEMENT_CONFIG_DIR', '/etc/nova').strip() + return os.path.join(dirname, CONFIG_FILE) + + +def init_application(): + # initialize the config system + conffile = _get_config_file() + config.parse_args([], default_config_files=[conffile]) + + # initialize the logging system + setup_logging(conf.CONF) + + # dump conf if we're at debug + if conf.CONF.debug: + conf.CONF.log_opt_values( + logging.getLogger(__name__), + logging.DEBUG) + + # build and return our WSGI app + return deploy.loadapp(conf.CONF) diff --git a/nova/api/openstack/placement/wsgi_wrapper.py b/nova/api/openstack/placement/wsgi_wrapper.py new file mode 100644 index 00000000000..4aa8b789ba5 --- /dev/null +++ b/nova/api/openstack/placement/wsgi_wrapper.py @@ -0,0 +1,33 @@ +# 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. +"""Extend functionality from webob.dec.wsgify for Placement API.""" + +import webob + +from oslo_log import log as logging +from webob.dec import wsgify + +from nova.api.openstack.placement import util + +LOG = logging.getLogger(__name__) + + +class PlacementWsgify(wsgify): + + def call_func(self, req, *args, **kwargs): + """Add json_error_formatter to any webob HTTPExceptions.""" + try: + super(PlacementWsgify, self).call_func(req, *args, **kwargs) + except webob.exc.HTTPException as exc: + LOG.debug("Placement API returning an error response: %s", exc) + exc.json_formatter = util.json_error_formatter + raise diff --git a/nova/api/openstack/requestlog.py b/nova/api/openstack/requestlog.py new file mode 100644 index 00000000000..85c0e8ed572 --- /dev/null +++ b/nova/api/openstack/requestlog.py @@ -0,0 +1,92 @@ +# 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. +"""Simple middleware for request logging.""" + +import time + +from oslo_log import log as logging +from oslo_utils import excutils +import webob.dec +import webob.exc + +from nova.api.openstack import wsgi +from nova import wsgi as base_wsgi + +# TODO(sdague) maybe we can use a better name here for the logger +LOG = logging.getLogger(__name__) + + +class RequestLog(base_wsgi.Middleware): + """WSGI Middleware to write a simple request log to. + + Borrowed from Paste Translogger + """ + + # This matches the placement fil + _log_format = ('%(REMOTE_ADDR)s "%(REQUEST_METHOD)s %(REQUEST_URI)s" ' + 'status: %(status)s len: %(len)s ' + 'microversion: %(microversion)s time: %(time).6f') + + @staticmethod + def _get_uri(environ): + req_uri = (environ.get('SCRIPT_NAME', '') + + environ.get('PATH_INFO', '')) + if environ.get('QUERY_STRING'): + req_uri += '?' + environ['QUERY_STRING'] + return req_uri + + @staticmethod + def _should_emit(req): + """Conditions under which we should skip logging. + + If we detect we are running under eventlet wsgi processing, we + already are logging things, let it go. This is broken out as a + separate method so that it can be easily adjusted for testing. + """ + if req.environ.get('eventlet.input', None) is not None: + return False + return True + + def _log_req(self, req, res, start): + if not self._should_emit(req): + return + + # in the event that things exploded really badly deeper in the + # wsgi stack, res is going to be an empty dict for the + # fallback logging. So never count on it having attributes. + status = getattr(res, "status", "500 Error").split(None, 1)[0] + data = { + 'REMOTE_ADDR': req.environ.get('REMOTE_ADDR', '-'), + 'REQUEST_METHOD': req.environ['REQUEST_METHOD'], + 'REQUEST_URI': self._get_uri(req.environ), + 'status': status, + 'len': getattr(res, "content_length", 0), + 'time': time.time() - start, + 'microversion': '-' + } + # set microversion if it exists + if not req.api_version_request.is_null(): + data["microversion"] = req.api_version_request.get_string() + LOG.info(self._log_format, data) + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + res = {} + start = time.time() + try: + res = req.get_response(self.application) + self._log_req(req, res, start) + return res + except Exception: + with excutils.save_and_reraise_exception(): + self._log_req(req, res, start) diff --git a/nova/api/openstack/rest_api_version_history.rst b/nova/api/openstack/rest_api_version_history.rst deleted file mode 100644 index 41e1b5f022e..00000000000 --- a/nova/api/openstack/rest_api_version_history.rst +++ /dev/null @@ -1,220 +0,0 @@ -REST API Version History -======================== - -This documents the changes made to the REST API with every -microversion change. The description for each version should be a -verbose one which has enough information to be suitable for use in -user documentation. - -2.1 ---- - - This is the initial version of the v2.1 API which supports - microversions. The V2.1 API is from the REST API users's point of - view exactly the same as v2.0 except with strong input validation. - - A user can specify a header in the API request:: - - X-OpenStack-Nova-API-Version: - - where ```` is any valid api version for this API. - - If no version is specified then the API will behave as if a version - request of v2.1 was requested. - -2.2 ---- - - Added Keypair type. - - A user can request the creation of a certain 'type' of keypair (``ssh`` or ``x509``) - in the ``os-keypairs`` plugin - - If no keypair type is specified, then the default ``ssh`` type of keypair is - created. - - Fixes status code for ``os-keypairs`` create method from 200 to 201 - - Fixes status code for ``os-keypairs`` delete method from 202 to 204 - -2.3 (Maximum in Kilo) ---------------------- - - Exposed additional attributes in ``os-extended-server-attributes``: - ``reservation_id``, ``launch_index``, ``ramdisk_id``, ``kernel_id``, ``hostname``, - ``root_device_name``, ``userdata``. - - Exposed ``delete_on_termination`` for ``volumes_attached`` in ``os-extended-volumes``. - - This change is required for the extraction of EC2 API into a standalone - service. It exposes necessary properties absent in public nova APIs yet. - Add info for Standalone EC2 API to cut access to Nova DB. - -2.4 ---- - - Show the ``reserved`` status on a ``FixedIP`` object in the ``os-fixed-ips`` API - extension. The extension allows one to ``reserve`` and ``unreserve`` a fixed IP - but the show method does not report the current status. - -2.5 ---- - - Before version 2.5, the command ``nova list --ip6 xxx`` returns all servers - for non-admins, as the filter option is silently discarded. There is no - reason to treat ip6 different from ip, though, so we just add this - option to the allowed list. - -2.6 ---- - - A new API for getting remote console is added:: - - POST /servers//remote-consoles - { - "remote_console": { - "protocol": ["vnc"|"rdp"|"serial"|"spice"], - "type": ["novnc"|"xpvnc"|"rdp-html5"|"spice-html5"|"serial"] - } - } - - Example response:: - - { - "remote_console": { - "protocol": "vnc", - "type": "novnc", - "url": "http://example.com:6080/vnc_auto.html?token=XYZ" - } - } - - The old APIs 'os-getVNCConsole', 'os-getSPICEConsole', 'os-getSerialConsole' - and 'os-getRDPConsole' are removed. - -2.7 ---- - - Check the ``is_public`` attribute of a flavor before adding tenant access - to it. Reject the request with HTTPConflict error. - -2.8 ---- - Add 'mks' protocol and 'webmks' type for remote consoles. - -2.9 ---- - - Add a new ``locked`` attribute to the detailed view of - servers. ``locked`` will be ``true`` if anyone is currently holding - a lock on the server, ``false`` otherwise. - -2.10 ----- - - Added user_id parameter to os-keypairs plugin, as well as a new property - in the request body, for the create operation. - - Administrators will be able to list, get details and delete keypairs owned by - users other than themselves and to create new keypairs on behalf of their - users. - -2.11 ----- - - Exposed attribute ``forced_down`` for ``os-services``. - Added ability to change the ``forced_down`` attribute by calling an update. - -2.12 (Maximum in Liberty) -------------------------- - - Exposes VIF ``net_id`` attribute in ``os-virtual-interfaces``. - User will be able to get Virtual Interfaces ``net_id`` in Virtual Interfaces - list and can determine in which network a Virtual Interface is plugged into. - -2.13 ----- - - Add information ``project_id`` and ``user_id`` to ``os-server-groups`` - API response data. - -2.14 ----- - - Remove ``onSharedStorage`` parameter from server's evacuate action. Nova will - automatically detect if the instance is on shared storage. - Also adminPass is removed from the response body. The user can get the - password with the server's os-server-password action. - -2.15 ----- - - From this version of the API users can choose 'soft-affinity' and - 'soft-anti-affinity' rules too for server-groups. - -2.16 ----- - - Exposes new host_status attribute for servers/detail and servers/{server_id}. - Ability to get nova-compute status when querying servers. By default, this is - only exposed to cloud administrators. - -2.17 ----- - - Add a new API for triggering crash dump in an instance. Different operation - systems in instance may need different configurations to trigger crash dump. - -2.18 ----- - Establishes a set of routes that makes project_id an optional construct in v2.1. - -2.19 ----- - Allow the user to set and get the server description. - The user will be able to set the description when creating, rebuilding, - or updating a server, and get the description as part of the server details. - -2.20 ----- - From this version of the API user can call detach and attach volumes for - instances which are in shelved and shelved_offloaded state. - -2.21 ----- - - The ``os-instance-actions`` API now returns information from deleted - instances. - -2.22 ----- - - A new resource servers:migrations added. A new API to force live migration - to complete added:: - - POST /servers//migrations//action - { - "force_complete": null - } - -2.23 ----- - - From this version of the API users can get the migration summary list by - index API or the information of a specific migration by get API. - And the old top-level resource `/os-migrations` won't be extended anymore. - Add migration_type for old /os-migrations API, also add ref link to the - /servers/{uuid}/migrations/{id} for it when the migration is an in-progress - live-migration. - -2.24 ----- - - A new API call to cancel a running live migration:: - - DELETE /servers//migrations/ - -2.25 ----- - - Modify input parameter for ``os-migrateLive``. The block_migration will - support 'auto' value, and disk_over_commit flag will be removed. diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index 1a530162f0e..0cc7b70df58 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -15,12 +15,11 @@ # under the License. import functools -import inspect -import math -import time +import microversion_parse from oslo_log import log as logging from oslo_serialization import jsonutils +from oslo_utils import encodeutils from oslo_utils import strutils import six import webob @@ -30,8 +29,6 @@ from nova import exception from nova import i18n from nova.i18n import _ -from nova.i18n import _LE -from nova.i18n import _LI from nova import utils from nova import wsgi @@ -43,11 +40,6 @@ 'application/vnd.openstack.compute+json', ) -_MEDIA_TYPE_MAP = { - 'application/vnd.openstack.compute+json': 'json', - 'application/json': 'json', -} - # These are typically automatically created by routes as either defaults # collection or member methods. _ROUTES_METHODS = [ @@ -70,9 +62,10 @@ # name of attribute to keep version method information VER_METHOD_ATTR = 'versioned_methods' -# Name of header used by clients to request a specific version +# Names of headers used by clients to request a specific version # of the REST API -API_VERSION_REQUEST_HEADER = 'X-OpenStack-Nova-API-Version' +API_VERSION_REQUEST_HEADER = 'OpenStack-API-Version' +LEGACY_API_VERSION_REQUEST_HEADER = 'X-OpenStack-Nova-API-Version' ENV_LEGACY_V2 = 'openstack.legacy_v2' @@ -82,10 +75,6 @@ def get_supported_content_types(): return _SUPPORTED_CONTENT_TYPES -def get_media_map(): - return dict(_MEDIA_TYPE_MAP.items()) - - # NOTE(rlrossit): This function allows a get on both a dict-like and an # object-like object. cache_db_items() is used on both versioned objects and # dicts, so the function can't be totally changed over to [] syntax, nor @@ -158,18 +147,6 @@ def get_db_flavors(self): def get_db_flavor(self, flavorid): return self.get_db_item('flavors', flavorid) - def cache_db_compute_nodes(self, compute_nodes): - self.cache_db_items('compute_nodes', compute_nodes, 'id') - - def cache_db_compute_node(self, compute_node): - self.cache_db_items('compute_nodes', [compute_node], 'id') - - def get_db_compute_nodes(self): - return self.get_db_items('compute_nodes') - - def get_db_compute_node(self, id): - return self.get_db_item('compute_nodes', id) - def best_match_content_type(self): """Determine the requested response content-type.""" if 'nova.best_content_type' not in self.environ: @@ -227,29 +204,30 @@ def best_match_language(self): def set_api_version_request(self): """Set API version request based on the request header information.""" - if API_VERSION_REQUEST_HEADER in self.headers: - hdr_string = self.headers[API_VERSION_REQUEST_HEADER] - # 'latest' is a special keyword which is equivalent to requesting - # the maximum version of the API supported - if hdr_string == 'latest': - self.api_version_request = api_version.max_api_version() - else: - self.api_version_request = api_version.APIVersionRequest( - hdr_string) - - # Check that the version requested is within the global - # minimum/maximum of supported API versions - if not self.api_version_request.matches( - api_version.min_api_version(), - api_version.max_api_version()): - raise exception.InvalidGlobalAPIVersion( - req_ver=self.api_version_request.get_string(), - min_ver=api_version.min_api_version().get_string(), - max_ver=api_version.max_api_version().get_string()) + hdr_string = microversion_parse.get_version( + self.headers, service_type='compute', + legacy_headers=[LEGACY_API_VERSION_REQUEST_HEADER]) - else: + if hdr_string is None: self.api_version_request = api_version.APIVersionRequest( api_version.DEFAULT_API_VERSION) + elif hdr_string == 'latest': + # 'latest' is a special keyword which is equivalent to + # requesting the maximum version of the API supported + self.api_version_request = api_version.max_api_version() + else: + self.api_version_request = api_version.APIVersionRequest( + hdr_string) + + # Check that the version requested is within the global + # minimum/maximum of supported API versions + if not self.api_version_request.matches( + api_version.min_api_version(), + api_version.max_api_version()): + raise exception.InvalidGlobalAPIVersion( + req_ver=self.api_version_request.get_string(), + min_ver=api_version.min_api_version().get_string(), + max_ver=api_version.max_api_version().get_string()) def set_legacy_v2(self): self.environ[ENV_LEGACY_V2] = True @@ -429,14 +407,14 @@ def __exit__(self, ex_type, ex_value, ex_traceback): explanation=ex_value.format_message())) elif isinstance(ex_value, TypeError): exc_info = (ex_type, ex_value, ex_traceback) - LOG.error(_LE('Exception handling resource: %s'), ex_value, + LOG.error('Exception handling resource: %s', ex_value, exc_info=exc_info) raise Fault(webob.exc.HTTPBadRequest()) elif isinstance(ex_value, Fault): - LOG.info(_LI("Fault thrown: %s"), ex_value) + LOG.info("Fault thrown: %s", ex_value) raise ex_value elif isinstance(ex_value, webob.exc.HTTPException): - LOG.info(_LI("HTTP exception thrown: %s"), ex_value) + LOG.info("HTTP exception thrown: %s", ex_value) raise Fault(ex_value) # We didn't handle the exception @@ -542,90 +520,25 @@ def get_body(self, request): def deserialize(self, body): return JSONDeserializer().deserialize(body) - # NOTE(sdague): I didn't start the fire, however here is what all - # of this is about. - # - # In the legacy v2 code stack, extensions could extend actions - # with a generator that let 1 method be split into a top and - # bottom half. The top half gets executed before the main - # processing of the request (so effectively gets to modify the - # request before it gets to the main method). - # - # Returning a response triggers a shortcut to fail out. The - # response will nearly always be a failure condition, as it ends - # up skipping further processing one level up from here. - # - # This then passes on the list of extensions, in reverse order, - # on. post_process will run through all those, again with same - # basic logic. - # - # In tree this is only used in the legacy v2 stack, and only in - # the DiskConfig and SchedulerHints from what I can see. - # - # pre_process_extensions can be removed when the legacyv2 code - # goes away. post_process_extensions can be massively simplified - # at that point. - def pre_process_extensions(self, extensions, request, action_args): - # List of callables for post-processing extensions - post = [] - - for ext in extensions: - if inspect.isgeneratorfunction(ext): - response = None - - # If it's a generator function, the part before the - # yield is the preprocessing stage - try: - with ResourceExceptionHandler(): - gen = ext(req=request, **action_args) - response = next(gen) - except Fault as ex: - response = ex - - # We had a response... - if response: - return response, [] - - # No response, queue up generator for post-processing - post.append(gen) - else: - # Regular functions only perform post-processing - post.append(ext) - - # None is response, it means we keep going. We reverse the - # extension list for post-processing. - return None, reversed(post) - - def post_process_extensions(self, extensions, resp_obj, request, - action_args): + def process_extensions(self, extensions, resp_obj, request, + action_args): for ext in extensions: response = None - if inspect.isgenerator(ext): - # If it's a generator, run the second half of - # processing - try: - with ResourceExceptionHandler(): - response = ext.send(resp_obj) - except StopIteration: - # Normal exit of generator - continue - except Fault as ex: - response = ex - else: - # Regular functions get post-processing... - try: - with ResourceExceptionHandler(): - response = ext(req=request, resp_obj=resp_obj, - **action_args) - except exception.VersionNotFoundForAPIMethod: - # If an attached extension (@wsgi.extends) for the - # method has no version match its not an error. We - # just don't run the extends code - continue - except Fault as ex: - response = ex - - # We had a response... + # Regular functions get post-processing... + try: + with ResourceExceptionHandler(): + response = ext(req=request, resp_obj=resp_obj, + **action_args) + except exception.VersionNotFoundForAPIMethod: + # If an attached extension (@wsgi.extends) for the + # method has no version match its not an error. We + # just don't run the extends code + continue + except Fault as ex: + response = ex + + # We had a response return it, to exit early. This is + # actually a failure mode. None is success. if response: return response @@ -661,7 +574,7 @@ def __call__(self, request): accept = request.best_match_content_type() except exception.InvalidContentType: msg = _("Unsupported Content-Type") - return Fault(webob.exc.HTTPBadRequest(explanation=msg)) + return Fault(webob.exc.HTTPUnsupportedMediaType(explanation=msg)) # NOTE(Vek): Splitting the function up this way allows for # auditing by external tools that wrap the existing @@ -700,13 +613,7 @@ def _process_stack(self, request, action, action_args, # Now, deserialize the request body... try: - contents = {} - if self._should_have_body(request): - # allow empty body with PUT and POST - if request.content_length == 0: - contents = {'body': None} - else: - contents = self.deserialize(body) + contents = self._get_request_content(body, request) except exception.MalformedRequestBody: msg = _("Malformed request body") return Fault(webob.exc.HTTPBadRequest(explanation=msg)) @@ -724,16 +631,12 @@ def _process_stack(self, request, action, action_args, 'context_project_id': context.project_id} return Fault(webob.exc.HTTPBadRequest(explanation=msg)) - # Run pre-processing extensions - response, post = self.pre_process_extensions(extensions, - request, action_args) - - if not response: - try: - with ResourceExceptionHandler(): - action_result = self.dispatch(meth, request, action_args) - except Fault as ex: - response = ex + response = None + try: + with ResourceExceptionHandler(): + action_result = self.dispatch(meth, request, action_args) + except Fault as ex: + response = ex if not response: # No exceptions; convert action_result into a @@ -751,8 +654,8 @@ def _process_stack(self, request, action, action_args, # Do a preserialize to set up the response object if hasattr(meth, 'wsgi_code'): resp_obj._default_code = meth.wsgi_code - # Process post-processing extensions - response = self.post_process_extensions(post, resp_obj, + # Process extensions + response = self.process_extensions(extensions, resp_obj, request, action_args) if resp_obj and not response: @@ -760,16 +663,34 @@ def _process_stack(self, request, action, action_args, if hasattr(response, 'headers'): for hdr, val in list(response.headers.items()): - # Headers must be utf-8 strings - response.headers[hdr] = utils.utf8(val) + if six.PY2: + # In Py2.X Headers must be byte strings + response.headers[hdr] = utils.utf8(val) + else: + # In Py3.X Headers must be utf-8 strings + response.headers[hdr] = encodeutils.safe_decode( + utils.utf8(val)) if not request.api_version_request.is_null(): response.headers[API_VERSION_REQUEST_HEADER] = \ + 'compute ' + request.api_version_request.get_string() + response.headers[LEGACY_API_VERSION_REQUEST_HEADER] = \ request.api_version_request.get_string() - response.headers['Vary'] = API_VERSION_REQUEST_HEADER + response.headers.add('Vary', API_VERSION_REQUEST_HEADER) + response.headers.add('Vary', LEGACY_API_VERSION_REQUEST_HEADER) return response + def _get_request_content(self, body, request): + contents = {} + if self._should_have_body(request): + # allow empty body with PUT and POST + if request.content_length == 0 or request.content_length is None: + contents = {'body': None} + else: + contents = self.deserialize(body) + return contents + def get_method(self, request, action, content_type, body): meth, extensions = self._get_method(request, action, @@ -1011,8 +932,16 @@ def decorator(f): # so later when we work through the list in order we find # the method which has the latest version which supports # the version requested. - # TODO(cyeoh): Add check to ensure that there are no overlapping - # ranges of valid versions as that is amibiguous + is_intersect = Controller.check_for_versions_intersection( + func_list) + + if is_intersect: + raise exception.ApiVersionsIntersect( + name=new_func.name, + min_ver=new_func.start_version, + max_ver=new_func.end_version, + ) + func_list.sort(key=lambda f: f.start_version, reverse=True) return f @@ -1033,6 +962,36 @@ def is_dict(d): return is_dict(body[entity_name]) + @staticmethod + def check_for_versions_intersection(func_list): + """Determines whether function list contains version intervals + intersections or not. General algorithm: + + https://en.wikipedia.org/wiki/Intersection_algorithm + + :param func_list: list of VersionedMethod objects + :return: boolean + """ + pairs = [] + counter = 0 + + for f in func_list: + pairs.append((f.start_version, 1, f)) + pairs.append((f.end_version, -1, f)) + + def compare(x): + return x[0] + + pairs.sort(key=compare) + + for p in pairs: + counter += p[1] + + if counter > 1: + return True + + return False + class Fault(webob.exc.HTTPException): """Wrap webob.exc.HTTPException to provide API friendly response.""" @@ -1081,9 +1040,12 @@ def __call__(self, req): if not req.api_version_request.is_null(): self.wrapped_exc.headers[API_VERSION_REQUEST_HEADER] = \ + 'compute ' + req.api_version_request.get_string() + self.wrapped_exc.headers[LEGACY_API_VERSION_REQUEST_HEADER] = \ req.api_version_request.get_string() - self.wrapped_exc.headers['Vary'] = \ - API_VERSION_REQUEST_HEADER + self.wrapped_exc.headers.add('Vary', API_VERSION_REQUEST_HEADER) + self.wrapped_exc.headers.add('Vary', + LEGACY_API_VERSION_REQUEST_HEADER) self.wrapped_exc.content_type = 'application/json' self.wrapped_exc.charset = 'UTF-8' @@ -1093,46 +1055,3 @@ def __call__(self, req): def __str__(self): return self.wrapped_exc.__str__() - - -class RateLimitFault(webob.exc.HTTPException): - """Rate-limited request response.""" - - def __init__(self, message, details, retry_time): - """Initialize new `RateLimitFault` with relevant information.""" - hdrs = RateLimitFault._retry_after(retry_time) - self.wrapped_exc = webob.exc.HTTPTooManyRequests(headers=hdrs) - self.content = { - "overLimit": { - "code": self.wrapped_exc.status_int, - "message": message, - "details": details, - "retryAfter": hdrs['Retry-After'], - }, - } - - @staticmethod - def _retry_after(retry_time): - delay = int(math.ceil(retry_time - time.time())) - retry_after = delay if delay > 0 else 0 - headers = {'Retry-After': '%d' % retry_after} - return headers - - @webob.dec.wsgify(RequestClass=Request) - def __call__(self, request): - """Return the wrapped exception with a serialized body conforming - to our error format. - """ - user_locale = request.best_match_language() - - self.content['overLimit']['message'] = \ - i18n.translate(self.content['overLimit']['message'], user_locale) - self.content['overLimit']['details'] = \ - i18n.translate(self.content['overLimit']['details'], user_locale) - - content = JSONDictSerializer().serialize(self.content) - self.wrapped_exc.charset = 'UTF-8' - self.wrapped_exc.content_type = "application/json" - self.wrapped_exc.text = content - - return self.wrapped_exc diff --git a/nova/api/openstack/wsgi_app.py b/nova/api/openstack/wsgi_app.py new file mode 100644 index 00000000000..0e9ae6d0fa5 --- /dev/null +++ b/nova/api/openstack/wsgi_app.py @@ -0,0 +1,87 @@ +# 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. +"""WSGI application initialization for Nova APIs.""" + +import os + +from oslo_config import cfg +from oslo_log import log as logging +from paste import deploy + +from nova import config +from nova import context +from nova import exception +from nova import objects +from nova import service +from nova import utils + +CONF = cfg.CONF + +CONFIG_FILES = ['api-paste.ini', 'nova.conf'] + +utils.monkey_patch() +objects.register_all() + + +def _get_config_files(env=None): + if env is None: + env = os.environ + dirname = env.get('OS_NOVA_CONFIG_DIR', '/etc/nova').strip() + return [os.path.join(dirname, config_file) + for config_file in CONFIG_FILES] + + +def _setup_service(host, name): + binary = name if name.startswith('nova-') else "nova-%s" % name + + ctxt = context.get_admin_context() + service_ref = objects.Service.get_by_host_and_binary( + ctxt, host, binary) + if service_ref: + service._update_service_ref(service_ref) + else: + try: + service_obj = objects.Service(ctxt) + service_obj.host = host + service_obj.binary = binary + service_obj.topic = None + service_obj.report_count = 0 + service_obj.create() + except (exception.ServiceTopicExists, + exception.ServiceBinaryExists): + # If we race to create a record with a sibling, don't + # fail here. + pass + + +def error_application(exc, name): + # TODO(cdent): make this something other than a stub + def application(environ, start_response): + start_response('500 Internal Server Error', [ + ('Content-Type', 'text/plain; charset=UTF-8')]) + return ['Out of date %s service %s\n' % (name, exc)] + return application + + +def init_application(name): + conf_files = _get_config_files() + config.parse_args([], default_config_files=conf_files) + + logging.setup(CONF, "nova") + try: + _setup_service(CONF.host, name) + except exception.ServiceTooOld as exc: + return error_application(exc, name) + + conf = conf_files[0] + + return deploy.loadapp('config:%s' % conf, name=name) diff --git a/nova/api/opts.py b/nova/api/opts.py deleted file mode 100644 index 8976afcbe2f..00000000000 --- a/nova/api/opts.py +++ /dev/null @@ -1,140 +0,0 @@ -# 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 itertools - -import nova.api.auth -import nova.api.metadata.base -import nova.api.metadata.handler -import nova.api.metadata.vendordata_json -import nova.api.openstack -import nova.api.openstack.common -import nova.api.openstack.compute -import nova.api.openstack.compute.hide_server_addresses -import nova.api.openstack.compute.legacy_v2.contrib -import nova.api.openstack.compute.legacy_v2.contrib.fping -import nova.api.openstack.compute.legacy_v2.contrib.os_tenant_networks -import nova.api.openstack.compute.legacy_v2.extensions -import nova.api.openstack.compute.legacy_v2.servers -import nova.availability_zones -import nova.baserpc -import nova.cells.manager -import nova.cells.messaging -import nova.cells.opts -import nova.cells.rpc_driver -import nova.cells.rpcapi -import nova.cells.scheduler -import nova.cells.state -import nova.cells.weights.mute_child -import nova.cells.weights.ram_by_instance_type -import nova.cells.weights.weight_offset -import nova.cert.rpcapi -import nova.cloudpipe.pipelib -import nova.cmd.novnc -import nova.cmd.novncproxy -import nova.cmd.serialproxy -import nova.cmd.spicehtml5proxy -import nova.compute.api -import nova.compute.flavors -import nova.compute.manager -import nova.compute.monitors -import nova.compute.resource_tracker -import nova.compute.rpcapi -import nova.conductor.api -import nova.conductor.rpcapi -import nova.conductor.tasks.live_migrate -import nova.console.manager -import nova.console.rpcapi -import nova.console.serial -import nova.console.xvp -import nova.consoleauth -import nova.consoleauth.manager -import nova.consoleauth.rpcapi -import nova.crypto -import nova.db.api -import nova.db.base -import nova.db.sqlalchemy.api -import nova.exception -import nova.image.download.file -import nova.image.glance -import nova.ipv6.api -import nova.keymgr -import nova.keymgr.barbican -import nova.keymgr.conf_key_mgr -import nova.netconf -import nova.network -import nova.network.driver -import nova.network.floating_ips -import nova.network.ldapdns -import nova.network.linux_net -import nova.network.manager -import nova.network.neutronv2.api -import nova.network.rpcapi -import nova.network.security_group.openstack_driver -import nova.notifications -import nova.objects.network -import nova.paths -import nova.pci.request -import nova.pci.whitelist -import nova.quota -import nova.rdp -import nova.scheduler.driver -import nova.scheduler.filter_scheduler -import nova.scheduler.filters.aggregate_image_properties_isolation -import nova.scheduler.filters.core_filter -import nova.scheduler.filters.disk_filter -import nova.scheduler.filters.io_ops_filter -import nova.scheduler.filters.isolated_hosts_filter -import nova.scheduler.filters.num_instances_filter -import nova.scheduler.filters.ram_filter -import nova.scheduler.filters.trusted_filter -import nova.scheduler.host_manager -import nova.scheduler.ironic_host_manager -import nova.scheduler.manager -import nova.scheduler.rpcapi -import nova.scheduler.scheduler_options -import nova.scheduler.utils -import nova.scheduler.weights.io_ops -import nova.scheduler.weights.metrics -import nova.scheduler.weights.ram -import nova.service -import nova.servicegroup.api -import nova.spice -import nova.utils -import nova.vnc -import nova.vnc.xvp_proxy -import nova.volume -import nova.volume.cinder -import nova.wsgi - - -def list_opts(): - return [ - ('DEFAULT', - itertools.chain( - [nova.api.metadata.vendordata_json.file_opt], - [nova.api.openstack.compute.allow_instance_snapshots_opt], - nova.api.auth.auth_opts, - nova.api.metadata.base.metadata_opts, - nova.api.metadata.handler.metadata_opts, - nova.api.openstack.common.osapi_opts, - nova.api.openstack.compute.legacy_v2.contrib.ext_opts, - nova.api.openstack.compute.legacy_v2.contrib.fping.fping_opts, - nova.api.openstack.compute.legacy_v2.contrib.os_tenant_networks. - os_network_opts, - nova.api.openstack.compute.legacy_v2.extensions.ext_opts, - nova.api.openstack.compute.hide_server_addresses.opts, - nova.api.openstack.compute.legacy_v2.servers.server_opts, - )), - ('neutron', nova.api.metadata.handler.metadata_proxy_opts), - ('osapi_v21', nova.api.openstack.api_opts), - ] diff --git a/nova/api/sizelimit.py b/nova/api/sizelimit.py deleted file mode 100644 index 0d8587d8243..00000000000 --- a/nova/api/sizelimit.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2012 OpenStack Foundation -# -# 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. -""" -Request Body limiting middleware. - -""" -from oslo_middleware import sizelimit - - -# TODO(ekudryashova): Remove below aliases when grenade jobs -# will start upgrading from juno -LimitingReader = sizelimit.LimitingReader -RequestBodySizeLimiter = sizelimit.RequestBodySizeLimiter diff --git a/nova/api/validation/__init__.py b/nova/api/validation/__init__.py index 0b8615f7043..490c4ea870e 100644 --- a/nova/api/validation/__init__.py +++ b/nova/api/validation/__init__.py @@ -17,11 +17,78 @@ """ import functools +import re from nova.api.openstack import api_version_request as api_version from nova.api.validation import validators +def _schema_validation_helper(schema, target, min_version, max_version, + args, kwargs, is_body=True): + """A helper method to execute JSON-Schema Validation. + + This method checks the request version whether matches the specified max + version and min_version. It also takes a care of legacy v2 request. + + If the version range matches the request, we validate the schema against + the target and a failure will result in a ValidationError being raised. + + :param schema: A dict, the JSON-Schema is used to validate the target. + :param target: A dict, the target is validated by the JSON-Schema. + :param min_version: A string of two numerals. X.Y indicating the minimum + version of the JSON-Schema to validate against. + :param max_version: A string of two numerals. X.Y indicating the maximum + version of the JSON-Schema to validate against. + :param args: Positional arguments which passed into original method. + :param kwargs: Keyword arguments which passed into original method. + :param is_body: A boolean. Indicating whether the target is HTTP request + body or not. + :returns: A boolean. `True` if and only if the version range matches the + request AND the schema is successfully validated. `False` if the + version range does not match the request and no validation is + performed. + :raises: ValidationError, when the validation fails. + """ + min_ver = api_version.APIVersionRequest(min_version) + max_ver = api_version.APIVersionRequest(max_version) + + # The request object is always the second argument. + # However numerous unittests pass in the request object + # via kwargs instead so we handle that as well. + # TODO(cyeoh): cleanup unittests so we don't have to + # to do this + if 'req' in kwargs: + ver = kwargs['req'].api_version_request + legacy_v2 = kwargs['req'].is_legacy_v2() + else: + ver = args[1].api_version_request + legacy_v2 = args[1].is_legacy_v2() + + if legacy_v2: + # NOTE: For v2.0 compatible API, here should work like + # client | schema min_version | schema + # -----------+--------------------+-------- + # legacy_v2 | None | work + # legacy_v2 | 2.0 | work + # legacy_v2 | 2.1+ | don't + if min_version is None or min_version == '2.0': + schema_validator = validators._SchemaValidator( + schema, legacy_v2, is_body) + schema_validator.validate(target) + return True + elif ver.matches(min_ver, max_ver): + # Only validate against the schema if it lies within + # the version range specified. Note that if both min + # and max are not specified the validator will always + # be run. + schema_validator = validators._SchemaValidator( + schema, legacy_v2, is_body) + schema_validator.validate(target) + return True + + return False + + def schema(request_body_schema, min_version=None, max_version=None): """Register a schema to validate request body. @@ -35,41 +102,82 @@ def schema(request_body_schema, min_version=None, max_version=None): def add_validator(func): @functools.wraps(func) def wrapper(*args, **kwargs): - min_ver = api_version.APIVersionRequest(min_version) - max_ver = api_version.APIVersionRequest(max_version) + _schema_validation_helper(request_body_schema, kwargs['body'], + min_version, max_version, + args, kwargs) + return func(*args, **kwargs) + return wrapper + + return add_validator + +def _strip_additional_query_parameters(schema, req): + """Strip the additional properties from the req.GET. + + This helper method assumes the JSON-Schema is only described as a dict + without nesting. This method should be called after query parameters pass + the JSON-Schema validation. It also means this method only can be called + after _schema_validation_helper return `True`. + """ + additional_properties = schema.get('addtionalProperties', True) + pattern_regexes = [] + + patterns = schema.get('patternProperties', None) + if patterns: + for regex in patterns: + pattern_regexes.append(re.compile(regex)) + + if additional_properties: + # `req.GET.keys` will return duplicated keys for multiple values + # parameters. To get rid of duplicated keys, convert it to set. + for param in set(req.GET.keys()): + if param not in schema['properties'].keys(): + # keys that can match the patternProperties will be + # retained and handle latter. + if not (list(regex for regex in pattern_regexes if + regex.match(param))): + del req.GET[param] + + +def query_schema(query_params_schema, min_version=None, + max_version=None): + """Register a schema to validate request query parameters. + + Registered schema will be used for validating request query params just + before API method executing. + + :param query_params_schema: A dict, the JSON-Schema for validating the + query parameters. + :param min_version: A string of two numerals. X.Y indicating the minimum + version of the JSON-Schema to validate against. + :param max_version: A string of two numerals. X.Y indicating the maximum + version of the JSON-Schema against to. + """ + + def add_validator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): # The request object is always the second argument. # However numerous unittests pass in the request object # via kwargs instead so we handle that as well. # TODO(cyeoh): cleanup unittests so we don't have to # to do this if 'req' in kwargs: - ver = kwargs['req'].api_version_request - legacy_v2 = kwargs['req'].is_legacy_v2() + req = kwargs['req'] else: - ver = args[1].api_version_request - legacy_v2 = args[1].is_legacy_v2() - - if legacy_v2: - # NOTE: For v2.0 compatible API, here should work like - # client | schema min_version | schema - # -----------+--------------------+-------- - # legacy_v2 | None | work - # legacy_v2 | 2.0 | work - # legacy_v2 | 2.1+ | don't - if min_version is None or min_version == '2.0': - schema_validator = validators._SchemaValidator( - request_body_schema, legacy_v2) - schema_validator.validate(kwargs['body']) - elif ver.matches(min_ver, max_ver): - # Only validate against the schema if it lies within - # the version range specified. Note that if both min - # and max are not specified the validator will always - # be run. - schema_validator = validators._SchemaValidator( - request_body_schema, legacy_v2) - schema_validator.validate(kwargs['body']) + req = args[1] + if _schema_validation_helper(query_params_schema, + req.GET.dict_of_lists(), + min_version, max_version, + args, kwargs, is_body=False): + # NOTE(alex_xu): The additional query parameters were stripped + # out when `additionalProperties=True`. This is for backward + # compatible with v2.1 API and legacy v2 API. But it makes the + # system more safe for no more unexpected parameters pass down + # to the system. In the future, we may block all of those + # additional parameters by Microversion. + _strip_additional_query_parameters(query_params_schema, req) return func(*args, **kwargs) return wrapper diff --git a/nova/api/validation/parameter_types.py b/nova/api/validation/parameter_types.py index e17443fd2f5..138d2de1798 100644 --- a/nova/api/validation/parameter_types.py +++ b/nova/api/validation/parameter_types.py @@ -21,7 +21,25 @@ import six +from nova import db from nova.i18n import _ +from nova.objects import tag + + +def single_param(schema): + """Macro function for use in JSONSchema to support query parameters that + should have only one value. + """ + ret = multi_params(schema) + ret['maxItems'] = 1 + return ret + + +def multi_params(schema): + """Macro function for use in JSONSchema to support query parameters that + may have multiple values. + """ + return {'type': 'array', 'items': schema} class ValidationRegex(object): @@ -132,6 +150,24 @@ def valid_char(char): "^[%(ws)s]*[%(no_ws)s][%(no_ws)s%(ws)s]+[%(no_ws)s][%(ws)s]*$") +valid_az_name_regex = ValidationRegex( + valid_name_regex_base % ( + _build_regex_range(ws=False, invert=True), + _build_regex_range(exclude=[':']), + _build_regex_range(ws=False, invert=True)), + _("printable characters except :." + "Can not start or end with whitespace.")) + + +# az's name disallow ':'. +valid_az_name_leading_trailing_spaces_regex = ValidationRegex( + valid_name_leading_trailing_spaces_regex_base % { + 'ws': _build_regex_range(exclude=[':']), + 'no_ws': _build_regex_range(ws=False, exclude=[':'])}, + _("printable characters except :, " + "with at least one non space character")) + + valid_cell_name_regex = ValidationRegex( valid_name_regex_base % ( _build_regex_range(ws=False, invert=True), @@ -183,15 +219,21 @@ def valid_char(char): positive_integer = { 'type': ['integer', 'string'], - 'pattern': '^[0-9]*$', 'minimum': 1 + 'pattern': '^[0-9]*$', 'minimum': 1, 'minLength': 1 } non_negative_integer = { 'type': ['integer', 'string'], - 'pattern': '^[0-9]*$', 'minimum': 0 + 'pattern': '^[0-9]*$', 'minimum': 0, 'minLength': 1 } +# This only be used by nova-network specific APIs. It will be removed when +# those API removed. +positive_integer_with_empty_str = { + 'type': ['integer', 'string'], + 'pattern': '^[0-9]*$', 'minimum': 1, +} hostname = { 'type': 'string', 'minLength': 1, 'maxLength': 255, @@ -221,6 +263,18 @@ def valid_char(char): } +az_name = { + 'type': 'string', 'minLength': 1, 'maxLength': 255, + 'format': 'az_name' +} + + +az_name_with_leading_trailing_spaces = { + 'type': 'string', 'minLength': 1, 'maxLength': 255, + 'format': 'az_name_with_leading_trailing_spaces' +} + + cell_name = { 'type': 'string', 'minLength': 1, 'maxLength': 255, 'format': 'cell_name' @@ -268,6 +322,14 @@ def valid_char(char): } +image_id_or_empty_string = { + 'oneOf': [ + {'type': 'string', 'format': 'uuid'}, + {'type': 'string', 'maxLength': 0} + ] +} + + volume_id = { 'type': 'string', 'format': 'uuid' } @@ -293,11 +355,6 @@ def valid_char(char): } -image_ref = { - 'type': 'string', -} - - flavor_ref = { 'type': ['string', 'integer'], 'minLength': 1 } @@ -347,3 +404,58 @@ def valid_char(char): cidr = { 'type': 'string', 'format': 'cidr' } + + +volume_size = { + 'type': ['integer', 'string'], + 'pattern': '^[0-9]+$', + 'minimum': 1, + 'maximum': db.MAX_INT +} + +disk_config = { + 'type': 'string', + 'enum': ['AUTO', 'MANUAL'] +} + +accessIPv4 = { + 'type': 'string', + 'format': 'ipv4', +} + +accessIPv6 = { + 'type': 'string', + 'format': 'ipv6', +} + +flavor_param_positive = copy.deepcopy(volume_size) + + +flavor_param_non_negative = copy.deepcopy(volume_size) +flavor_param_non_negative['minimum'] = 0 + +personality = { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'path': {'type': 'string'}, + 'contents': { + 'type': 'string', + 'format': 'base64' + } + }, + 'additionalProperties': False, + } +} + +tag = { + "type": "string", + "minLength": 1, "maxLength": tag.MAX_TAG_LENGTH, + "pattern": "^[^,/]*$" +} + +pagination_parameters = { + 'limit': multi_params(non_negative_integer), + 'marker': multi_params({'type': 'string'}) +} diff --git a/nova/api/validation/validators.py b/nova/api/validation/validators.py index 392689d1149..de3225a0507 100644 --- a/nova/api/validation/validators.py +++ b/nova/api/validation/validators.py @@ -32,6 +32,17 @@ from nova.i18n import _ +@jsonschema.FormatChecker.cls_checks('regex') +def _validate_regex_format(instance): + if not instance or not isinstance(instance, six.text_type): + return False + try: + re.compile(instance) + except re.error: + return False + return True + + @jsonschema.FormatChecker.cls_checks('date-time') def _validate_datetime_format(instance): try: @@ -45,9 +56,15 @@ def _validate_datetime_format(instance): @jsonschema.FormatChecker.cls_checks('base64') def _validate_base64_format(instance): try: + if isinstance(instance, six.text_type): + instance = instance.encode('utf-8') base64.decodestring(instance) except base64.binascii.Error: return False + except TypeError: + # The name must be string type. If instance isn't string type, the + # TypeError will be raised at here. + return False return True @@ -103,6 +120,33 @@ def _validate_name(instance): raise exception.InvalidName(reason=regex.reason) +@jsonschema.FormatChecker.cls_checks('az_name_with_leading_trailing_spaces', + exception.InvalidName) +def _validate_az_name_with_leading_trailing_spaces(instance): + regex = parameter_types.valid_az_name_leading_trailing_spaces_regex + try: + if re.search(regex.regex, instance): + return True + except TypeError: + # The name must be string type. If instance isn't string type, the + # TypeError will be raised at here. + pass + raise exception.InvalidName(reason=regex.reason) + + +@jsonschema.FormatChecker.cls_checks('az_name', exception.InvalidName) +def _validate_az_name(instance): + regex = parameter_types.valid_az_name_regex + try: + if re.search(regex.regex, instance): + return True + except TypeError: + # The name must be string type. If instance isn't string type, the + # TypeError will be raised at here. + pass + raise exception.InvalidName(reason=regex.reason) + + @jsonschema.FormatChecker.cls_checks('cell_name_with_leading_trailing_spaces', exception.InvalidName) def _validate_cell_name_with_leading_trailing_spaces(instance): @@ -235,7 +279,9 @@ class _SchemaValidator(object): validator = None validator_org = jsonschema.Draft4Validator - def __init__(self, schema, relax_additional_properties=False): + def __init__(self, schema, relax_additional_properties=False, + is_body=True): + self.is_body = is_body validators = { 'minimum': self._validate_minimum, 'maximum': self._validate_maximum, @@ -256,13 +302,28 @@ def validate(self, *args, **kwargs): if isinstance(ex.cause, exception.InvalidName): detail = ex.cause.format_message() elif len(ex.path) > 0: - # NOTE: For whole OpenStack message consistency, this error - # message has been written as the similar format of WSME. - detail = _("Invalid input for field/attribute %(path)s." - " Value: %(value)s. %(message)s") % { - 'path': ex.path.pop(), 'value': ex.instance, - 'message': ex.message - } + if self.is_body: + # NOTE: For whole OpenStack message consistency, this error + # message has been written as the similar format of + # WSME. + detail = _("Invalid input for field/attribute %(path)s. " + "Value: %(value)s. %(message)s") % { + 'path': ex.path.pop(), + 'value': ex.instance, + 'message': ex.message} + else: + # NOTE: Use 'ex.path.popleft()' instead of 'ex.path.pop()', + # due to the structure of query parameters is a dict + # with key as name and value is list. So the first + # item in the 'ex.path' is the key, and second item + # is the index of list in the value. We need the + # key as the parameter name in the error message. + # So pop the first value out of 'ex.path'. + detail = _("Invalid input for query parameters %(path)s. " + "Value: %(value)s. %(message)s") % { + 'path': ex.path.popleft(), + 'value': ex.instance, + 'message': ex.message} else: detail = ex.message raise exception.ValidationError(detail=detail) diff --git a/nova/api/validator.py b/nova/api/validator.py deleted file mode 100644 index 12a0433c0f0..00000000000 --- a/nova/api/validator.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright 2011 Cloudscaling, 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 base64 - -from oslo_log import log as logging -import rfc3986 -import six - - -LOG = logging.getLogger(__name__) - - -def validate_str(max_length=None): - - def _do(val): - if not isinstance(val, six.string_types): - return False - if max_length and len(val) > max_length: - return False - return True - - return _do - - -def validate_int(max_value=None): - - def _do(val): - if not isinstance(val, int): - return False - if max_value and val > max_value: - return False - return True - - return _do - - -def validate_url_path(val): - """True if val is matched by the path component grammar in rfc3986.""" - - if not validate_str()(val): - return False - - uri = rfc3986.URIReference(None, None, val, None, None) - - return uri.path_is_valid() and val.startswith('/') - - -def validate_image_path(val): - if not validate_str()(val): - return False - - bucket_name = val.split('/')[0] - manifest_path = val[len(bucket_name) + 1:] - if not len(bucket_name) or not len(manifest_path): - return False - - if val[0] == '/': - return False - - # make sure the image path is rfc3986 compliant - # prepend '/' to make input validate - if not validate_url_path('/' + val): - return False - - return True - - -def validate_user_data(user_data): - """Check if the user_data is encoded properly.""" - try: - base64.b64decode(user_data) - except TypeError: - return False - return True - - -def validate(args, validator): - """Validate values of args against validators in validator. - - :param args: Dict of values to be validated. - :param validator: A dict where the keys map to keys in args - and the values are validators. - Applies each validator to ``args[key]`` - :returns: True if validation succeeds. Otherwise False. - - A validator should be a callable which accepts 1 argument and which - returns True if the argument passes validation. False otherwise. - A validator should not raise an exception to indicate validity of the - argument. - - Only validates keys which show up in both args and validator. - - """ - - for key in validator: - if key not in args: - continue - - f = validator[key] - assert callable(f) - - if not f(args[key]): - LOG.debug("%(key)s with value %(value)s failed" - " validator %(name)s", - {'key': key, 'value': args[key], 'name': f.__name__}) - return False - return True diff --git a/nova/availability_zones.py b/nova/availability_zones.py index 4ae4f3ef362..7c8d94890c4 100644 --- a/nova/availability_zones.py +++ b/nova/availability_zones.py @@ -16,9 +16,11 @@ """Availability zone helper functions.""" import collections -import nova.conf + +import six from nova import cache_utils +import nova.conf from nova import objects # NOTE(vish): azs don't change that often, so cache them for an hour to @@ -49,7 +51,9 @@ def reset_cache(): def _make_cache_key(host): - return "azcache-%s" % host.encode('utf-8') + if six.PY2: + host = host.encode('utf-8') + return "azcache-%s" % host def _build_metadata_by_host(aggregates, hosts=None): @@ -116,8 +120,12 @@ def get_availability_zones(context, get_only_available=False, :param with_hosts: whether to return hosts part of the AZs :type with_hosts: bool """ - enabled_services = objects.ServiceList.get_all(context, disabled=False, - set_zones=True) + # NOTE(danms): Avoid circular import + from nova import compute + hostapi = compute.HostAPI() + + enabled_services = hostapi.service_get_all( + context, {'disabled': False}, set_zones=True, all_cells=True) available_zones = [] for (zone, host) in [(service['availability_zone'], service['host']) @@ -132,8 +140,8 @@ def get_availability_zones(context, get_only_available=False, available_zones = list(_available_zones.items()) if not get_only_available: - disabled_services = objects.ServiceList.get_all(context, disabled=True, - set_zones=True) + disabled_services = hostapi.service_get_all( + context, {'disabled': True}, set_zones=True, all_cells=True) not_available_zones = [] azs = available_zones if not with_hosts else dict(available_zones) zones = [(service['availability_zone'], service['host']) @@ -156,7 +164,7 @@ def get_availability_zones(context, get_only_available=False, def get_instance_availability_zone(context, instance): """Return availability zone of specified instance.""" - host = instance.get('host') + host = instance.host if 'host' in instance else None if not host: # Likely hasn't reached a viable compute node yet so give back the # desired availability_zone in the instance record if the boot request diff --git a/nova/baserpc.py b/nova/baserpc.py index 95336a9da64..d51281ec1a1 100644 --- a/nova/baserpc.py +++ b/nova/baserpc.py @@ -18,18 +18,14 @@ Base RPC client and server common to all services. """ -from oslo_config import cfg import oslo_messaging as messaging from oslo_serialization import jsonutils +import nova.conf from nova import rpc -CONF = cfg.CONF -rpcapi_cap_opt = cfg.StrOpt('baseapi', - help='Set a version cap for messages sent to the base api in any ' - 'service') -CONF.register_opt(rpcapi_cap_opt, 'upgrade_levels') +CONF = nova.conf.CONF _NAMESPACE = 'baseapi' diff --git a/nova/block_device.py b/nova/block_device.py index b40aa5c7a0d..c3a3cc36240 100644 --- a/nova/block_device.py +++ b/nova/block_device.py @@ -17,7 +17,7 @@ from oslo_log import log as logging from oslo_utils import strutils -import six + import nova.conf from nova import exception @@ -45,10 +45,10 @@ 'guest_format', 'device_type', 'disk_bus', 'boot_index', 'device_name', 'delete_on_termination', 'snapshot_id', 'volume_id', 'volume_size', 'image_id', 'no_device', - 'connection_info']) + 'connection_info', 'tag']) -bdm_db_only_fields = set(['id', 'instance_uuid']) +bdm_db_only_fields = set(['id', 'instance_uuid', 'attachment_id']) bdm_db_inherited_fields = set(['created_at', 'updated_at', @@ -89,11 +89,11 @@ def __init__(self, bdm_dict=None, do_not_default=None, **kwargs): bdm_dict.get('delete_on_termination')) # NOTE (ndipanov): Never default db fields self.update({field: None for field in self._fields - do_not_default}) - self.update(list(six.iteritems(bdm_dict))) + self.update(bdm_dict.items()) def _validate(self, bdm_dict): """Basic data format validations.""" - dict_fields = set(key for key, _ in six.iteritems(bdm_dict)) + dict_fields = set(key for key, _ in bdm_dict.items()) # Check that there are no bogus fields if not (dict_fields <= @@ -139,7 +139,7 @@ def from_legacy(cls, legacy_bdm): non_computable_fields = set(['boot_index', 'disk_bus', 'guest_format', 'device_type']) - new_bdm = {fld: val for fld, val in six.iteritems(legacy_bdm) + new_bdm = {fld: val for fld, val in legacy_bdm.items() if fld in copy_over_fields} virt_name = legacy_bdm.get('virtual_name') @@ -184,10 +184,7 @@ def from_api(cls, api_dict, image_uuid_specified): device_uuid = api_dict.get('uuid') destination_type = api_dict.get('destination_type') - if source_type not in ('volume', 'image', 'snapshot', 'blank'): - raise exception.InvalidBDMFormat( - details=_("Invalid source_type field.")) - elif source_type == 'blank' and device_uuid: + if source_type == 'blank' and device_uuid: raise exception.InvalidBDMFormat( details=_("Invalid device UUID.")) elif source_type != 'blank': @@ -196,11 +193,14 @@ def from_api(cls, api_dict, image_uuid_specified): details=_("Missing device UUID.")) api_dict[source_type + '_id'] = device_uuid if source_type == 'image' and destination_type == 'local': - try: - boot_index = int(api_dict.get('boot_index', -1)) - except ValueError: - raise exception.InvalidBDMFormat( - details=_("Boot index is invalid.")) + # NOTE(mriedem): boot_index can be None so we need to + # account for that to avoid a TypeError. + boot_index = api_dict.get('boot_index', -1) + if boot_index is None: + # boot_index=None is equivalent to -1. + boot_index = -1 + boot_index = int(boot_index) + # if this bdm is generated from --image ,then # source_type = image and destination_type = local is allowed if not (image_uuid_specified and boot_index == 0): @@ -369,7 +369,7 @@ def from_legacy_mapping(legacy_block_device_mapping, image_uuid='', def properties_root_device_name(properties): - """get root device name from image meta data. + """Get root device name from image meta data. If it isn't specified, return None. """ root_device_name = None @@ -391,7 +391,7 @@ def validate_device_name(value): try: # NOTE (ndipanov): Do not allow empty device names # until assigning default values - # is supported by nova.compute + # are supported by nova.compute utils.check_string_length(value, 'Device name', min_length=1, max_length=255) except exception.InvalidInput: @@ -410,7 +410,7 @@ def validate_and_default_volume_size(bdm): bdm['volume_size'], 'volume_size', min_value=0) except exception.InvalidInput: # NOTE: We can remove this validation code after removing - # Nova v2.0 API code because v2.1 API validates this case + # Nova v2.0 API code, because v2.1 API validates this case # already at its REST API layer. raise exception.InvalidBDMFormat( details=_("Invalid volume_size.")) @@ -541,7 +541,9 @@ def instance_block_mapping(instance, bdms): # Right now sort by device name for deterministic # result. if ebs_devices: - ebs_devices.sort() + # NOTE(claudiub): python2.7 sort places None values first. + # this sort will maintain the same behaviour for both py27 and py34. + ebs_devices = sorted(ebs_devices, key=lambda x: (x is not None, x)) for nebs, ebs in enumerate(ebs_devices): mappings['ebs%d' % nebs] = ebs diff --git a/nova/cache_utils.py b/nova/cache_utils.py index 864e7b6d415..6a5bd4418ae 100644 --- a/nova/cache_utils.py +++ b/nova/cache_utils.py @@ -14,89 +14,45 @@ # License for the specific language governing permissions and limitations # under the License. -"""Super simple fake memcache client.""" - -import copy +"""Simple wrapper for oslo_cache.""" from oslo_cache import core as cache -from oslo_config import cfg +from oslo_log import log as logging +import nova.conf from nova.i18n import _ +CONF = nova.conf.CONF -# NOTE(dims): There are many copies of memcache_opts with memcached_servers -# in various projects as this used to be in a copy of memory_cache.py -# Since we are making a change in just our copy, oslo-config-generator fails -# with cfg.DuplicateOptError unless we override the comparison check -class _DeprecatedListOpt(cfg.ListOpt): - def __ne__(self, another): - self_dict = copy.deepcopy(vars(self)) - another_dict = copy.deepcopy(vars(another)) - self_dict.pop('help') - self_dict.pop('deprecated_for_removal') - another_dict.pop('help') - another_dict.pop('deprecated_for_removal') - return self_dict != another_dict - - -memcache_opts = [ - _DeprecatedListOpt('memcached_servers', - help='DEPRECATED: Memcached servers or None for in ' - 'process cache. "memcached_servers" opt is ' - 'deprecated in Mitaka. In Newton release ' - 'oslo.cache config options should be used as ' - 'this option will be removed. Please add a ' - '[cache] group in your nova.conf file and ' - 'add "enable" and "memcache_servers" option in ' - 'this section.', - deprecated_for_removal=True), -] - -CONF = cfg.CONF -CONF.register_opts(memcache_opts) +LOG = logging.getLogger(__name__) WEEK = 604800 -def list_opts(): - """Entry point for oslo-config-generator.""" - return [(None, copy.deepcopy(memcache_opts))] +def _warn_if_null_backend(): + if CONF.cache.backend == 'dogpile.cache.null': + LOG.warning("Cache enabled with backend dogpile.cache.null.") def get_memcached_client(expiration_time=0): """Used ONLY when memcached is explicitly needed.""" - # If the operator uses the old style [DEFAULT]/memcached_servers - # then we just respect that setting - if CONF.memcached_servers: - return CacheClient( - _get_custom_cache_region(expiration_time=expiration_time, - backend='dogpile.cache.memcached', - url=CONF.memcached_servers)) - # If the operator still uses the new style [cache]/memcache_servers - # and has [cache]/enabled flag on then we let oslo_cache configure - # the region from the configuration settings - elif CONF.cache.enabled and CONF.cache.memcache_servers: + # If the operator has [cache]/enabled flag on then we let oslo_cache + # configure the region from the configuration settings + if CONF.cache.enabled and CONF.cache.memcache_servers: + _warn_if_null_backend() return CacheClient( _get_default_cache_region(expiration_time=expiration_time)) - raise RuntimeError(_('memcached_servers not defined')) def get_client(expiration_time=0): """Used to get a caching client.""" - # If the operator still uses the old style [DEFAULT]/memcached_servers - # then we just respect that setting - if CONF.memcached_servers: - return CacheClient( - _get_custom_cache_region(expiration_time=expiration_time, - backend='dogpile.cache.memcached', - url=CONF.memcached_servers)) # If the operator has [cache]/enabled flag on then we let oslo_cache # configure the region from configuration settings. - elif CONF.cache.enabled: + if CONF.cache.enabled: + _warn_if_null_backend() return CacheClient( _get_default_cache_region(expiration_time=expiration_time)) - # If [cache]/enabled flag is off and [DEFAULT]/memcached_servers is - # absent we use the dictionary backend + # If [cache]/enabled flag is off, we use the dictionary backend return CacheClient( _get_custom_cache_region(expiration_time=expiration_time, backend='oslo_cache.dict')) diff --git a/nova/cells/__init__.py b/nova/cells/__init__.py index ea277b7f9c8..caf1eab3f2e 100644 --- a/nova/cells/__init__.py +++ b/nova/cells/__init__.py @@ -15,3 +15,5 @@ """ Cells """ + +TOPIC = 'cells' diff --git a/nova/cells/filters/__init__.py b/nova/cells/filters/__init__.py index 33fff0d0f6b..33988c2ccf0 100644 --- a/nova/cells/filters/__init__.py +++ b/nova/cells/filters/__init__.py @@ -18,7 +18,6 @@ """ from nova import filters -from nova import policy class BaseCellFilter(filters.BaseFilter): @@ -31,9 +30,7 @@ def authorized(self, ctxt): is the name of the filter class. """ name = 'cells_scheduler_filter:' + self.__class__.__name__ - target = {'project_id': ctxt.project_id, - 'user_id': ctxt.user_id} - return policy.enforce(ctxt, name, target, do_raise=False) + return ctxt.can(name, fatal=False) def _filter_one(self, cell, filter_properties): return self.cell_passes(cell, filter_properties) diff --git a/nova/cells/filters/target_cell.py b/nova/cells/filters/target_cell.py index 99618b51f3c..fdf2362daa1 100644 --- a/nova/cells/filters/target_cell.py +++ b/nova/cells/filters/target_cell.py @@ -24,7 +24,6 @@ from oslo_log import log as logging from nova.cells import filters -from nova.i18n import _LI LOG = logging.getLogger(__name__) @@ -56,8 +55,8 @@ def filter_all(self, cells, filter_properties): # No filtering, if not authorized. return cells - LOG.info(_LI("Forcing direct route to %(cell_name)s because " - "of 'target_cell' scheduler hint"), + LOG.info("Forcing direct route to %(cell_name)s because " + "of 'target_cell' scheduler hint", {'cell_name': cell_name}) scheduler = filter_properties['scheduler'] diff --git a/nova/cells/manager.py b/nova/cells/manager.py index d335e549b51..23d7cb7dc3a 100644 --- a/nova/cells/manager.py +++ b/nova/cells/manager.py @@ -22,18 +22,18 @@ from oslo_log import log as logging import oslo_messaging from oslo_service import periodic_task -from oslo_utils import importutils from oslo_utils import timeutils -import six + from six.moves import range from nova.cells import messaging +from nova.cells import rpc_driver as cells_rpc_driver from nova.cells import state as cells_state from nova.cells import utils as cells_utils +from nova.compute import rpcapi as compute_rpcapi import nova.conf from nova import context from nova import exception -from nova.i18n import _LW from nova import manager from nova import objects from nova.objects import base as base_obj @@ -61,18 +61,18 @@ class CellsManager(manager.Manager): Scheduling requests get passed to the scheduler class. """ - target = oslo_messaging.Target(version='1.37') + target = oslo_messaging.Target(version='1.38') def __init__(self, *args, **kwargs): - LOG.warning(_LW('The cells feature of Nova is considered experimental ' - 'by the OpenStack project because it receives much ' - 'less testing than the rest of Nova. This may change ' - 'in the future, but current deployers should be aware ' - 'that the use of it in production right now may be ' - 'risky. Also note that cells does not currently ' - 'support rolling upgrades, it is assumed that cells ' - 'deployments are upgraded lockstep so n-1 cells ' - 'compatibility does not work.')) + LOG.warning('The cells feature of Nova is considered experimental ' + 'by the OpenStack project because it receives much ' + 'less testing than the rest of Nova. This may change ' + 'in the future, but current deployers should be aware ' + 'that the use of it in production right now may be ' + 'risky. Also note that cells does not currently ' + 'support rolling upgrades, it is assumed that cells ' + 'deployments are upgraded lockstep so n-1 cells ' + 'compatibility does not work.') # Mostly for tests. cell_state_manager = kwargs.pop('cell_state_manager', None) super(CellsManager, self).__init__(service_name='cells', @@ -81,9 +81,7 @@ def __init__(self, *args, **kwargs): cell_state_manager = cells_state.CellStateManager self.state_manager = cell_state_manager() self.msg_runner = messaging.MessageRunner(self.state_manager) - cells_driver_cls = importutils.import_class( - CONF.cells.driver) - self.driver = cells_driver_cls() + self.driver = cells_rpc_driver.CellsRPCDriver() self.instances_to_heal = iter([]) def post_start_hook(self): @@ -315,7 +313,7 @@ def service_delete(self, ctxt, cell_service_id): @oslo_messaging.expected_exceptions(exception.CellRoutingInconsistency) def proxy_rpc_to_manager(self, ctxt, topic, rpc_message, call, timeout): """Proxy an RPC message as-is to a manager.""" - compute_topic = CONF.compute_topic + compute_topic = compute_rpcapi.RPC_TOPIC cell_and_host = topic[len(compute_topic) + 1:] cell_name, host_name = cells_utils.split_cell_and_item(cell_and_host) response = self.msg_runner.proxy_rpc_to_manager(ctxt, cell_name, @@ -358,7 +356,7 @@ def task_log_get_all(self, ctxt, task_name, period_beginning, @oslo_messaging.expected_exceptions(exception.CellRoutingInconsistency) def compute_node_get(self, ctxt, compute_id): - """Get a compute node by ID in a specific cell.""" + """Get a compute node by ID or UUID in a specific cell.""" cell_name, compute_id = cells_utils.split_cell_and_item( compute_id) response = self.msg_runner.compute_node_get(ctxt, cell_name, @@ -388,7 +386,7 @@ def compute_node_stats(self, ctxt): totals = {} for response in responses: data = response.value_or_raise() - for key, val in six.iteritems(data): + for key, val in data.items(): totals.setdefault(key, 0) totals[key] += val return totals @@ -452,8 +450,10 @@ def get_migrations(self, ctxt, filters): False, filters) migrations = [] for response in responses: - migrations += response.value_or_raise() - return migrations + # response.value_or_raise returns MigrationList objects. + # MigrationList.objects returns the list of Migration objects. + migrations.extend(response.value_or_raise().objects) + return objects.MigrationList(objects=migrations) def instance_update_from_api(self, ctxt, instance, expected_vm_state, expected_task_state, admin_state_reset): @@ -578,9 +578,9 @@ def get_keypair_at_top(self, ctxt, user_id, name): elif len(keypairs) > 1: cell_names = ', '.join([resp.cell_name for resp in responses if resp.value is not None]) - LOG.warning(_LW("The same keypair name '%(name)s' exists in the " - "following cells: %(cell_names)s. The keypair " - "value from the first cell is returned."), + LOG.warning("The same keypair name '%(name)s' exists in the " + "following cells: %(cell_names)s. The keypair " + "value from the first cell is returned.", {'name': name, 'cell_names': cell_names}) return keypairs[0] diff --git a/nova/cells/messaging.py b/nova/cells/messaging.py index 949e9401a78..75fb015cb76 100644 --- a/nova/cells/messaging.py +++ b/nova/cells/messaging.py @@ -52,7 +52,7 @@ from nova import context from nova.db import base from nova import exception -from nova.i18n import _, _LE, _LI, _LW +from nova.i18n import _ from nova import objects from nova.objects import base as objects_base from nova import rpc @@ -159,7 +159,7 @@ def __init__(self, msg_runner, ctxt, method_name, method_kwargs, self.max_hop_count = max_hop_count self.is_broadcast = False self._append_hop() - # Each sub-class should set this when the message is inited + # Each sub-class should set this when the message is initialized self.next_hops = [] self.resp_queue = None self.serializer = objects_base.NovaObjectSerializer() @@ -190,7 +190,7 @@ def _process_locally(self): except Exception: resp_value = sys.exc_info() failure = True - LOG.exception(_LE("Error processing message locally")) + LOG.exception("Error processing message locally") return Response(self.ctxt, self.routing_path, resp_value, failure) def _setup_response_queue(self): @@ -399,7 +399,7 @@ def process(self): next_hop = self._get_next_hop() except Exception: exc_info = sys.exc_info() - LOG.exception(_LE("Error locating next hop for message")) + LOG.exception("Error locating next hop for message") return self._send_response_from_exception(exc_info) if next_hop.is_me: @@ -425,7 +425,7 @@ def process(self): next_hop.send_message(self) except Exception: exc_info = sys.exc_info() - err_str = _LE("Failed to send message to cell: %(next_hop)s") + err_str = "Failed to send message to cell: %(next_hop)s" LOG.exception(err_str, {'next_hop': next_hop}) self._cleanup_response_queue() return self._send_response_from_exception(exc_info) @@ -503,7 +503,7 @@ def process(self): next_hops = self._get_next_hops() except Exception: exc_info = sys.exc_info() - LOG.exception(_LE("Error locating next hops for message")) + LOG.exception("Error locating next hops for message") return self._send_response_from_exception(exc_info) # Short circuit if we don't need to respond @@ -522,7 +522,7 @@ def process(self): # Error just trying to send to cells. Send a single response # with the failure. exc_info = sys.exc_info() - LOG.exception(_LE("Error sending message to next hops.")) + LOG.exception("Error sending message to next hops.") self._cleanup_response_queue() return self._send_response_from_exception(exc_info) @@ -539,8 +539,7 @@ def process(self): # Error waiting for responses, most likely a timeout. # Send a single response back with the failure. exc_info = sys.exc_info() - LOG.exception(_LE("Error waiting for responses from" - " neighbor cells")) + LOG.exception("Error waiting for responses from neighbor cells") return self._send_response_from_exception(exc_info) if local_response: @@ -748,7 +747,9 @@ def proxy_rpc_to_manager(self, message, host_name, rpc_message, cctxt.cast(message.ctxt, method, **kwargs) def compute_node_get(self, message, compute_id): - """Get compute node by ID.""" + """Get compute node by ID or UUID.""" + if uuidutils.is_uuid_like(compute_id): + return objects.ComputeNode.get_by_uuid(message.ctxt, compute_id) return objects.ComputeNode.get_by_id(message.ctxt, compute_id) def actions_get(self, message, instance_uuid): @@ -1008,8 +1009,10 @@ def instance_update_at_top(self, message, instance, **kwargs): instance.obj_reset_changes(items_to_remove) instance.cell_name = _reverse_path(message.routing_path) + # instance.display_name could be unicode + instance_repr = utils.get_obj_repr_unicode(instance) LOG.debug("Got update for instance: %(instance)s", - {'instance': instance}, instance_uuid=instance.uuid) + {'instance': instance_repr}, instance_uuid=instance.uuid) expected_vm_state = self._get_expected_vm_state(instance) expected_task_state = self._get_expected_task_state(instance) @@ -1098,8 +1101,8 @@ def sync_instances(self, message, project_id, updated_since, deleted, **kwargs): projid_str = project_id is None and "" or project_id since_str = updated_since is None and "" or updated_since - LOG.info(_LI("Forcing a sync of instances, project_id=" - "%(projid_str)s, updated_since=%(since_str)s"), + LOG.info("Forcing a sync of instances, project_id=" + "%(projid_str)s, updated_since=%(since_str)s", {'projid_str': projid_str, 'since_str': since_str}) if updated_since is not None: updated_since = timeutils.parse_isotime(updated_since) @@ -1116,7 +1119,7 @@ def service_get_all(self, message, filters): services = objects.ServiceList.get_all(message.ctxt, disabled=disabled) ret_services = [] for service in services: - for key, val in six.iteritems(filters): + for key, val in filters.items(): if getattr(service, key) != val: break else: @@ -1178,7 +1181,7 @@ def bdm_update_or_create_at_top(self, message, bdm, create): if vol_id and instance_bdm['volume_id'] == vol_id: break else: - LOG.warning(_LW("No match when trying to update BDM: %(bdm)s"), + LOG.warning("No match when trying to update BDM: %(bdm)s", dict(bdm=bdm)) return LOG.debug('Calling db.block_device_mapping_update from API cell with ' @@ -1259,7 +1262,7 @@ def __init__(self, state_manager): self.response_queues = {} self.methods_by_type = {} self.our_name = CONF.cells.name - for msg_type, cls in six.iteritems(_CELL_MESSAGE_TYPE_TO_METHODS_CLS): + for msg_type, cls in _CELL_MESSAGE_TYPE_TO_METHODS_CLS.items(): self.methods_by_type[msg_type] = cls(self) self.serializer = objects_base.NovaObjectSerializer() @@ -1586,7 +1589,7 @@ def compute_node_stats(self, ctxt): return message.process() def compute_node_get(self, ctxt, cell_name, compute_id): - """Return compute node entry from a specific cell by ID.""" + """Return compute node entry from a specific cell by ID or UUID.""" method_kwargs = dict(compute_id=compute_id) message = _TargetedMessage(self, ctxt, 'compute_node_get', method_kwargs, 'down', @@ -1672,7 +1675,7 @@ def _instance_action(self, ctxt, instance, method, extra_kwargs=None, """Call instance_ in correct cell for instance.""" cell_name = instance.cell_name if not cell_name: - LOG.warning(_LW("No cell_name for %(method)s() from API"), + LOG.warning("No cell_name for %(method)s() from API", dict(method=method), instance=instance) return method_kwargs = {'instance': instance} @@ -1689,7 +1692,7 @@ def instance_update_from_api(self, ctxt, instance, """Update an instance object in its cell.""" cell_name = instance.cell_name if not cell_name: - LOG.warning(_LW("No cell_name for instance update from API"), + LOG.warning("No cell_name for instance update from API", instance=instance) return method_kwargs = {'instance': instance, @@ -1877,7 +1880,7 @@ def serialize_remote_exception(failure_info, log_failure=True): tb = traceback.format_exception(*failure_info) failure = failure_info[1] if log_failure: - LOG.error(_LE("Returning exception %s to caller"), + LOG.error("Returning exception %s to caller", six.text_type(failure)) LOG.error(tb) diff --git a/nova/cells/opts.py b/nova/cells/opts.py index 027524e8c80..f9d122d5e46 100644 --- a/nova/cells/opts.py +++ b/nova/cells/opts.py @@ -26,7 +26,3 @@ def get_cell_type(): if not CONF.cells.enable: return return CONF.cells.cell_type - - -def list_opts(): - return [] diff --git a/nova/cells/rpc_driver.py b/nova/cells/rpc_driver.py index 898f135ca9c..3f45af6e32f 100644 --- a/nova/cells/rpc_driver.py +++ b/nova/cells/rpc_driver.py @@ -121,8 +121,8 @@ def _get_transport(self, next_hop): """ transport_url = next_hop.db_info['transport_url'] if transport_url not in self.transports: - transport = messaging.get_transport(nova.conf.CONF, transport_url, - rpc.TRANSPORT_ALIASES) + transport = messaging.get_rpc_transport( + nova.conf.CONF, transport_url) self.transports[transport_url] = transport else: transport = self.transports[transport_url] diff --git a/nova/cells/rpcapi.py b/nova/cells/rpcapi.py index 2c63b57ae28..f2f0adc211e 100644 --- a/nova/cells/rpcapi.py +++ b/nova/cells/rpcapi.py @@ -26,12 +26,14 @@ from oslo_log import log as logging import oslo_messaging as messaging from oslo_serialization import jsonutils +from oslo_utils import uuidutils +from nova import cells import nova.conf from nova import exception -from nova.i18n import _LE from nova import objects from nova.objects import base as objects_base +from nova import profiler from nova import rpc LOG = logging.getLogger(__name__) @@ -39,6 +41,7 @@ CONF = nova.conf.CONF +@profiler.trace_cls("rpc") class CellsAPI(object): '''Cells client-side RPC API @@ -114,9 +117,12 @@ class CellsAPI(object): * 1.36 - Added 'delete_type' parameter to terminate_instance() * 1.37 - Add get_keypair_at_top to fetch keypair from api cell - ... Liberty supports message version 1.37. So, any changes to - existing methods in 1.x after that point should be done such that they - can handle the version_cap being set to 1.37. + ... Liberty, Mitaka, Newton, and Ocata support message version 1.37. + So, any changes to existing methods in 1.x after that point should be + done such that they can handle the version_cap being set to + 1.37. + + * 1.38 - Handle uuid parameter in compute_node_get() method. ''' VERSION_ALIASES = { @@ -126,11 +132,14 @@ class CellsAPI(object): 'juno': '1.29', 'kilo': '1.34', 'liberty': '1.37', + 'mitaka': '1.37', + 'newton': '1.37', + 'ocata': '1.37', } def __init__(self): super(CellsAPI, self).__init__() - target = messaging.Target(topic=CONF.cells.topic, version='1.0') + target = messaging.Target(topic=cells.TOPIC, version='1.0') version_cap = self.VERSION_ALIASES.get(CONF.upgrade_levels.cells, CONF.upgrade_levels.cells) # NOTE(sbauza): Yes, this is ugly but cells_utils is calling cells.db @@ -322,8 +331,7 @@ def proxy_rpc_to_manager(self, ctxt, rpc_message, topic, call=False, return cctxt.call(ctxt, 'proxy_rpc_to_manager', topic=topic, rpc_message=rpc_message, - call=call, - timeout=timeout) + call=call) def task_log_get_all(self, ctxt, task_name, period_beginning, period_ending, host=None, state=None): @@ -336,8 +344,16 @@ def task_log_get_all(self, ctxt, task_name, period_beginning, host=host, state=state) def compute_node_get(self, ctxt, compute_id): - """Get a compute node by ID in a specific cell.""" - cctxt = self.client.prepare(version='1.4') + """Get a compute node by ID or UUID in a specific cell.""" + version = '1.38' + if uuidutils.is_uuid_like(compute_id): + if not self.client.can_send_version(version): + LOG.warning('Unable to get compute node by UUID %s; service ' + 'is too old or the version is capped.', compute_id) + raise exception.ComputeHostNotFound(host=compute_id) + else: + version = '1.4' + cctxt = self.client.prepare(version=version) return cctxt.call(ctxt, 'compute_node_get', compute_id=compute_id) def compute_node_get_all(self, ctxt, hypervisor_match=None): @@ -414,7 +430,7 @@ def bdm_update_or_create_at_top(self, ctxt, bdm, create=None): cctxt.cast(ctxt, 'bdm_update_or_create_at_top', bdm=bdm, create=create) except Exception: - LOG.exception(_LE("Failed to notify cells of BDM update/create.")) + LOG.exception("Failed to notify cells of BDM update/create.") def bdm_destroy_at_top(self, ctxt, instance_uuid, device_name=None, volume_id=None): @@ -428,7 +444,7 @@ def bdm_destroy_at_top(self, ctxt, instance_uuid, device_name=None, device_name=device_name, volume_id=volume_id) except Exception: - LOG.exception(_LE("Failed to notify cells of BDM destroy.")) + LOG.exception("Failed to notify cells of BDM destroy.") def get_migrations(self, ctxt, filters): """Get all migrations applying the filters.""" @@ -555,8 +571,13 @@ def soft_delete_instance(self, ctxt, instance, reservations=None): cctxt.cast(ctxt, 'soft_delete_instance', instance=instance) def resize_instance(self, ctxt, instance, extra_instance_updates, - scheduler_hint, flavor, reservations, - clean_shutdown=True): + scheduler_hint, flavor, reservations=None, + clean_shutdown=True, + request_spec=None): + # NOTE(sbauza): Since Cells v1 is quite feature-frozen, we don't want + # to pass down request_spec to the manager and rather keep the + # cell conductor providing a new RequestSpec like the original + # behaviour flavor_p = jsonutils.to_primitive(flavor) version = '1.33' msg_args = {'instance': instance, @@ -585,12 +606,12 @@ def live_migrate_instance(self, ctxt, instance, host_name, host_name=host_name) def revert_resize(self, ctxt, instance, migration, host, - reservations): + reservations=None): cctxt = self.client.prepare(version='1.21') cctxt.cast(ctxt, 'revert_resize', instance=instance) def confirm_resize(self, ctxt, instance, migration, host, - reservations, cast=True): + reservations=None, cast=True): # NOTE(comstud): This is only used in the API cell where we should # always cast and ignore the 'cast' kwarg. # Also, the compute api method normally takes an optional diff --git a/nova/cells/scheduler.py b/nova/cells/scheduler.py index 8e27498c30a..91d70850ff0 100644 --- a/nova/cells/scheduler.py +++ b/nova/cells/scheduler.py @@ -31,7 +31,6 @@ import nova.conf from nova.db import base from nova import exception -from nova.i18n import _LE, _LI from nova import objects from nova.objects import base as obj_base from nova.scheduler import utils as scheduler_utils @@ -84,6 +83,9 @@ def _create_instances_here(self, ctxt, instance_uuids, instance_properties, # FIXME(danms): Same for ec2_ids instance_values.pop('ec2_ids', None) + # FIXME(danms): Same for keypairs + instance_values.pop('keypairs', None) + instances = [] num_instances = len(instance_uuids) security_groups = ( @@ -104,6 +106,10 @@ def _create_instances_here(self, ctxt, instance_uuids, instance_properties, security_groups, block_device_mapping, num_instances, i) + block_device_mapping = ( + self.compute_api._bdm_validate_set_size_and_instance( + ctxt, instance, instance_type, block_device_mapping)) + self.compute_api._create_block_device_mapping(block_device_mapping) instances.append(instance) self.msg_runner.instance_update_at_top(ctxt, instance) @@ -180,11 +186,11 @@ def _build_instances(self, message, target_cells, instance_uuids, build_inst_kwargs) return except Exception: - LOG.exception(_LE("Couldn't communicate with cell '%s'"), + LOG.exception("Couldn't communicate with cell '%s'", target_cell.name) # FIXME(comstud): Would be nice to kick this back up so that # the parent cell could retry, if we had a parent. - LOG.error(_LE("Couldn't communicate with any cells")) + LOG.error("Couldn't communicate with any cells") raise exception.NoCellsAvailable() def build_instances(self, message, build_inst_kwargs): @@ -221,13 +227,13 @@ def _schedule_build_to_cells(self, message, instance_uuids, if i == max(0, CONF.cells.scheduler_retries): raise sleep_time = max(1, CONF.cells.scheduler_retry_delay) - LOG.info(_LI("No cells available when scheduling. Will " - "retry in %(sleep_time)s second(s)"), + LOG.info("No cells available when scheduling. Will " + "retry in %(sleep_time)s second(s)", {'sleep_time': sleep_time}) time.sleep(sleep_time) continue except Exception: - LOG.exception(_LE("Error scheduling instances %(instance_uuids)s"), + LOG.exception("Error scheduling instances %(instance_uuids)s", {'instance_uuids': instance_uuids}) ctxt = message.ctxt for instance_uuid in instance_uuids: diff --git a/nova/cells/state.py b/nova/cells/state.py index a61989fed9e..1d471220f2d 100644 --- a/nova/cells/state.py +++ b/nova/cells/state.py @@ -35,7 +35,6 @@ from nova import context from nova.db import base from nova import exception -from nova.i18n import _LE from nova import objects from nova import rpc from nova import servicegroup @@ -63,7 +62,7 @@ def __init__(self, cell_name, is_me=False): def update_db_info(self, cell_db_info): """Update cell credentials from db.""" - self.db_info = {k: v for k, v in six.iteritems(cell_db_info) + self.db_info = {k: v for k, v in cell_db_info.items() if k != 'name'} def update_capabilities(self, cell_metadata): @@ -165,7 +164,7 @@ def __init__(self, cell_state_cls=None): attempts += 1 if attempts > 120: raise - LOG.exception(_LE('DB error')) + LOG.exception('DB error') time.sleep(30) my_cell_capabs = {} @@ -275,12 +274,10 @@ def _get_compute_hosts(): continue chost = compute_hosts[host] - chost['free_ram_mb'] += compute['free_ram_mb'] - free_disk = compute['free_disk_gb'] * 1024 - chost['free_disk_mb'] += free_disk - chost['total_ram_mb'] += compute['memory_mb'] - total_disk = compute['local_gb'] * 1024 - chost['total_disk_mb'] += total_disk + chost['free_ram_mb'] += max(0, compute.free_ram_mb) + chost['free_disk_mb'] += max(0, compute.free_disk_gb) * 1024 + chost['total_ram_mb'] += max(0, compute.memory_mb) + chost['total_disk_mb'] += max(0, compute.local_gb) * 1024 _get_compute_hosts() if not compute_hosts: @@ -300,12 +297,12 @@ def _free_units(total, free, per_inst): else: return 0 - instance_types = self.db.flavor_get_all(ctxt) + flavors = objects.FlavorList.get_all(ctxt) memory_mb_slots = frozenset( - [inst_type['memory_mb'] for inst_type in instance_types]) + [flavor.memory_mb for flavor in flavors]) disk_mb_slots = frozenset( - [(inst_type['root_gb'] + inst_type['ephemeral_gb']) * units.Ki - for inst_type in instance_types]) + [(flavor.root_gb + flavor.ephemeral_gb) * units.Ki + for flavor in flavors]) for compute_values in compute_hosts.values(): total_ram_mb_free += compute_values['free_ram_mb'] @@ -365,8 +362,8 @@ def update_cell_capabilities(self, cell_name, capabilities): cell = (self.child_cells.get(cell_name) or self.parent_cells.get(cell_name)) if not cell: - LOG.error(_LE("Unknown cell '%(cell_name)s' when trying to " - "update capabilities"), + LOG.error("Unknown cell '%(cell_name)s' when trying to " + "update capabilities", {'cell_name': cell_name}) return # Make sure capabilities are sets. @@ -380,8 +377,8 @@ def update_cell_capacities(self, cell_name, capacities): cell = (self.child_cells.get(cell_name) or self.parent_cells.get(cell_name)) if not cell: - LOG.error(_LE("Unknown cell '%(cell_name)s' when trying to " - "update capacities"), + LOG.error("Unknown cell '%(cell_name)s' when trying to " + "update capacities", {'cell_name': cell_name}) return cell.update_capacities(capacities) diff --git a/nova/cells/utils.py b/nova/cells/utils.py index e196470e139..8c3b4563fbd 100644 --- a/nova/cells/utils.py +++ b/nova/cells/utils.py @@ -34,7 +34,7 @@ # meaningful PATH_CELL_SEP in an invalid way will need to suffice. BLOCK_SYNC_FLAG = '!!' # Separator used between cell name and item -_CELL_ITEM_SEP = '@' +CELL_ITEM_SEP = '@' CONF = nova.conf.CONF @@ -69,6 +69,20 @@ def __getitem__(self, key): return getattr(self._obj, key) + def __contains__(self, key): + """Pass-through "in" check to the wrapped object. + + This is needed to proxy any types of checks in the calling code + like:: + + if 'availability_zone' in service: + ... + + :param key: They key to look for in the wrapped object. + :returns: True if key is in the wrapped object, False otherwise. + """ + return key in self._obj + def obj_to_primitive(self): obj_p = self._obj.obj_to_primitive() obj_p['cell_proxy.class_name'] = self.__class__.__name__ @@ -123,7 +137,9 @@ def __getattr__(self, key): # ComputeNode object that consumers of this Proxy don't use, we can # safely remove it from what's returned raise AttributeError - return getattr(self._obj, key) + # NOTE(claudiub): needed for py34 compatibility. + # get self._obj first, without ending into an infinite recursion. + return getattr(self.__getattribute__("_obj"), key) def get_instances_to_sync(context, updated_since=None, project_id=None, @@ -176,12 +192,12 @@ def cell_with_item(cell_name, item): """Turn cell_name and item into @.""" if cell_name is None: return item - return cell_name + _CELL_ITEM_SEP + str(item) + return cell_name + CELL_ITEM_SEP + str(item) def split_cell_and_item(cell_and_item): """Split a combined cell@item and return them.""" - result = cell_and_item.rsplit(_CELL_ITEM_SEP, 1) + result = cell_and_item.rsplit(CELL_ITEM_SEP, 1) if len(result) == 1: return (None, cell_and_item) else: diff --git a/nova/cells/weights/mute_child.py b/nova/cells/weights/mute_child.py index eaf62577f81..6cf7f46817c 100644 --- a/nova/cells/weights/mute_child.py +++ b/nova/cells/weights/mute_child.py @@ -23,7 +23,6 @@ from nova.cells import weights import nova.conf -from nova.i18n import _LW LOG = logging.getLogger(__name__) @@ -52,8 +51,8 @@ def _weigh_object(self, cell, weight_properties): if timeutils.is_older_than(last_seen, secs): # yep, that's a mute child; recommend highly that it be skipped! - LOG.warning(_LW("%(cell)s has not been seen since %(last_seen)s " - "and is being treated as mute."), + LOG.warning("%(cell)s has not been seen since %(last_seen)s " + "and is being treated as mute.", {'cell': cell, 'last_seen': last_seen}) return self.MUTE_WEIGH_VALUE else: diff --git a/nova/cert/manager.py b/nova/cert/manager.py deleted file mode 100644 index f986a515255..00000000000 --- a/nova/cert/manager.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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. -""" -Cert manager manages x509 certificates. - -**Related Flags** - -:cert_topic: What :mod:`rpc` topic to listen to (default: `cert`). -:cert_manager: The module name of a class derived from - :class:`manager.Manager` (default: - :class:`nova.cert.manager.Manager`). -""" - -import base64 - -import oslo_messaging as messaging - -from nova import crypto -from nova import manager - - -class CertManager(manager.Manager): - - target = messaging.Target(version='2.0') - - def __init__(self, *args, **kwargs): - super(CertManager, self).__init__(service_name='cert', - *args, **kwargs) - - def init_host(self): - crypto.ensure_ca_filesystem() - - def revoke_certs_by_user(self, context, user_id): - """Revoke all user certs.""" - return crypto.revoke_certs_by_user(user_id) - - def revoke_certs_by_project(self, context, project_id): - """Revoke all project certs.""" - return crypto.revoke_certs_by_project(project_id) - - def revoke_certs_by_user_and_project(self, context, user_id, project_id): - """Revoke certs for user in project.""" - return crypto.revoke_certs_by_user_and_project(user_id, project_id) - - def generate_x509_cert(self, context, user_id, project_id): - """Generate and sign a cert for user in project.""" - return crypto.generate_x509_cert(user_id, project_id) - - def fetch_ca(self, context, project_id): - """Get root ca for a project.""" - return crypto.fetch_ca(project_id) - - def fetch_crl(self, context, project_id): - """Get crl for a project.""" - return crypto.fetch_crl(project_id) - - def decrypt_text(self, context, project_id, text): - """Decrypt base64 encoded text using the projects private key.""" - return crypto.decrypt_text(project_id, base64.b64decode(text)) diff --git a/nova/cert/rpcapi.py b/nova/cert/rpcapi.py deleted file mode 100644 index 537b715d2e4..00000000000 --- a/nova/cert/rpcapi.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright 2013, Red Hat, Inc. -# -# 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. - -""" -Client side of the cert manager RPC API. -""" - -import oslo_messaging as messaging - -import nova.conf -from nova import rpc - -CONF = nova.conf.CONF - - -class CertAPI(object): - '''Client side of the cert rpc API. - - API version history: - - 1.0 - Initial version. - 1.1 - Added get_backdoor_port() - - ... Grizzly and Havana support message version 1.1. So, any changes to - existing methods in 1.x after that point should be done such that they - can handle the version_cap being set to 1.1. - - 2.0 - Major API rev for Icehouse - - ... Icehouse, Juno, Kilo, and Liberty support message version - 2.0. So, any changes to existing methods in 2.x after that - point should be done such that they can handle the version_cap - being set to 2.0. - - ''' - - VERSION_ALIASES = { - 'grizzly': '1.1', - 'havana': '1.1', - 'icehouse': '2.0', - 'juno': '2.0', - 'kilo': '2.0', - 'liberty': '2.0', - } - - def __init__(self): - super(CertAPI, self).__init__() - target = messaging.Target(topic=CONF.cert_topic, version='2.0') - version_cap = self.VERSION_ALIASES.get(CONF.upgrade_levels.cert, - CONF.upgrade_levels.cert) - self.client = rpc.get_client(target, version_cap=version_cap) - - def revoke_certs_by_user(self, ctxt, user_id): - cctxt = self.client.prepare() - return cctxt.call(ctxt, 'revoke_certs_by_user', user_id=user_id) - - def revoke_certs_by_project(self, ctxt, project_id): - cctxt = self.client.prepare() - return cctxt.call(ctxt, 'revoke_certs_by_project', - project_id=project_id) - - def revoke_certs_by_user_and_project(self, ctxt, user_id, project_id): - cctxt = self.client.prepare() - return cctxt.call(ctxt, 'revoke_certs_by_user_and_project', - user_id=user_id, project_id=project_id) - - def generate_x509_cert(self, ctxt, user_id, project_id): - cctxt = self.client.prepare() - return cctxt.call(ctxt, 'generate_x509_cert', - user_id=user_id, - project_id=project_id) - - def fetch_ca(self, ctxt, project_id): - cctxt = self.client.prepare() - return cctxt.call(ctxt, 'fetch_ca', project_id=project_id) - - def fetch_crl(self, ctxt, project_id): - cctxt = self.client.prepare() - return cctxt.call(ctxt, 'fetch_crl', project_id=project_id) - - def decrypt_text(self, ctxt, project_id, text): - cctxt = self.client.prepare() - return cctxt.call(ctxt, 'decrypt_text', - project_id=project_id, - text=text) diff --git a/nova/cloudpipe/__init__.py b/nova/cloudpipe/__init__.py deleted file mode 100644 index aa6c77a5aa8..00000000000 --- a/nova/cloudpipe/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -""" -:mod:`nova.cloudpipe` -- VPN Server Management -===================================================== - -.. automodule:: nova.cloudpipe - :platform: Unix - :synopsis: An OpenVPN server for every nova user. -""" diff --git a/nova/cloudpipe/bootscript.template b/nova/cloudpipe/bootscript.template deleted file mode 100755 index c6fbc08621e..00000000000 --- a/nova/cloudpipe/bootscript.template +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -# This gets zipped and run on the cloudpipe-managed OpenVPN server - -export LC_ALL=C -export VPN_IP=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{print $$1}'` -export BROADCAST=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f3 | awk '{print $$1}'` -export DHCP_MASK=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f4 | awk '{print $$1}'` -export GATEWAY=`netstat -r | grep default | cut -d' ' -f10` -# Need a higher valued MAC address than eth0, to prevent the TAP MAC address -# from becoming the bridge MAC address. Since Essex eth0 MAC starts with -# FA:16:3E, we'll thus generate a MAC starting with FA:17:3E to be higher than eth0. -export RANDOM_TAP_MAC=`openssl rand -hex 8 | sed 's/\(..\)/\1:/g' | cut -b-8 | awk '{print "FA:17:3E:"$$1}'` - -DHCP_LOWER=`echo $$BROADCAST | awk -F. '{print $$1"."$$2"."$$3"." $$4 - ${num_vpn} }'` -DHCP_UPPER=`echo $$BROADCAST | awk -F. '{print $$1"."$$2"."$$3"." $$4 - 1 }'` - -# generate a server DH -openssl dhparam -out /etc/openvpn/dh1024.pem 1024 - -cp crl.pem /etc/openvpn/ -cp server.key /etc/openvpn/ -cp ca.crt /etc/openvpn/ -cp server.crt /etc/openvpn/ -# Customize the server.conf.template -cd /etc/openvpn - -sed -e s/VPN_IP/$$VPN_IP/g server.conf.template > server.conf -sed -i -e s/DHCP_SUBNET/$$DHCP_MASK/g server.conf -sed -i -e s/DHCP_LOWER/$$DHCP_LOWER/g server.conf -sed -i -e s/DHCP_UPPER/$$DHCP_UPPER/g server.conf -sed -i -e s/max-clients\ 1/max-clients\ 10/g server.conf - -echo "push \"route ${dmz_net} ${dmz_mask} $$GATEWAY\"" >> server.conf -echo "duplicate-cn" >> server.conf -echo "crl-verify /etc/openvpn/crl.pem" >> server.conf -echo "lladdr $$RANDOM_TAP_MAC" >> server.conf - -/etc/init.d/openvpn start diff --git a/nova/cloudpipe/client.ovpn.template b/nova/cloudpipe/client.ovpn.template deleted file mode 100644 index dd48b556f14..00000000000 --- a/nova/cloudpipe/client.ovpn.template +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -# NOVA user connection -# Edit the following lines to point to your cert files: -cert {{ certfile }} -key {{ keyfile }} - -ca cacert.pem - -client -dev tap -proto udp - -remote {{ ip }} {{ port }} -resolv-retry infinite -nobind - -# Downgrade privileges after initialization (non-Windows only) -user nobody -group nogroup -comp-lzo - -# Set log file verbosity. -verb 2 - -keepalive 10 120 -ping-timer-rem -persist-tun -persist-key diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py deleted file mode 100644 index a18d7430d2f..00000000000 --- a/nova/cloudpipe/pipelib.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -""" -CloudPipe - Build a user-data payload zip file, and launch -an instance with it. - -""" - -import os -import string -import zipfile - -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import fileutils - -from nova import compute -from nova.compute import flavors -from nova import crypto -from nova import db -from nova import exception -from nova.i18n import _ -from nova import paths -from nova import utils - - -cloudpipe_opts = [ - cfg.StrOpt('vpn_image_id', - default='0', - help='Image ID used when starting up a cloudpipe vpn server'), - cfg.StrOpt('vpn_flavor', - default='m1.tiny', - help=_('Flavor for vpn instances')), - cfg.StrOpt('boot_script_template', - default=paths.basedir_def('nova/cloudpipe/bootscript.template'), - help=_('Template for cloudpipe instance boot script')), - cfg.StrOpt('dmz_net', - default='10.0.0.0', - help=_('Network to push into openvpn config')), - cfg.StrOpt('dmz_mask', - default='255.255.255.0', - help=_('Netmask to push into openvpn config')), - cfg.StrOpt('vpn_key_suffix', - default='-vpn', - help='Suffix to add to project name for vpn key and secgroups'), - ] - -CONF = cfg.CONF -CONF.register_opts(cloudpipe_opts) -CONF.import_opt('keys_path', 'nova.crypto') - -LOG = logging.getLogger(__name__) - - -def is_vpn_image(image_id): - return image_id == CONF.vpn_image_id - - -def _load_boot_script(): - with open(CONF.boot_script_template, "r") as shellfile: - s = string.Template(shellfile.read()) - - CONF.import_opt('cnt_vpn_clients', 'nova.network.manager') - - return s.substitute(dmz_net=CONF.dmz_net, - dmz_mask=CONF.dmz_mask, - num_vpn=CONF.cnt_vpn_clients) - - -class CloudPipe(object): - def __init__(self, skip_policy_check=False): - self.compute_api = compute.API(skip_policy_check=skip_policy_check) - - def get_encoded_zip(self, project_id): - # Make a payload.zip - with utils.tempdir() as tmpdir: - filename = "payload.zip" - zippath = os.path.join(tmpdir, filename) - z = zipfile.ZipFile(zippath, "w", zipfile.ZIP_DEFLATED) - boot_script = _load_boot_script() - # genvpn, sign csr - crypto.generate_vpn_files(project_id) - z.writestr('autorun.sh', boot_script) - crl = os.path.join(crypto.ca_folder(project_id), 'crl.pem') - z.write(crl, 'crl.pem') - server_key = os.path.join(crypto.ca_folder(project_id), - 'server.key') - z.write(server_key, 'server.key') - ca_crt = os.path.join(crypto.ca_path(project_id)) - z.write(ca_crt, 'ca.crt') - server_crt = os.path.join(crypto.ca_folder(project_id), - 'server.crt') - z.write(server_crt, 'server.crt') - z.close() - with open(zippath, "r") as zippy: - # NOTE(vish): run instances expects encoded userdata, - # it is decoded in the get_metadata_call. - # autorun.sh also decodes the zip file, - # hence the double encoding. - encoded = zippy.read().encode("base64").encode("base64") - - return encoded - - def launch_vpn_instance(self, context): - LOG.debug("Launching VPN for %s", context.project_id) - key_name = self.setup_key_pair(context) - group_name = self.setup_security_group(context) - flavor = flavors.get_flavor_by_name(CONF.vpn_flavor) - instance_name = '%s%s' % (context.project_id, CONF.vpn_key_suffix) - user_data = self.get_encoded_zip(context.project_id) - return self.compute_api.create(context, - flavor, - CONF.vpn_image_id, - display_name=instance_name, - user_data=user_data, - key_name=key_name, - security_group=[group_name]) - - def setup_security_group(self, context): - group_name = '%s%s' % (context.project_id, CONF.vpn_key_suffix) - group = {'user_id': context.user_id, - 'project_id': context.project_id, - 'name': group_name, - 'description': 'Group for vpn'} - try: - group_ref = db.security_group_create(context, group) - except exception.SecurityGroupExists: - return group_name - rule = {'parent_group_id': group_ref['id'], - 'cidr': '0.0.0.0/0', - 'protocol': 'udp', - 'from_port': 1194, - 'to_port': 1194} - db.security_group_rule_create(context, rule) - rule = {'parent_group_id': group_ref['id'], - 'cidr': '0.0.0.0/0', - 'protocol': 'icmp', - 'from_port': -1, - 'to_port': -1} - db.security_group_rule_create(context, rule) - # NOTE(vish): No need to trigger the group since the instance - # has not been run yet. - return group_name - - def setup_key_pair(self, context): - key_name = '%s%s' % (context.project_id, CONF.vpn_key_suffix) - try: - keypair_api = compute.api.KeypairAPI() - result, private_key = keypair_api.create_key_pair(context, - context.user_id, - key_name) - key_dir = os.path.join(CONF.keys_path, context.user_id) - fileutils.ensure_tree(key_dir) - key_path = os.path.join(key_dir, '%s.pem' % key_name) - with open(key_path, 'w') as f: - f.write(private_key) - except (exception.KeyPairExists, os.error, IOError): - pass - return key_name diff --git a/nova/cmd/all.py b/nova/cmd/all.py deleted file mode 100644 index 8ac5f393b9a..00000000000 --- a/nova/cmd/all.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -"""Starter script for all nova services. - -This script attempts to start all the nova services in one process. Each -service is started in its own greenthread. Please note that exceptions and -sys.exit() on the starting of a service are logged and the script will -continue attempting to launch the rest of the services. - -""" - -import sys - -from oslo_log import log as logging - -import nova.conf -from nova import config -from nova.i18n import _LE -from nova import objects -from nova import service -from nova import utils -from nova.vnc import xvp_proxy - - -CONF = nova.conf.CONF -CONF.import_opt('enabled_apis', 'nova.service') -CONF.import_opt('enabled_ssl_apis', 'nova.service') - - -def main(): - config.parse_args(sys.argv) - logging.setup(CONF, "nova") - LOG = logging.getLogger('nova.all') - utils.monkey_patch() - objects.register_all() - launcher = service.process_launcher() - - # nova-api - for api in CONF.enabled_apis: - try: - should_use_ssl = api in CONF.enabled_ssl_apis - server = service.WSGIService(api, use_ssl=should_use_ssl) - launcher.launch_service(server, workers=server.workers or 1) - except (Exception, SystemExit): - LOG.exception(_LE('Failed to load %s-api'), api) - - for mod in [xvp_proxy]: - try: - launcher.launch_service(mod.get_wsgi_server()) - except (Exception, SystemExit): - LOG.exception(_LE('Failed to load %s'), mod.__name__) - - for binary in ['nova-compute', 'nova-network', 'nova-scheduler', - 'nova-cert', 'nova-conductor']: - - # FIXME(sirp): Most service configs are defined in nova/service.py, but - # conductor has set a new precedent of storing these configs - # nova//api.py. - # - # We should update the existing services to use this new approach so we - # don't have to treat conductor differently here. - if binary == 'nova-conductor': - topic = CONF.conductor.topic - manager = CONF.conductor.manager - else: - topic = None - manager = None - - try: - launcher.launch_service(service.Service.create(binary=binary, - topic=topic, - manager=manager)) - except (Exception, SystemExit): - LOG.exception(_LE('Failed to load %s'), binary) - launcher.wait() diff --git a/nova/cmd/api.py b/nova/cmd/api.py index a63871b565f..f2a324f2b23 100644 --- a/nova/cmd/api.py +++ b/nova/cmd/api.py @@ -22,19 +22,18 @@ import sys -from oslo_config import cfg from oslo_log import log as logging from oslo_reports import guru_meditation_report as gmr +import nova.conf from nova import config +from nova import exception from nova import objects from nova import service from nova import utils from nova import version -CONF = cfg.CONF -CONF.import_opt('enabled_apis', 'nova.service') -CONF.import_opt('enabled_ssl_apis', 'nova.service') +CONF = nova.conf.CONF def main(): @@ -42,12 +41,29 @@ def main(): logging.setup(CONF, "nova") utils.monkey_patch() objects.register_all() + if 'osapi_compute' in CONF.enabled_apis: + # NOTE(mriedem): This is needed for caching the nova-compute service + # version. + objects.Service.enable_min_version_cache() + log = logging.getLogger(__name__) gmr.TextGuruMeditation.setup_autorun(version) launcher = service.process_launcher() + started = 0 for api in CONF.enabled_apis: should_use_ssl = api in CONF.enabled_ssl_apis - server = service.WSGIService(api, use_ssl=should_use_ssl) - launcher.launch_service(server, workers=server.workers or 1) + try: + server = service.WSGIService(api, use_ssl=should_use_ssl) + launcher.launch_service(server, workers=server.workers or 1) + started += 1 + except exception.PasteAppNotFound as ex: + log.warning("%s. ``enabled_apis`` includes bad values. " + "Fix to remove this warning.", ex) + + if started == 0: + log.error('No APIs were started. ' + 'Check the enabled_apis config option.') + sys.exit(1) + launcher.wait() diff --git a/nova/cmd/api_metadata.py b/nova/cmd/api_metadata.py index dabc4cd5550..4cbb9c76e18 100644 --- a/nova/cmd/api_metadata.py +++ b/nova/cmd/api_metadata.py @@ -32,7 +32,6 @@ CONF = nova.conf.CONF -CONF.import_opt('enabled_ssl_apis', 'nova.service') def main(): @@ -43,9 +42,7 @@ def main(): gmr.TextGuruMeditation.setup_autorun(version) - if not CONF.conductor.use_local: - objects_base.NovaObject.indirection_api = \ - conductor_rpcapi.ConductorAPI() + objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI() should_use_ssl = 'metadata' in CONF.enabled_ssl_apis server = service.WSGIService('metadata', use_ssl=should_use_ssl) diff --git a/nova/cmd/api_os_compute.py b/nova/cmd/api_os_compute.py index 9a700808312..78cc805c22d 100644 --- a/nova/cmd/api_os_compute.py +++ b/nova/cmd/api_os_compute.py @@ -18,10 +18,10 @@ import sys -from oslo_config import cfg from oslo_log import log as logging from oslo_reports import guru_meditation_report as gmr +import nova.conf from nova import config from nova import objects from nova import service @@ -29,8 +29,7 @@ from nova import version -CONF = cfg.CONF -CONF.import_opt('enabled_ssl_apis', 'nova.service') +CONF = nova.conf.CONF def main(): @@ -38,6 +37,9 @@ def main(): logging.setup(CONF, "nova") utils.monkey_patch() objects.register_all() + # NOTE(mriedem): This is needed for caching the nova-compute service + # version. + objects.Service.enable_min_version_cache() gmr.TextGuruMeditation.setup_autorun(version) diff --git a/nova/cmd/baseproxy.py b/nova/cmd/baseproxy.py index ef27470faf5..f7b30679025 100644 --- a/nova/cmd/baseproxy.py +++ b/nova/cmd/baseproxy.py @@ -20,22 +20,17 @@ import os import sys -from oslo_config import cfg from oslo_log import log as logging from oslo_reports import guru_meditation_report as gmr +import nova.conf +from nova.conf import novnc from nova.console import websocketproxy from nova import version -CONF = cfg.CONF -CONF.import_opt('record', 'nova.cmd.novnc') -CONF.import_opt('daemon', 'nova.cmd.novnc') -CONF.import_opt('ssl_only', 'nova.cmd.novnc') -CONF.import_opt('source_is_ipv6', 'nova.cmd.novnc') -CONF.import_opt('cert', 'nova.cmd.novnc') -CONF.import_opt('key', 'nova.cmd.novnc') -CONF.import_opt('web', 'nova.cmd.novnc') +CONF = nova.conf.CONF +novnc.register_cli_opts(CONF) def exit_with_error(msg, errno=-1): @@ -61,13 +56,12 @@ def proxy(host, port): listen_host=host, listen_port=port, source_is_ipv6=CONF.source_is_ipv6, - verbose=CONF.verbose, cert=CONF.cert, key=CONF.key, ssl_only=CONF.ssl_only, daemon=CONF.daemon, record=CONF.record, - traffic=CONF.verbose and not CONF.daemon, + traffic=not CONF.daemon, web=CONF.web, file_only=True, RequestHandlerClass=websocketproxy.NovaProxyRequestHandler diff --git a/nova/cmd/cells.py b/nova/cmd/cells.py index b74867c96b0..7303ed0889f 100644 --- a/nova/cmd/cells.py +++ b/nova/cmd/cells.py @@ -20,6 +20,7 @@ from oslo_log import log as logging from oslo_reports import guru_meditation_report as gmr +from nova import cells import nova.conf from nova import config from nova import objects @@ -28,6 +29,7 @@ from nova import version CONF = nova.conf.CONF +LOG = logging.getLogger('nova.cells') def main(): @@ -38,8 +40,10 @@ def main(): gmr.TextGuruMeditation.setup_autorun(version) + LOG.warning('Cells v1 is deprecated in favor of Cells v2 and will be ' + 'removed in the future.') server = service.Service.create(binary='nova-cells', - topic=CONF.cells.topic, - manager=CONF.cells.manager) + topic=cells.TOPIC, + manager='nova.cells.manager.CellsManager') service.serve(server) service.wait() diff --git a/nova/cmd/cert.py b/nova/cmd/cert.py deleted file mode 100644 index 9e199d77978..00000000000 --- a/nova/cmd/cert.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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. - -"""Starter script for Nova Cert.""" - -import sys - -from oslo_log import log as logging -from oslo_reports import guru_meditation_report as gmr - -import nova.conf -from nova import config -from nova import objects -from nova import service -from nova import utils -from nova import version - -CONF = nova.conf.CONF - - -def main(): - config.parse_args(sys.argv) - logging.setup(CONF, "nova") - utils.monkey_patch() - objects.register_all() - - gmr.TextGuruMeditation.setup_autorun(version) - - server = service.Service.create(binary='nova-cert', topic=CONF.cert_topic) - service.serve(server) - service.wait() diff --git a/nova/cmd/common.py b/nova/cmd/common.py new file mode 100644 index 00000000000..494483f97b8 --- /dev/null +++ b/nova/cmd/common.py @@ -0,0 +1,183 @@ +# Copyright 2016 Cloudbase Solutions Srl +# 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. + +""" + Common functions used by different CLI interfaces. +""" + +from __future__ import print_function + +import argparse +import traceback + +from oslo_log import log as logging +import six + +import nova.conf +import nova.db.api +from nova import exception +from nova.i18n import _ +from nova import utils + +CONF = nova.conf.CONF +LOG = logging.getLogger(__name__) + + +def block_db_access(service_name): + """Blocks Nova DB access.""" + + class NoDB(object): + def __getattr__(self, attr): + return self + + def __call__(self, *args, **kwargs): + stacktrace = "".join(traceback.format_stack()) + LOG.error('No db access allowed in %(service_name)s: ' + '%(stacktrace)s', + dict(service_name=service_name, stacktrace=stacktrace)) + raise exception.DBNotAllowed(service_name) + + nova.db.api.IMPL = NoDB() + + +# Decorators for actions +def args(*args, **kwargs): + """Decorator which adds the given args and kwargs to the args list of + the desired func's __dict__. + """ + def _decorator(func): + func.__dict__.setdefault('args', []).insert(0, (args, kwargs)) + return func + return _decorator + + +def methods_of(obj): + """Get all callable methods of an object that don't start with underscore + + returns a list of tuples of the form (method_name, method) + """ + result = [] + for i in dir(obj): + if callable(getattr(obj, i)) and not i.startswith('_'): + result.append((i, getattr(obj, i))) + return result + + +def add_command_parsers(subparsers, categories): + """Adds command parsers to the given subparsers. + + Adds version and bash-completion parsers. + Adds a parser with subparsers for each category in the categories dict + given. + """ + parser = subparsers.add_parser('version') + + parser = subparsers.add_parser('bash-completion') + parser.add_argument('query_category', nargs='?') + + for category in categories: + command_object = categories[category]() + + desc = getattr(command_object, 'description', None) + parser = subparsers.add_parser(category, description=desc) + parser.set_defaults(command_object=command_object) + + category_subparsers = parser.add_subparsers(dest='action') + + for (action, action_fn) in methods_of(command_object): + parser = category_subparsers.add_parser( + action, description=getattr(action_fn, 'description', desc)) + + action_kwargs = [] + for args, kwargs in getattr(action_fn, 'args', []): + # we must handle positional parameters (ARG) separately from + # positional parameters (--opt). Detect this by checking for + # the presence of leading '--' + if args[0] != args[0].lstrip('-'): + kwargs.setdefault('dest', args[0].lstrip('-')) + if kwargs['dest'].startswith('action_kwarg_'): + action_kwargs.append( + kwargs['dest'][len('action_kwarg_'):]) + else: + action_kwargs.append(kwargs['dest']) + kwargs['dest'] = 'action_kwarg_' + kwargs['dest'] + else: + action_kwargs.append(args[0]) + args = ['action_kwarg_' + arg for arg in args] + + parser.add_argument(*args, **kwargs) + + parser.set_defaults(action_fn=action_fn) + parser.set_defaults(action_kwargs=action_kwargs) + + parser.add_argument('action_args', nargs='*', + help=argparse.SUPPRESS) + + +def print_bash_completion(categories): + if not CONF.category.query_category: + print(" ".join(categories.keys())) + elif CONF.category.query_category in categories: + fn = categories[CONF.category.query_category] + command_object = fn() + actions = methods_of(command_object) + print(" ".join([k for (k, v) in actions])) + + +def get_action_fn(): + fn = CONF.category.action_fn + fn_args = [] + for arg in CONF.category.action_args: + if isinstance(arg, six.binary_type): + arg = arg.decode('utf-8') + fn_args.append(arg) + + fn_kwargs = {} + for k in CONF.category.action_kwargs: + v = getattr(CONF.category, 'action_kwarg_' + k) + if v is None: + continue + if isinstance(v, six.binary_type): + v = v.decode('utf-8') + fn_kwargs[k] = v + + # call the action with the remaining arguments + # check arguments + missing = utils.validate_args(fn, *fn_args, **fn_kwargs) + if missing: + # NOTE(mikal): this isn't the most helpful error message ever. It is + # long, and tells you a lot of things you probably don't want to know + # if you just got a single arg wrong. + print(fn.__doc__) + CONF.print_help() + raise exception.Invalid( + _("Missing arguments: %s") % ", ".join(missing)) + + return fn, fn_args, fn_kwargs + + +def action_description(text): + """Decorator for adding a description to command action. + + To display help text on action call instead of common category help text + action function can be decorated. + + command -h will show description and arguments. + + """ + def _decorator(func): + func.description = text + return func + return _decorator diff --git a/nova/cmd/compute.py b/nova/cmd/compute.py index 8dc627cb39c..eb48853d45f 100644 --- a/nova/cmd/compute.py +++ b/nova/cmd/compute.py @@ -16,18 +16,19 @@ """Starter script for Nova Compute.""" +import shlex import sys -import traceback +import os_vif from oslo_log import log as logging +from oslo_privsep import priv_context from oslo_reports import guru_meditation_report as gmr +from nova.cmd import common as cmd_common +from nova.compute import rpcapi as compute_rpcapi from nova.conductor import rpcapi as conductor_rpcapi import nova.conf from nova import config -import nova.db.api -from nova import exception -from nova.i18n import _LE, _LW from nova import objects from nova.objects import base as objects_base from nova import service @@ -35,42 +36,24 @@ from nova import version CONF = nova.conf.CONF -CONF.import_opt('compute_topic', 'nova.compute.rpcapi') LOG = logging.getLogger('nova.compute') -def block_db_access(): - class NoDB(object): - def __getattr__(self, attr): - return self - - def __call__(self, *args, **kwargs): - stacktrace = "".join(traceback.format_stack()) - LOG.error(_LE('No db access allowed in nova-compute: %s'), - stacktrace) - raise exception.DBNotAllowed('nova-compute') - - nova.db.api.IMPL = NoDB() - - def main(): config.parse_args(sys.argv) logging.setup(CONF, 'nova') + priv_context.init(root_helper=shlex.split(utils.get_root_helper())) utils.monkey_patch() objects.register_all() + # Ensure os-vif objects are registered and plugins loaded + os_vif.initialize() gmr.TextGuruMeditation.setup_autorun(version) - if not CONF.conductor.use_local: - block_db_access() - objects_base.NovaObject.indirection_api = \ - conductor_rpcapi.ConductorAPI() - else: - LOG.warning(_LW('Conductor local mode is deprecated and will ' - 'be removed in a subsequent release')) - + cmd_common.block_db_access('nova-compute') + objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI() + objects.Service.enable_min_version_cache() server = service.Service.create(binary='nova-compute', - topic=CONF.compute_topic, - db_allowed=CONF.conductor.use_local) + topic=compute_rpcapi.RPC_TOPIC) service.serve(server) service.wait() diff --git a/nova/cmd/conductor.py b/nova/cmd/conductor.py index 6dc3500a678..12e4f71cfbe 100644 --- a/nova/cmd/conductor.py +++ b/nova/cmd/conductor.py @@ -40,8 +40,7 @@ def main(): gmr.TextGuruMeditation.setup_autorun(version) server = service.Service.create(binary='nova-conductor', - topic=CONF.conductor.topic, - manager=CONF.conductor.manager) + topic=CONF.conductor.topic) workers = CONF.conductor.workers or processutils.get_worker_count() service.serve(server, workers=workers) service.wait() diff --git a/nova/cmd/console.py b/nova/cmd/console.py index 83d64f15cd5..107374f46f1 100644 --- a/nova/cmd/console.py +++ b/nova/cmd/console.py @@ -22,12 +22,12 @@ from oslo_reports import guru_meditation_report as gmr from nova import config +from nova.console import rpcapi as console_rpcapi from nova import objects from nova import service from nova import version CONF = cfg.CONF -CONF.import_opt('console_topic', 'nova.console.rpcapi') def main(): @@ -38,6 +38,6 @@ def main(): gmr.TextGuruMeditation.setup_autorun(version) server = service.Service.create(binary='nova-console', - topic=CONF.console_topic) + topic=console_rpcapi.RPC_TOPIC) service.serve(server) service.wait() diff --git a/nova/cmd/consoleauth.py b/nova/cmd/consoleauth.py index ecfd44dcd1e..41204f394ab 100644 --- a/nova/cmd/consoleauth.py +++ b/nova/cmd/consoleauth.py @@ -17,18 +17,17 @@ import sys -from oslo_config import cfg from oslo_log import log as logging from oslo_reports import guru_meditation_report as gmr +import nova.conf from nova import config +from nova.consoleauth import rpcapi from nova import objects from nova import service from nova import version -CONF = cfg.CONF - -CONF.import_opt('consoleauth_topic', 'nova.consoleauth') +CONF = nova.conf.CONF def main(): @@ -39,6 +38,6 @@ def main(): gmr.TextGuruMeditation.setup_autorun(version) server = service.Service.create(binary='nova-consoleauth', - topic=CONF.consoleauth_topic) + topic=rpcapi.RPC_TOPIC) service.serve(server) service.wait() diff --git a/nova/cmd/dhcpbridge.py b/nova/cmd/dhcpbridge.py index 69fb949e627..3d3ab9dbad5 100644 --- a/nova/cmd/dhcpbridge.py +++ b/nova/cmd/dhcpbridge.py @@ -22,28 +22,23 @@ import os import sys -import traceback from oslo_config import cfg from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_utils import importutils +from nova.cmd import common as cmd_common from nova.conductor import rpcapi as conductor_rpcapi import nova.conf from nova import config from nova import context -import nova.db.api -from nova import exception -from nova.i18n import _LE, _LW from nova.network import rpcapi as network_rpcapi from nova import objects from nova.objects import base as objects_base from nova import rpc CONF = nova.conf.CONF -CONF.import_opt('host', 'nova.netconf') -CONF.import_opt('network_manager', 'nova.service') LOG = logging.getLogger(__name__) @@ -83,12 +78,17 @@ def add_action_parsers(subparsers): # is passed if known. We don't care about # hostname, but argparse will complain if we # do not accept it. - for action in ['add', 'del', 'old']: + actions = { + 'add': add_lease, + 'del': del_lease, + 'old': old_lease, + } + for action, func in actions.items(): parser = subparsers.add_parser(action) parser.add_argument('mac') parser.add_argument('ip') parser.add_argument('hostname', nargs='?', default='') - parser.set_defaults(func=globals()[action + '_lease']) + parser.set_defaults(func=func) CONF.register_cli_opt( @@ -98,20 +98,6 @@ def add_action_parsers(subparsers): handler=add_action_parsers)) -def block_db_access(): - class NoDB(object): - def __getattr__(self, attr): - return self - - def __call__(self, *args, **kwargs): - stacktrace = "".join(traceback.format_stack()) - LOG.error(_LE('No db access allowed in nova-dhcpbridge: %s'), - stacktrace) - raise exception.DBNotAllowed('nova-dhcpbridge') - - nova.db.api.IMPL = NoDB() - - def main(): """Parse environment and arguments and call the appropriate action.""" config.parse_args(sys.argv, @@ -130,13 +116,8 @@ def main(): objects.register_all() - if not CONF.conductor.use_local: - block_db_access() - objects_base.NovaObject.indirection_api = \ - conductor_rpcapi.ConductorAPI() - else: - LOG.warning(_LW('Conductor local mode is deprecated and will ' - 'be removed in a subsequent release')) + cmd_common.block_db_access('nova-dhcpbridge') + objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI() if CONF.action.name in ['add', 'del']: LOG.debug("Called '%(action)s' for mac '%(mac)s' with IP '%(ip)s'", @@ -148,8 +129,8 @@ def main(): try: network_id = int(os.environ.get('NETWORK_ID')) except TypeError: - LOG.error(_LE("Environment variable 'NETWORK_ID' must be set.")) - return(1) + LOG.error("Environment variable 'NETWORK_ID' must be set.") + return 1 print(init_leases(network_id)) diff --git a/nova/cmd/idmapshift.py b/nova/cmd/idmapshift.py index 3b8946e7ff9..93b6d2193e6 100644 --- a/nova/cmd/idmapshift.py +++ b/nova/cmd/idmapshift.py @@ -81,10 +81,11 @@ import argparse -from nova.i18n import _ import os import sys +from nova.i18n import _ + NOBODY_ID = 65534 diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index a5724ab0345..5f4365b77e6 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -55,9 +55,11 @@ from __future__ import print_function import argparse +import functools import os +import re import sys -import urllib +import traceback import decorator import netaddr @@ -67,10 +69,16 @@ import oslo_messaging as messaging from oslo_utils import importutils from oslo_utils import uuidutils +import prettytable + import six +import six.moves.urllib.parse as urlparse +from sqlalchemy.engine import url as sqla_url from nova.api.ec2 import ec2utils from nova import availability_zones +from nova.cmd import common as cmd_common +import nova.conf from nova import config from nova import context from nova import db @@ -78,48 +86,34 @@ from nova import exception from nova.i18n import _ from nova import objects -from nova.openstack.common import cliutils +from nova.objects import aggregate as aggregate_obj +from nova.objects import build_request as build_request_obj +from nova.objects import flavor as flavor_obj +from nova.objects import host_mapping as host_mapping_obj +from nova.objects import instance as instance_obj +from nova.objects import instance_group as instance_group_obj +from nova.objects import keypair as keypair_obj +from nova.objects import quotas as quotas_obj +from nova.objects import request_spec from nova import quota from nova import rpc -from nova import servicegroup from nova import utils from nova import version +from nova.virt import ironic -CONF = cfg.CONF -CONF.import_opt('network_manager', 'nova.service') -CONF.import_opt('service_down_time', 'nova.service') -CONF.import_opt('flat_network_bridge', 'nova.network.manager') -CONF.import_opt('num_networks', 'nova.network.manager') -CONF.import_opt('multi_host', 'nova.network.manager') -CONF.import_opt('network_size', 'nova.network.manager') -CONF.import_opt('vlan_start', 'nova.network.manager') -CONF.import_opt('vpn_start', 'nova.network.manager') -CONF.import_opt('default_floating_pool', 'nova.network.floating_ips') -CONF.import_opt('public_interface', 'nova.network.linux_net') -CONF.import_opt('connection', 'oslo_db.options', group='database') +CONF = nova.conf.CONF QUOTAS = quota.QUOTAS -_DEFAULT_LOG_LEVELS = config._DEFAULT_LOG_LEVELS + ['oslo_db=INFO'] +# Keep this list sorted and one entry per line for readability. +_EXTRA_DEFAULT_LOG_LEVELS = ['oslo_concurrency=INFO', + 'oslo_db=INFO', + 'oslo_policy=INFO'] # Decorators for actions -def args(*args, **kwargs): - def _decorator(func): - func.__dict__.setdefault('args', []).insert(0, (args, kwargs)) - return func - return _decorator - - -def deprecate(msg): - """Decorator which print the deprecation message before the decorated - function is called - """ - @decorator.decorator - def _deprecate(f, *args, **kwargs): - print(msg, file=sys.stderr) - return f(*args, **kwargs) - return _deprecate +args = cmd_common.args +action_description = cmd_common.action_description def param2id(object_id): @@ -132,32 +126,23 @@ def param2id(object_id): return object_id -class VpnCommands(object): - """Class for managing VPNs.""" +def mask_passwd_in_url(url): + parsed = urlparse.urlparse(url) + safe_netloc = re.sub(':.*@', ':****@', parsed.netloc) + new_parsed = urlparse.ParseResult( + parsed.scheme, safe_netloc, + parsed.path, parsed.params, + parsed.query, parsed.fragment) + return urlparse.urlunparse(new_parsed) - @args('--project', dest='project_id', metavar='', - help='Project name') - @args('--ip', metavar='', help='IP Address') - @args('--port', metavar='', help='Port') - def change(self, project_id, ip, port): - """Change the IP and port for a VPN. - This will update all networks associated with a project - not sure if that's the desired behavior or not, patches accepted. - - """ - # TODO(tr3buchet): perhaps this shouldn't update all networks - # associated with a project in the future - admin_context = context.get_admin_context() - networks = db.project_get_networks(admin_context, project_id) - for network in networks: - db.network_update(admin_context, - network['id'], - {'vpn_public_address': ip, - 'vpn_public_port': int(port)}) +class ShellCommands(object): + # TODO(stephenfin): Remove this during the Queens cycle + description = ('DEPRECATED: The shell commands are deprecated since ' + 'Pike as they serve no useful purpose in modern nova. ' + 'They will be removed in an upcoming release.') -class ShellCommands(object): def bpython(self): """Runs a bpython shell. @@ -239,9 +224,34 @@ def _db_error(caught_exception): sys.exit(1) +class QuotaCommands(object): + """Class for managing quotas.""" + + # TODO(melwitt): Remove this during the Queens cycle + description = ('DEPRECATED: The quota commands are deprecated since ' + 'Pike as quota usage is counted from resources instead ' + 'of being tracked separately. They will be removed in an ' + 'upcoming release.') + + @args('--project', dest='project_id', metavar='', + help='Project Id', required=True) + @args('--user', dest='user_id', metavar='', + help='User Id') + @args('--key', metavar='', help='Key') + def refresh(self, project_id, user_id=None, key=None): + """DEPRECATED: This command is deprecated and no longer does anything. + """ + pass + + class ProjectCommands(object): """Class for managing projects.""" + # TODO(stephenfin): Remove this during the Queens cycle + description = ('DEPRECATED: The project commands are deprecated since ' + 'Pike as this information is available over the API. They ' + 'will be removed in an upcoming release.') + @args('--project', dest='project_id', metavar='', help='Project name') @args('--user', dest='user_id', metavar='', @@ -275,25 +285,25 @@ def quota(self, project_id, user_id=None, key=None, value=None): value = -1 if int(value) < -1: print(_('Quota limit must be -1 or greater.')) - return(2) + return 2 if ((int(value) < minimum) and (maximum != -1 or (maximum == -1 and int(value) != -1))): print(_('Quota limit must be greater than %s.') % minimum) - return(2) + return 2 if maximum != -1 and int(value) > maximum: print(_('Quota limit must be less than %s.') % maximum) - return(2) + return 2 try: - db.quota_create(ctxt, project_id, key, value, - user_id=user_id) + objects.Quotas.create_limit(ctxt, project_id, key, value, + user_id=user_id) except exception.QuotaExists: - db.quota_update(ctxt, project_id, key, value, - user_id=user_id) + objects.Quotas.update_limit(ctxt, project_id, key, value, + user_id=user_id) else: print(_('%(key)s is not a valid quota key. Valid options are: ' '%(options)s.') % {'key': key, 'options': ', '.join(quota)}) - return(2) + return 2 print_format = "%-36s %-10s %-10s %-10s" print(print_format % ( _('Quota'), @@ -305,125 +315,42 @@ def quota(self, project_id, user_id=None, key=None, value=None): quota = QUOTAS.get_user_quotas(ctxt, project_id, user_id) else: quota = QUOTAS.get_project_quotas(ctxt, project_id) - for key, value in six.iteritems(quota): - if value['limit'] < 0 or value['limit'] is None: + for key, value in quota.items(): + if value['limit'] is None or value['limit'] < 0: value['limit'] = 'unlimited' print(print_format % (key, value['limit'], value['in_use'], value['reserved'])) - @args('--project', dest='project_id', metavar='', - help='Project name') - def scrub(self, project_id): - """Deletes data associated with project.""" - admin_context = context.get_admin_context() - networks = db.project_get_networks(admin_context, project_id) - for network in networks: - db.network_disassociate(admin_context, network['id']) - groups = db.security_group_get_by_project(admin_context, project_id) - for group in groups: - db.security_group_destroy(admin_context, group['id']) - - -AccountCommands = ProjectCommands - - -class FixedIpCommands(object): - """Class for managing fixed IP.""" - - @args('--host', metavar='', help='Host') - def list(self, host=None): - """Lists all fixed IPs (optionally by host).""" - ctxt = context.get_admin_context() - - try: - if host is None: - fixed_ips = db.fixed_ip_get_all(ctxt) - else: - fixed_ips = db.fixed_ip_get_by_host(ctxt, host) - - except exception.NotFound as ex: - print(_("error: %s") % ex) - return(2) - - instances = db.instance_get_all(context.get_admin_context()) - instances_by_uuid = {} - for instance in instances: - instances_by_uuid[instance['uuid']] = instance - - print("%-18s\t%-15s\t%-15s\t%s" % (_('network'), - _('IP address'), - _('hostname'), - _('host'))) - - all_networks = {} - try: - # use network_get_all to retrieve all existing networks - # this is to ensure that IPs associated with deleted networks - # will not throw exceptions. - for network in db.network_get_all(context.get_admin_context()): - all_networks[network.id] = network - except exception.NoNetworksFound: - # do not have any networks, so even if there are IPs, these - # IPs should have been deleted ones, so return. - print(_('No fixed IP found.')) - return - - has_ip = False - for fixed_ip in fixed_ips: - hostname = None - host = None - network = all_networks.get(fixed_ip['network_id']) - if network: - has_ip = True - if fixed_ip.get('instance_uuid'): - instance = instances_by_uuid.get(fixed_ip['instance_uuid']) - if instance: - hostname = instance['hostname'] - host = instance['host'] - else: - print(_('WARNING: fixed IP %s allocated to missing' - ' instance') % str(fixed_ip['address'])) - print("%-18s\t%-15s\t%-15s\t%s" % ( - network['cidr'], - fixed_ip['address'], - hostname, host)) - - if not has_ip: - print(_('No fixed IP found.')) - - @args('--address', metavar='', help='IP address') - def reserve(self, address): - """Mark fixed IP as reserved - - arguments: address + @args('--project', dest='project_id', metavar='', + help='Project Id', required=True) + @args('--user', dest='user_id', metavar='', + help='User Id') + @args('--key', metavar='', help='Key') + def quota_usage_refresh(self, project_id, user_id=None, key=None): + """DEPRECATED: This command is deprecated and no longer does anything. """ - return self._set_reserved(address, True) - - @args('--address', metavar='', help='IP address') - def unreserve(self, address): - """Mark fixed IP as free to use + # TODO(melwitt): Remove this during the Queens cycle + pass - arguments: address - """ - return self._set_reserved(address, False) - def _set_reserved(self, address, reserved): - ctxt = context.get_admin_context() +class AccountCommands(ProjectCommands): + """Class for managing projects.""" - try: - fixed_ip = db.fixed_ip_get_by_address(ctxt, address) - if fixed_ip is None: - raise exception.NotFound('Could not find address') - db.fixed_ip_update(ctxt, fixed_ip['address'], - {'reserved': reserved}) - except exception.NotFound as ex: - print(_("error: %s") % ex) - return(2) + # TODO(stephenfin): Remove this during the Queens cycle + description = ('DEPRECATED: The account commands are deprecated since ' + 'Pike as this information is available over the API. They ' + 'will be removed in an upcoming release.') class FloatingIpCommands(object): """Class for managing floating IP.""" + # TODO(stephenfin): Remove these when we remove cells v1 + description = ('DEPRECATED: Floating IP commands are deprecated since ' + 'nova-network is deprecated in favor of Neutron. The ' + 'floating IP commands will be removed in an upcoming ' + 'release.') + @staticmethod def address_to_hosts(addresses): """Iterate over hosts within an address range. @@ -470,7 +397,7 @@ def create(self, ip_range, pool=None, interface=None): # instead of printing, but logging isn't used here and I # don't know why. print('error: %s' % exc) - return(1) + return 1 @args('--ip_range', metavar='', help='IP range') def delete(self, ip_range): @@ -515,13 +442,18 @@ def validate_network_plugin(f, *args, **kwargs): if utils.is_neutron(): print(_("ERROR: Network commands are not supported when using the " "Neutron API. Use python-neutronclient instead.")) - return(2) + return 2 return f(*args, **kwargs) class NetworkCommands(object): """Class for managing networks.""" + # TODO(stephenfin): Remove these when we remove cells v1 + description = ('DEPRECATED: Network commands are deprecated since ' + 'nova-network is deprecated in favor of Neutron. The ' + 'network commands will be removed in an upcoming release.') + @validate_network_plugin @args('--label', metavar='