Add admin endpoint for amphora info
Administrators can now use /v2.0/octavia/amphorae to retrieve internal information about amphora details like compute_id and lb_network_ip. Change-Id: I5ac8d1ce189db09d52e518d42aeb3a192b8a8814
This commit is contained in:
parent
b8e0f5e763
commit
7f1c5011ed
@ -1,6 +1,12 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# Path fields
|
# Path fields
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
path-amphora-id:
|
||||||
|
description: |
|
||||||
|
The ID of the amphora to query.
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
path-healthmonitor-id:
|
path-healthmonitor-id:
|
||||||
description: |
|
description: |
|
||||||
The ID of the health monitor to query.
|
The ID of the health monitor to query.
|
||||||
@ -125,6 +131,25 @@ admin_state_up-optional:
|
|||||||
in: body
|
in: body
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
|
amphora-id:
|
||||||
|
description: |
|
||||||
|
The associated amphora ID.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
amphora-role:
|
||||||
|
description: |
|
||||||
|
The role of the amphora. One of ``STANDALONE``, ``MASTER``, ``BACKUP``.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
amphora-status:
|
||||||
|
description: |
|
||||||
|
The status of the amphora. One of: ``BOOTING``, ``ALLOCATED``, ``READY``,
|
||||||
|
``PENDING_DELETE``, ``DELETED``, ``ERROR``.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
api_links:
|
api_links:
|
||||||
description: |
|
description: |
|
||||||
Links to the resources in question.
|
Links to the resources in question.
|
||||||
@ -160,6 +185,24 @@ bytes_out:
|
|||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: integer
|
type: integer
|
||||||
|
cert-busy:
|
||||||
|
description: |
|
||||||
|
Whether the certificate is in the process of being replaced.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
cert-expiration:
|
||||||
|
description: |
|
||||||
|
The date the certificate for the amphora expires.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
compute-id:
|
||||||
|
description: |
|
||||||
|
The ID of the amphora resource in the compute system.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
connection_limit:
|
connection_limit:
|
||||||
description: |
|
description: |
|
||||||
The maximum number of connections permitted for this listener. Default
|
The maximum number of connections permitted for this listener. Default
|
||||||
@ -563,6 +606,12 @@ lb-algorithm-optional:
|
|||||||
in: body
|
in: body
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
lb-network-ip:
|
||||||
|
description: |
|
||||||
|
The management IP of the amphora.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
listener:
|
listener:
|
||||||
description: |
|
description: |
|
||||||
A listener object.
|
A listener object.
|
||||||
@ -1015,6 +1064,36 @@ vip_subnet_id-optional:
|
|||||||
in: body
|
in: body
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
vrrp-id:
|
||||||
|
description: |
|
||||||
|
The vrrp group's ID for the amphora.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
vrrp-interface:
|
||||||
|
description: |
|
||||||
|
The bound interface name of the vrrp port on the amphora.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
vrrp-ip:
|
||||||
|
description: |
|
||||||
|
The address of the vrrp port on the amphora.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
vrrp-port-id:
|
||||||
|
description: |
|
||||||
|
The vrrp port's ID in the networking system.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
vrrp-priority:
|
||||||
|
description: |
|
||||||
|
The priority of the amphora in the vrrp group.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
weight:
|
weight:
|
||||||
description: |
|
description: |
|
||||||
The weight of a member determines the portion of requests or connections it
|
The weight of a member determines the portion of requests or connections it
|
||||||
|
132
api-ref/source/v2/amphora.inc
Normal file
132
api-ref/source/v2/amphora.inc
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
.. -*- rst -*-
|
||||||
|
|
||||||
|
List Amphora
|
||||||
|
============
|
||||||
|
|
||||||
|
.. rest_method:: GET /v2.0/octavia/amphorae
|
||||||
|
|
||||||
|
Lists all amphora for the project.
|
||||||
|
|
||||||
|
If you are not an administrative user, the service returns the HTTP
|
||||||
|
``Forbidden (403)`` response code.
|
||||||
|
|
||||||
|
Use the ``fields`` query parameter to control which fields are
|
||||||
|
returned in the response body. Additionally, you can filter results
|
||||||
|
by using query string parameters. For information, see :ref:`filtering`.
|
||||||
|
|
||||||
|
The list might be empty.
|
||||||
|
|
||||||
|
.. rest_status_code:: success ../http-status.yaml
|
||||||
|
|
||||||
|
- 200
|
||||||
|
|
||||||
|
.. rest_status_code:: error ../http-status.yaml
|
||||||
|
|
||||||
|
- 400
|
||||||
|
- 401
|
||||||
|
- 403
|
||||||
|
- 500
|
||||||
|
|
||||||
|
Request
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. rest_parameters:: ../parameters.yaml
|
||||||
|
|
||||||
|
- fields: fields
|
||||||
|
|
||||||
|
Curl Example
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. literalinclude:: examples/amphora-list-curl
|
||||||
|
:language: bash
|
||||||
|
|
||||||
|
Response Parameters
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. rest_parameters:: ../parameters.yaml
|
||||||
|
|
||||||
|
- id: amphora-id
|
||||||
|
- loadbalancer_id: loadbalancer-id
|
||||||
|
- compute_id: compute-id
|
||||||
|
- lb_network_ip: lb-network-ip
|
||||||
|
- vrrp_ip: vrrp-ip
|
||||||
|
- ha_ip: vip_address
|
||||||
|
- vrrp_port_id: vrrp-port-id
|
||||||
|
- ha_port_id: vip_port_id
|
||||||
|
- cert_expiration: cert-expiration
|
||||||
|
- cert_busy: cert-busy
|
||||||
|
- role: amphora-role
|
||||||
|
- status: amphora-status
|
||||||
|
- vrrp_interface: vrrp-interface
|
||||||
|
- vrrp_id: vrrp-id
|
||||||
|
- vrrp_priority: vrrp-priority
|
||||||
|
|
||||||
|
Response Example
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. literalinclude:: examples/amphora-list-response.json
|
||||||
|
:language: javascript
|
||||||
|
|
||||||
|
Show Amphora details
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. rest_method:: GET /v2.0/octavia/amphorae/{amphora_id}
|
||||||
|
|
||||||
|
Shows the details of an amphora.
|
||||||
|
|
||||||
|
If you are not an administrative user, the service returns the HTTP
|
||||||
|
``Forbidden (403)`` response code.
|
||||||
|
|
||||||
|
This operation does not require a request body.
|
||||||
|
|
||||||
|
.. rest_status_code:: success ../http-status.yaml
|
||||||
|
|
||||||
|
- 200
|
||||||
|
|
||||||
|
.. rest_status_code:: error ../http-status.yaml
|
||||||
|
|
||||||
|
- 401
|
||||||
|
- 403
|
||||||
|
- 404
|
||||||
|
- 500
|
||||||
|
|
||||||
|
Request
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. rest_parameters:: ../parameters.yaml
|
||||||
|
|
||||||
|
- fields: fields
|
||||||
|
- amphora_id: path-amphora-id
|
||||||
|
|
||||||
|
Curl Example
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. literalinclude:: examples/amphora-show-curl
|
||||||
|
:language: bash
|
||||||
|
|
||||||
|
Response Parameters
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. rest_parameters:: ../parameters.yaml
|
||||||
|
|
||||||
|
- id: amphora-id
|
||||||
|
- loadbalancer_id: loadbalancer-id
|
||||||
|
- compute_id: compute-id
|
||||||
|
- lb_network_ip: lb-network-ip
|
||||||
|
- vrrp_ip: vrrp-ip
|
||||||
|
- ha_ip: vip_address
|
||||||
|
- vrrp_port_id: vrrp-port-id
|
||||||
|
- ha_port_id: vip_port_id
|
||||||
|
- cert_expiration: cert-expiration
|
||||||
|
- cert_busy: cert-busy
|
||||||
|
- role: amphora-role
|
||||||
|
- status: amphora-status
|
||||||
|
- vrrp_interface: vrrp-interface
|
||||||
|
- vrrp_id: vrrp-id
|
||||||
|
- vrrp_priority: vrrp-priority
|
||||||
|
|
||||||
|
Response Example
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. literalinclude:: examples/amphora-show-response.json
|
||||||
|
:language: javascript
|
1
api-ref/source/v2/examples/amphora-list-curl
Normal file
1
api-ref/source/v2/examples/amphora-list-curl
Normal file
@ -0,0 +1 @@
|
|||||||
|
curl -X GET -H "X-Auth-Token: <token>" http://198.51.100.10:9876/v2.0/octavia/amphorae?loadbalancer_id=09eedfc6-2c55-41a8-a75c-2cd4e95212ca
|
38
api-ref/source/v2/examples/amphora-list-response.json
Normal file
38
api-ref/source/v2/examples/amphora-list-response.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"amphorae": [
|
||||||
|
{
|
||||||
|
"id": "6bd55cd3-802e-447e-a518-1e74e23bb106",
|
||||||
|
"load_balancer_id": "09eedfc6-2c55-41a8-a75c-2cd4e95212ca",
|
||||||
|
"compute_id": "f0f79f90-733d-417a-8d70-cc6be62cd54d",
|
||||||
|
"lb_network_ip": "192.168.1.2",
|
||||||
|
"vrrp_ip": "192.168.1.5",
|
||||||
|
"ha_ip": "192.168.1.10",
|
||||||
|
"vrrp_port_id": "ab2a8add-76a9-44bb-89f8-88430193cc83",
|
||||||
|
"ha_port_id": "19561fd3-5da5-46cc-bdd3-99bbdf7246e6",
|
||||||
|
"cert_expiration": "2019-09-19 00:34:51",
|
||||||
|
"cert_busy": 0,
|
||||||
|
"role": "MASTER",
|
||||||
|
"status": "ALLOCATED",
|
||||||
|
"vrrp_interface": "eth1",
|
||||||
|
"vrrp_id": 1,
|
||||||
|
"vrrp_priority": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "89c186a3-cb16-497b-b099-c4bd40316642",
|
||||||
|
"load_balancer_id": "09eedfc6-2c55-41a8-a75c-2cd4e95212ca",
|
||||||
|
"compute_id": "24b1cb54-122d-4960-9035-083642f5c2bb",
|
||||||
|
"lb_network_ip": "192.168.1.3",
|
||||||
|
"vrrp_ip": "192.168.1.6",
|
||||||
|
"ha_ip": "192.168.1.10",
|
||||||
|
"vrrp_port_id": "cae421f6-dcf0-4866-9438-d0c682645799",
|
||||||
|
"ha_port_id": "19561fd3-5da5-46cc-bdd3-99bbdf7246e6",
|
||||||
|
"cert_expiration": "2019-09-19 00:34:51",
|
||||||
|
"cert_busy": 0,
|
||||||
|
"role": "BACKUP",
|
||||||
|
"status": "ALLOCATED",
|
||||||
|
"vrrp_interface": "eth1",
|
||||||
|
"vrrp_id": 1,
|
||||||
|
"vrrp_priority": 200
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1
api-ref/source/v2/examples/amphora-show-curl
Normal file
1
api-ref/source/v2/examples/amphora-show-curl
Normal file
@ -0,0 +1 @@
|
|||||||
|
curl -X GET -H "X-Auth-Token: <token>" http://198.51.100.10:9876/v2.0/octavia/amphorae/6bd55cd3-802e-447e-a518-1e74e23bb106
|
19
api-ref/source/v2/examples/amphora-show-response.json
Normal file
19
api-ref/source/v2/examples/amphora-show-response.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"amphora": {
|
||||||
|
"id": "6bd55cd3-802e-447e-a518-1e74e23bb106",
|
||||||
|
"load_balancer_id": "09eedfc6-2c55-41a8-a75c-2cd4e95212ca",
|
||||||
|
"compute_id": "f0f79f90-733d-417a-8d70-cc6be62cd54d",
|
||||||
|
"lb_network_ip": "192.168.1.2",
|
||||||
|
"vrrp_ip": "192.168.1.5",
|
||||||
|
"ha_ip": "192.168.1.10",
|
||||||
|
"vrrp_port_id": "ab2a8add-76a9-44bb-89f8-88430193cc83",
|
||||||
|
"ha_port_id": "19561fd3-5da5-46cc-bdd3-99bbdf7246e6",
|
||||||
|
"cert_expiration": "2019-09-19 00:34:51",
|
||||||
|
"cert_busy": 0,
|
||||||
|
"role": "MASTER",
|
||||||
|
"status": "ALLOCATED",
|
||||||
|
"vrrp_interface": "eth1",
|
||||||
|
"vrrp_id": 1,
|
||||||
|
"vrrp_priority": 100
|
||||||
|
}
|
||||||
|
}
|
@ -50,3 +50,8 @@ L7 Rules
|
|||||||
Quotas
|
Quotas
|
||||||
------
|
------
|
||||||
.. include:: quota.inc
|
.. include:: quota.inc
|
||||||
|
|
||||||
|
--------
|
||||||
|
Amphorae
|
||||||
|
--------
|
||||||
|
.. include:: amphora.inc
|
||||||
|
@ -205,6 +205,9 @@ class PaginationHelper(object):
|
|||||||
if k in filter_attrs}
|
if k in filter_attrs}
|
||||||
|
|
||||||
query = model.apply_filter(query, model, self.filters)
|
query = model.apply_filter(query, model, self.filters)
|
||||||
|
if model.__name__ == "Amphora" and 'project_id' in self.params:
|
||||||
|
query = query.filter(model.load_balancer.has(
|
||||||
|
project_id=self.params['project_id']))
|
||||||
|
|
||||||
# Add sorting
|
# Add sorting
|
||||||
if CONF.api_settings.allow_sorting:
|
if CONF.api_settings.allow_sorting:
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
from wsme import types as wtypes
|
from wsme import types as wtypes
|
||||||
from wsmeext import pecan as wsme_pecan
|
from wsmeext import pecan as wsme_pecan
|
||||||
|
|
||||||
|
from octavia.api.v2.controllers import amphora
|
||||||
from octavia.api.v2.controllers import base
|
from octavia.api.v2.controllers import base
|
||||||
from octavia.api.v2.controllers import health_monitor
|
from octavia.api.v2.controllers import health_monitor
|
||||||
from octavia.api.v2.controllers import l7policy
|
from octavia.api.v2.controllers import l7policy
|
||||||
@ -46,9 +47,22 @@ class BaseV2Controller(base.BaseController):
|
|||||||
return "v2.0"
|
return "v2.0"
|
||||||
|
|
||||||
|
|
||||||
|
class OctaviaV2Controller(base.BaseController):
|
||||||
|
amphorae = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(OctaviaV2Controller, self).__init__()
|
||||||
|
self.amphorae = amphora.AmphoraController()
|
||||||
|
|
||||||
|
@wsme_pecan.wsexpose(wtypes.text)
|
||||||
|
def get(self):
|
||||||
|
return "v2.0"
|
||||||
|
|
||||||
|
|
||||||
class V2Controller(BaseV2Controller):
|
class V2Controller(BaseV2Controller):
|
||||||
lbaas = None
|
lbaas = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(V2Controller, self).__init__()
|
super(V2Controller, self).__init__()
|
||||||
self.lbaas = BaseV2Controller()
|
self.lbaas = BaseV2Controller()
|
||||||
|
self.octavia = OctaviaV2Controller()
|
||||||
|
80
octavia/api/v2/controllers/amphora.py
Normal file
80
octavia/api/v2/controllers/amphora.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# Copyright 2014 Rackspace
|
||||||
|
# Copyright 2016 Blue Box, an IBM Company
|
||||||
|
#
|
||||||
|
# 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 logging
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
import pecan
|
||||||
|
from wsme import types as wtypes
|
||||||
|
from wsmeext import pecan as wsme_pecan
|
||||||
|
|
||||||
|
from octavia.api.v2.controllers import base
|
||||||
|
from octavia.api.v2.types import amphora as amp_types
|
||||||
|
from octavia.common import constants
|
||||||
|
from octavia.common import data_models
|
||||||
|
from octavia.common import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AmphoraController(base.BaseController):
|
||||||
|
RBAC_TYPE = constants.RBAC_AMPHORA
|
||||||
|
|
||||||
|
def _get_db_amp(self, session, amp_id):
|
||||||
|
"""Gets the current amphora object from the database."""
|
||||||
|
db_amp = self.repositories.amphora.get(
|
||||||
|
session, id=amp_id)
|
||||||
|
if not db_amp:
|
||||||
|
LOG.info("Amphora %s was not found", amp_id)
|
||||||
|
raise exceptions.NotFound(
|
||||||
|
resource=data_models.Amphora._name(),
|
||||||
|
id=amp_id)
|
||||||
|
return db_amp
|
||||||
|
|
||||||
|
@wsme_pecan.wsexpose(amp_types.AmphoraRootResponse, wtypes.text,
|
||||||
|
wtypes.text)
|
||||||
|
def get_one(self, id):
|
||||||
|
"""Gets a single amphora's details."""
|
||||||
|
context = pecan.request.context.get('octavia_context')
|
||||||
|
db_amp = self._get_db_amp(context.session, id)
|
||||||
|
|
||||||
|
self._auth_validate_action(context, db_amp.load_balancer.project_id,
|
||||||
|
constants.RBAC_GET_ONE)
|
||||||
|
|
||||||
|
result = self._convert_db_to_type(
|
||||||
|
db_amp, amp_types.AmphoraResponse)
|
||||||
|
return amp_types.AmphoraRootResponse(amphora=result)
|
||||||
|
|
||||||
|
@wsme_pecan.wsexpose(amp_types.AmphoraeRootResponse, wtypes.text,
|
||||||
|
[wtypes.text], ignore_extra_args=True)
|
||||||
|
def get_all(self, fields=None):
|
||||||
|
"""Gets all health monitors."""
|
||||||
|
pcontext = pecan.request.context
|
||||||
|
context = pcontext.get('octavia_context')
|
||||||
|
|
||||||
|
self._auth_validate_action(context, context.project_id,
|
||||||
|
constants.RBAC_GET_ALL)
|
||||||
|
|
||||||
|
db_amp, links = self.repositories.amphora.get_all(
|
||||||
|
context.session, show_deleted=False,
|
||||||
|
pagination_helper=pcontext.get(constants.PAGINATION_HELPER))
|
||||||
|
result = self._convert_db_to_type(
|
||||||
|
db_amp, [amp_types.AmphoraResponse])
|
||||||
|
if fields is not None:
|
||||||
|
result = self._filter_fields(result, fields)
|
||||||
|
return amp_types.AmphoraeRootResponse(
|
||||||
|
amphorae=result, amphorae_links=links)
|
57
octavia/api/v2/types/amphora.py
Normal file
57
octavia/api/v2/types/amphora.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Copyright 2016 Rackspace
|
||||||
|
#
|
||||||
|
# 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 wsme import types as wtypes
|
||||||
|
|
||||||
|
from octavia.api.common import types
|
||||||
|
|
||||||
|
|
||||||
|
class BaseAmphoraType(types.BaseType):
|
||||||
|
_type_to_model_map = {'loadbalancer_id': 'load_balancer_id'}
|
||||||
|
_child_map = {}
|
||||||
|
|
||||||
|
|
||||||
|
class AmphoraResponse(BaseAmphoraType):
|
||||||
|
"""Defines which attributes are to be shown on any response."""
|
||||||
|
id = wtypes.wsattr(wtypes.UuidType())
|
||||||
|
loadbalancer_id = wtypes.wsattr(wtypes.UuidType())
|
||||||
|
compute_id = wtypes.wsattr(wtypes.UuidType())
|
||||||
|
lb_network_ip = wtypes.wsattr(types.IPAddressType())
|
||||||
|
vrrp_ip = wtypes.wsattr(types.IPAddressType())
|
||||||
|
ha_ip = wtypes.wsattr(types.IPAddressType())
|
||||||
|
vrrp_port_id = wtypes.wsattr(wtypes.UuidType())
|
||||||
|
ha_port_id = wtypes.wsattr(wtypes.UuidType())
|
||||||
|
cert_expiration = wtypes.wsattr(wtypes.datetime.datetime)
|
||||||
|
cert_busy = wtypes.wsattr(bool)
|
||||||
|
role = wtypes.wsattr(wtypes.StringType())
|
||||||
|
status = wtypes.wsattr(wtypes.StringType())
|
||||||
|
vrrp_interface = wtypes.wsattr(wtypes.StringType())
|
||||||
|
vrrp_id = wtypes.wsattr(wtypes.IntegerType())
|
||||||
|
vrrp_priority = wtypes.wsattr(wtypes.IntegerType())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_data_model(cls, data_model, children=False):
|
||||||
|
amphorae = super(AmphoraResponse, cls).from_data_model(
|
||||||
|
data_model, children=children)
|
||||||
|
|
||||||
|
return amphorae
|
||||||
|
|
||||||
|
|
||||||
|
class AmphoraRootResponse(types.BaseType):
|
||||||
|
amphora = wtypes.wsattr(AmphoraResponse)
|
||||||
|
|
||||||
|
|
||||||
|
class AmphoraeRootResponse(types.BaseType):
|
||||||
|
amphorae = wtypes.wsattr([AmphoraResponse])
|
||||||
|
amphorae_links = wtypes.wsattr([types.PageType])
|
@ -98,7 +98,7 @@ SUPPORTED_PROVISIONING_STATUSES = (ACTIVE, AMPHORA_ALLOCATED,
|
|||||||
MUTABLE_STATUSES = (ACTIVE,)
|
MUTABLE_STATUSES = (ACTIVE,)
|
||||||
DELETABLE_STATUSES = (ACTIVE, ERROR)
|
DELETABLE_STATUSES = (ACTIVE, ERROR)
|
||||||
|
|
||||||
SUPPORTED_AMPHORA_STATUSES = (AMPHORA_ALLOCATED, AMPHORA_BOOTING,
|
SUPPORTED_AMPHORA_STATUSES = (AMPHORA_ALLOCATED, AMPHORA_BOOTING, ERROR,
|
||||||
AMPHORA_READY, DELETED, PENDING_DELETE)
|
AMPHORA_READY, DELETED, PENDING_DELETE)
|
||||||
|
|
||||||
ONLINE = 'ONLINE'
|
ONLINE = 'ONLINE'
|
||||||
@ -450,6 +450,7 @@ RBAC_HEALTHMONITOR = '{}:healthmonitor:'.format(LOADBALANCER_API)
|
|||||||
RBAC_L7POLICY = '{}:l7policy:'.format(LOADBALANCER_API)
|
RBAC_L7POLICY = '{}:l7policy:'.format(LOADBALANCER_API)
|
||||||
RBAC_L7RULE = '{}:l7rule:'.format(LOADBALANCER_API)
|
RBAC_L7RULE = '{}:l7rule:'.format(LOADBALANCER_API)
|
||||||
RBAC_QUOTA = '{}:quota:'.format(LOADBALANCER_API)
|
RBAC_QUOTA = '{}:quota:'.format(LOADBALANCER_API)
|
||||||
|
RBAC_AMPHORA = '{}:amphora:'.format(LOADBALANCER_API)
|
||||||
RBAC_POST = 'post'
|
RBAC_POST = 'post'
|
||||||
RBAC_PUT = 'put'
|
RBAC_PUT = 'put'
|
||||||
RBAC_PUT_FAILOVER = 'put_failover'
|
RBAC_PUT_FAILOVER = 'put_failover'
|
||||||
|
@ -103,7 +103,8 @@ class Policy(oslo_policy.Enforcer):
|
|||||||
credentials = context.to_policy_values()
|
credentials = context.to_policy_values()
|
||||||
# Inject is_admin into the credentials to allow override via
|
# Inject is_admin into the credentials to allow override via
|
||||||
# config auth_strategy = constants.NOAUTH
|
# config auth_strategy = constants.NOAUTH
|
||||||
credentials['is_admin'] = context.is_admin
|
credentials['is_admin'] = (
|
||||||
|
credentials.get('is_admin') or context.is_admin)
|
||||||
|
|
||||||
if not exc:
|
if not exc:
|
||||||
exc = exceptions.PolicyForbidden
|
exc = exceptions.PolicyForbidden
|
||||||
|
@ -20,6 +20,7 @@ from sqlalchemy import orm
|
|||||||
from sqlalchemy.orm import validates
|
from sqlalchemy.orm import validates
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
|
|
||||||
|
from octavia.api.v2.types import amphora
|
||||||
from octavia.api.v2.types import health_monitor
|
from octavia.api.v2.types import health_monitor
|
||||||
from octavia.api.v2.types import l7policy
|
from octavia.api.v2.types import l7policy
|
||||||
from octavia.api.v2.types import l7rule
|
from octavia.api.v2.types import l7rule
|
||||||
@ -482,6 +483,8 @@ class Amphora(base_models.BASE, base_models.IdMixin):
|
|||||||
|
|
||||||
__tablename__ = "amphora"
|
__tablename__ = "amphora"
|
||||||
|
|
||||||
|
__v2_wsme__ = amphora.AmphoraResponse
|
||||||
|
|
||||||
load_balancer_id = sa.Column(
|
load_balancer_id = sa.Column(
|
||||||
sa.String(36), sa.ForeignKey("load_balancer.id",
|
sa.String(36), sa.ForeignKey("load_balancer.id",
|
||||||
name="fk_amphora_load_balancer_id"),
|
name="fk_amphora_load_balancer_id"),
|
||||||
|
@ -118,6 +118,10 @@ class BaseRepository(object):
|
|||||||
query = query.options(joinedload('*'))
|
query = query.options(joinedload('*'))
|
||||||
|
|
||||||
if not deleted:
|
if not deleted:
|
||||||
|
if hasattr(self.model_class, 'status'):
|
||||||
|
query = query.filter(
|
||||||
|
self.model_class.status != consts.DELETED)
|
||||||
|
else:
|
||||||
query = query.filter(
|
query = query.filter(
|
||||||
self.model_class.provisioning_status != consts.DELETED)
|
self.model_class.provisioning_status != consts.DELETED)
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
|
from octavia.policies import amphora
|
||||||
from octavia.policies import base
|
from octavia.policies import base
|
||||||
from octavia.policies import healthmonitor
|
from octavia.policies import healthmonitor
|
||||||
from octavia.policies import l7policy
|
from octavia.policies import l7policy
|
||||||
@ -35,4 +36,5 @@ def list_rules():
|
|||||||
member.list_rules(),
|
member.list_rules(),
|
||||||
pool.list_rules(),
|
pool.list_rules(),
|
||||||
quota.list_rules(),
|
quota.list_rules(),
|
||||||
|
amphora.list_rules(),
|
||||||
)
|
)
|
||||||
|
36
octavia/policies/amphora.py
Normal file
36
octavia/policies/amphora.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Copyright 2017 Rackspace, US 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 octavia.common import constants
|
||||||
|
from oslo_policy import policy
|
||||||
|
|
||||||
|
rules = [
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
'{rbac_obj}{action}'.format(rbac_obj=constants.RBAC_AMPHORA,
|
||||||
|
action=constants.RBAC_GET_ALL),
|
||||||
|
constants.RULE_API_ADMIN,
|
||||||
|
"List Amphorae",
|
||||||
|
[{'method': 'GET', 'path': '/v2.0/octavia/amphorae'}]
|
||||||
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
'{rbac_obj}{action}'.format(rbac_obj=constants.RBAC_AMPHORA,
|
||||||
|
action=constants.RBAC_GET_ONE),
|
||||||
|
constants.RULE_API_ADMIN,
|
||||||
|
"Show Amphora details",
|
||||||
|
[{'method': 'GET', 'path': '/v2.0/octavia/amphorae/{amphora_id}'}]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return rules
|
@ -28,21 +28,21 @@ from octavia.tests.functional.db import base as base_db_test
|
|||||||
|
|
||||||
class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
||||||
|
|
||||||
BASE_PATH = '/v2.0/lbaas'
|
BASE_PATH = '/v2.0'
|
||||||
|
|
||||||
# /lbaas/loadbalancers
|
# /lbaas/loadbalancers
|
||||||
LBS_PATH = '/loadbalancers'
|
LBS_PATH = '/lbaas/loadbalancers'
|
||||||
LB_PATH = LBS_PATH + '/{lb_id}'
|
LB_PATH = LBS_PATH + '/{lb_id}'
|
||||||
LB_STATUS_PATH = LB_PATH + '/statuses'
|
LB_STATUS_PATH = LB_PATH + '/statuses'
|
||||||
LB_STATS_PATH = LB_PATH + '/stats'
|
LB_STATS_PATH = LB_PATH + '/stats'
|
||||||
|
|
||||||
# /lbaas/listeners/
|
# /lbaas/listeners/
|
||||||
LISTENERS_PATH = '/listeners'
|
LISTENERS_PATH = '/lbaas/listeners'
|
||||||
LISTENER_PATH = LISTENERS_PATH + '/{listener_id}'
|
LISTENER_PATH = LISTENERS_PATH + '/{listener_id}'
|
||||||
LISTENER_STATS_PATH = LISTENER_PATH + '/stats'
|
LISTENER_STATS_PATH = LISTENER_PATH + '/stats'
|
||||||
|
|
||||||
# /lbaas/pools
|
# /lbaas/pools
|
||||||
POOLS_PATH = '/pools'
|
POOLS_PATH = '/lbaas/pools'
|
||||||
POOL_PATH = POOLS_PATH + '/{pool_id}'
|
POOL_PATH = POOLS_PATH + '/{pool_id}'
|
||||||
|
|
||||||
# /lbaas/pools/{pool_id}/members
|
# /lbaas/pools/{pool_id}/members
|
||||||
@ -50,19 +50,22 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
|||||||
MEMBER_PATH = MEMBERS_PATH + '/{member_id}'
|
MEMBER_PATH = MEMBERS_PATH + '/{member_id}'
|
||||||
|
|
||||||
# /lbaas/healthmonitors
|
# /lbaas/healthmonitors
|
||||||
HMS_PATH = '/healthmonitors'
|
HMS_PATH = '/lbaas/healthmonitors'
|
||||||
HM_PATH = HMS_PATH + '/{healthmonitor_id}'
|
HM_PATH = HMS_PATH + '/{healthmonitor_id}'
|
||||||
|
|
||||||
# /lbaas/l7policies
|
# /lbaas/l7policies
|
||||||
L7POLICIES_PATH = '/l7policies'
|
L7POLICIES_PATH = '/lbaas/l7policies'
|
||||||
L7POLICY_PATH = L7POLICIES_PATH + '/{l7policy_id}'
|
L7POLICY_PATH = L7POLICIES_PATH + '/{l7policy_id}'
|
||||||
L7RULES_PATH = L7POLICY_PATH + '/rules'
|
L7RULES_PATH = L7POLICY_PATH + '/rules'
|
||||||
L7RULE_PATH = L7RULES_PATH + '/{l7rule_id}'
|
L7RULE_PATH = L7RULES_PATH + '/{l7rule_id}'
|
||||||
|
|
||||||
QUOTAS_PATH = '/quotas'
|
QUOTAS_PATH = '/lbaas/quotas'
|
||||||
QUOTA_PATH = QUOTAS_PATH + '/{project_id}'
|
QUOTA_PATH = QUOTAS_PATH + '/{project_id}'
|
||||||
QUOTA_DEFAULT_PATH = QUOTAS_PATH + '/{project_id}/default'
|
QUOTA_DEFAULT_PATH = QUOTAS_PATH + '/{project_id}/default'
|
||||||
|
|
||||||
|
AMPHORAE_PATH = '/octavia/amphorae'
|
||||||
|
AMPHORA_PATH = AMPHORAE_PATH + '/{amphora_id}'
|
||||||
|
|
||||||
NOT_AUTHORIZED_BODY = {
|
NOT_AUTHORIZED_BODY = {
|
||||||
'debuginfo': None, 'faultcode': 'Client',
|
'debuginfo': None, 'faultcode': 'Client',
|
||||||
'faultstring': 'Policy does not allow this request to be performed.'}
|
'faultstring': 'Policy does not allow this request to be performed.'}
|
||||||
|
289
octavia/tests/functional/api/v2/test_amphora.py
Normal file
289
octavia/tests/functional/api/v2/test_amphora.py
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
# Copyright 2014 Rackspace
|
||||||
|
#
|
||||||
|
# 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 mock
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_config import fixture as oslo_fixture
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
from octavia.common import constants
|
||||||
|
import octavia.common.context
|
||||||
|
from octavia.tests.functional.api.v2 import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestAmphora(base.BaseAPITest):
|
||||||
|
|
||||||
|
root_tag = 'amphora'
|
||||||
|
root_tag_list = 'amphorae'
|
||||||
|
root_tag_links = 'amphorae_links'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestAmphora, self).setUp()
|
||||||
|
self.lb = self.create_load_balancer(
|
||||||
|
uuidutils.generate_uuid()).get('loadbalancer')
|
||||||
|
self.lb_id = self.lb.get('id')
|
||||||
|
self.project_id = self.lb.get('project_id')
|
||||||
|
self.set_lb_status(self.lb_id)
|
||||||
|
self.amp_args = {
|
||||||
|
'load_balancer_id': self.lb_id,
|
||||||
|
'compute_id': uuidutils.generate_uuid(),
|
||||||
|
'lb_network_ip': '192.168.1.2',
|
||||||
|
'vrrp_ip': '192.168.1.5',
|
||||||
|
'ha_ip': '192.168.1.10',
|
||||||
|
'vrrp_port_id': uuidutils.generate_uuid(),
|
||||||
|
'ha_port_id': uuidutils.generate_uuid(),
|
||||||
|
'cert_expiration': datetime.datetime.now(),
|
||||||
|
'cert_busy': False,
|
||||||
|
'role': constants.ROLE_STANDALONE,
|
||||||
|
'status': constants.AMPHORA_ALLOCATED,
|
||||||
|
'vrrp_interface': 'eth1',
|
||||||
|
'vrrp_id': 1,
|
||||||
|
'vrrp_priority': 100,
|
||||||
|
}
|
||||||
|
self.amp = self.amphora_repo.create(self.session, **self.amp_args)
|
||||||
|
self.amp_id = self.amp.id
|
||||||
|
self.amp_args['id'] = self.amp_id
|
||||||
|
|
||||||
|
def _create_additional_amp(self):
|
||||||
|
amp_args = {
|
||||||
|
'load_balancer_id': None,
|
||||||
|
'compute_id': uuidutils.generate_uuid(),
|
||||||
|
'lb_network_ip': '192.168.1.2',
|
||||||
|
'vrrp_ip': '192.168.1.5',
|
||||||
|
'ha_ip': '192.168.1.10',
|
||||||
|
'vrrp_port_id': uuidutils.generate_uuid(),
|
||||||
|
'ha_port_id': uuidutils.generate_uuid(),
|
||||||
|
'cert_expiration': None,
|
||||||
|
'cert_busy': False,
|
||||||
|
'role': constants.ROLE_MASTER,
|
||||||
|
'status': constants.AMPHORA_READY,
|
||||||
|
'vrrp_interface': 'eth1',
|
||||||
|
'vrrp_id': 1,
|
||||||
|
'vrrp_priority': 100,
|
||||||
|
}
|
||||||
|
return self.amphora_repo.create(self.session, **amp_args)
|
||||||
|
|
||||||
|
def _assert_amp_equal(self, source, response):
|
||||||
|
self.assertEqual(source.pop('load_balancer_id'),
|
||||||
|
response.pop('loadbalancer_id'))
|
||||||
|
self.assertEqual(source.pop('cert_expiration').isoformat(),
|
||||||
|
response.pop('cert_expiration'))
|
||||||
|
self.assertEqual(source, response)
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
response = self.get(self.AMPHORA_PATH.format(
|
||||||
|
amphora_id=self.amp_id)).json.get(self.root_tag)
|
||||||
|
self._assert_amp_equal(self.amp_args, response)
|
||||||
|
|
||||||
|
def test_get_authorized(self):
|
||||||
|
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
|
||||||
|
auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
|
||||||
|
self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
|
||||||
|
with mock.patch.object(octavia.common.context.Context, 'project_id',
|
||||||
|
self.project_id):
|
||||||
|
override_credentials = {
|
||||||
|
'service_user_id': None,
|
||||||
|
'user_domain_id': None,
|
||||||
|
'is_admin_project': True,
|
||||||
|
'service_project_domain_id': None,
|
||||||
|
'service_project_id': None,
|
||||||
|
'roles': ['load-balancer_member'],
|
||||||
|
'user_id': None,
|
||||||
|
'is_admin': True,
|
||||||
|
'service_user_domain_id': None,
|
||||||
|
'project_domain_id': None,
|
||||||
|
'service_roles': [],
|
||||||
|
'project_id': self.project_id}
|
||||||
|
with mock.patch(
|
||||||
|
"oslo_context.context.RequestContext.to_policy_values",
|
||||||
|
return_value=override_credentials):
|
||||||
|
response = self.get(self.AMPHORA_PATH.format(
|
||||||
|
amphora_id=self.amp_id)).json.get(self.root_tag)
|
||||||
|
# Reset api auth setting
|
||||||
|
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
|
||||||
|
|
||||||
|
self._assert_amp_equal(self.amp_args, response)
|
||||||
|
|
||||||
|
def test_get_not_authorized(self):
|
||||||
|
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
|
||||||
|
auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
|
||||||
|
self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
|
||||||
|
with mock.patch.object(octavia.common.context.Context, 'project_id',
|
||||||
|
uuidutils.generate_uuid()):
|
||||||
|
response = self.get(self.AMPHORA_PATH.format(
|
||||||
|
amphora_id=self.amp_id), status=403)
|
||||||
|
# Reset api auth setting
|
||||||
|
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
|
||||||
|
self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json)
|
||||||
|
|
||||||
|
def test_get_hides_deleted(self):
|
||||||
|
new_amp = self._create_additional_amp()
|
||||||
|
|
||||||
|
response = self.get(self.AMPHORAE_PATH)
|
||||||
|
objects = response.json.get(self.root_tag_list)
|
||||||
|
self.assertEqual(len(objects), 2)
|
||||||
|
self.amphora_repo.update(self.session, new_amp.id,
|
||||||
|
status=constants.DELETED)
|
||||||
|
response = self.get(self.AMPHORAE_PATH)
|
||||||
|
objects = response.json.get(self.root_tag_list)
|
||||||
|
self.assertEqual(len(objects), 1)
|
||||||
|
|
||||||
|
def test_bad_get(self):
|
||||||
|
self.get(self.AMPHORA_PATH.format(
|
||||||
|
amphora_id=uuidutils.generate_uuid()), status=404)
|
||||||
|
|
||||||
|
def test_get_all(self):
|
||||||
|
amps = self.get(self.AMPHORAE_PATH).json.get(self.root_tag_list)
|
||||||
|
self.assertIsInstance(amps, list)
|
||||||
|
self.assertEqual(1, len(amps))
|
||||||
|
self.assertEqual(self.amp_id, amps[0].get('id'))
|
||||||
|
|
||||||
|
def test_get_all_authorized(self):
|
||||||
|
auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
|
||||||
|
self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
|
||||||
|
with mock.patch.object(octavia.common.context.Context, 'project_id',
|
||||||
|
uuidutils.generate_uuid()):
|
||||||
|
override_credentials = {
|
||||||
|
'service_user_id': None,
|
||||||
|
'user_domain_id': None,
|
||||||
|
'is_admin_project': True,
|
||||||
|
'service_project_domain_id': None,
|
||||||
|
'service_project_id': None,
|
||||||
|
'roles': ['load-balancer_member'],
|
||||||
|
'user_id': None,
|
||||||
|
'is_admin': True,
|
||||||
|
'service_user_domain_id': None,
|
||||||
|
'project_domain_id': None,
|
||||||
|
'service_roles': [],
|
||||||
|
'project_id': self.project_id}
|
||||||
|
with mock.patch(
|
||||||
|
"oslo_context.context.RequestContext.to_policy_values",
|
||||||
|
return_value=override_credentials):
|
||||||
|
amps = self.get(self.AMPHORAE_PATH).json.get(
|
||||||
|
self.root_tag_list)
|
||||||
|
|
||||||
|
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
|
||||||
|
self.assertIsInstance(amps, list)
|
||||||
|
self.assertEqual(1, len(amps))
|
||||||
|
self.assertEqual(self.amp_id, amps[0].get('id'))
|
||||||
|
|
||||||
|
def test_get_all_not_authorized(self):
|
||||||
|
auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
|
||||||
|
self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
|
||||||
|
with mock.patch.object(octavia.common.context.Context, 'project_id',
|
||||||
|
uuidutils.generate_uuid()):
|
||||||
|
amps = self.get(self.AMPHORAE_PATH, status=403).json
|
||||||
|
|
||||||
|
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
|
||||||
|
self.assertEqual(self.NOT_AUTHORIZED_BODY, amps)
|
||||||
|
|
||||||
|
def test_get_by_loadbalancer_id(self):
|
||||||
|
amps = self.get(
|
||||||
|
self.AMPHORAE_PATH,
|
||||||
|
params={'loadbalancer_id': self.lb_id}
|
||||||
|
).json.get(self.root_tag_list)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(amps))
|
||||||
|
amps = self.get(
|
||||||
|
self.AMPHORAE_PATH,
|
||||||
|
params={'loadbalancer_id': uuidutils.generate_uuid()}
|
||||||
|
).json.get(self.root_tag_list)
|
||||||
|
self.assertEqual(0, len(amps))
|
||||||
|
|
||||||
|
def test_get_by_project_id(self):
|
||||||
|
amps = self.get(
|
||||||
|
self.AMPHORAE_PATH,
|
||||||
|
params={'project_id': self.project_id}
|
||||||
|
).json.get(self.root_tag_list)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(amps))
|
||||||
|
amps = self.get(
|
||||||
|
self.AMPHORAE_PATH,
|
||||||
|
params={'project_id': uuidutils.generate_uuid()}
|
||||||
|
).json.get(self.root_tag_list)
|
||||||
|
self.assertEqual(0, len(amps))
|
||||||
|
|
||||||
|
def test_get_all_sorted(self):
|
||||||
|
self._create_additional_amp()
|
||||||
|
|
||||||
|
response = self.get(self.AMPHORAE_PATH, params={'sort': 'role:desc'})
|
||||||
|
amps_desc = response.json.get(self.root_tag_list)
|
||||||
|
response = self.get(self.AMPHORAE_PATH, params={'sort': 'role:asc'})
|
||||||
|
amps_asc = response.json.get(self.root_tag_list)
|
||||||
|
|
||||||
|
self.assertEqual(2, len(amps_desc))
|
||||||
|
self.assertEqual(2, len(amps_asc))
|
||||||
|
|
||||||
|
amp_id_roles_desc = [(amp.get('id'), amp.get('role'))
|
||||||
|
for amp in amps_desc]
|
||||||
|
amp_id_roles_asc = [(amp.get('id'), amp.get('role'))
|
||||||
|
for amp in amps_asc]
|
||||||
|
self.assertEqual(amp_id_roles_asc, list(reversed(amp_id_roles_desc)))
|
||||||
|
|
||||||
|
def test_get_all_limited(self):
|
||||||
|
self._create_additional_amp()
|
||||||
|
self._create_additional_amp()
|
||||||
|
|
||||||
|
# First two -- should have 'next' link
|
||||||
|
first_two = self.get(self.AMPHORAE_PATH, params={'limit': 2}).json
|
||||||
|
objs = first_two[self.root_tag_list]
|
||||||
|
links = first_two[self.root_tag_links]
|
||||||
|
self.assertEqual(2, len(objs))
|
||||||
|
self.assertEqual(1, len(links))
|
||||||
|
self.assertEqual('next', links[0]['rel'])
|
||||||
|
|
||||||
|
# Third + off the end -- should have previous link
|
||||||
|
third = self.get(self.AMPHORAE_PATH, params={
|
||||||
|
'limit': 2,
|
||||||
|
'marker': first_two[self.root_tag_list][1]['id']}).json
|
||||||
|
objs = third[self.root_tag_list]
|
||||||
|
links = third[self.root_tag_links]
|
||||||
|
self.assertEqual(1, len(objs))
|
||||||
|
self.assertEqual(1, len(links))
|
||||||
|
self.assertEqual('previous', links[0]['rel'])
|
||||||
|
|
||||||
|
# Middle -- should have both links
|
||||||
|
middle = self.get(self.AMPHORAE_PATH, params={
|
||||||
|
'limit': 1,
|
||||||
|
'marker': first_two[self.root_tag_list][0]['id']}).json
|
||||||
|
objs = middle[self.root_tag_list]
|
||||||
|
links = middle[self.root_tag_links]
|
||||||
|
self.assertEqual(1, len(objs))
|
||||||
|
self.assertEqual(2, len(links))
|
||||||
|
self.assertItemsEqual(['previous', 'next'], [l['rel'] for l in links])
|
||||||
|
|
||||||
|
def test_get_all_fields_filter(self):
|
||||||
|
amps = self.get(self.AMPHORAE_PATH, params={
|
||||||
|
'fields': ['id', 'compute_id']}).json
|
||||||
|
for amp in amps['amphorae']:
|
||||||
|
self.assertIn(u'id', amp.keys())
|
||||||
|
self.assertIn(u'compute_id', amp.keys())
|
||||||
|
self.assertNotIn(u'vrrp_ip', amp.keys())
|
||||||
|
|
||||||
|
def test_get_all_filter(self):
|
||||||
|
self._create_additional_amp()
|
||||||
|
|
||||||
|
amps = self.get(self.AMPHORAE_PATH, params={
|
||||||
|
'id': self.amp_id}).json.get(self.root_tag_list)
|
||||||
|
self.assertEqual(1, len(amps))
|
||||||
|
self.assertEqual(self.amp_id,
|
||||||
|
amps[0]['id'])
|
||||||
|
|
||||||
|
def test_empty_get_all(self):
|
||||||
|
self.amphora_repo.delete(self.session, id=self.amp_id)
|
||||||
|
response = self.get(self.AMPHORAE_PATH).json.get(self.root_tag_list)
|
||||||
|
self.assertIsInstance(response, list)
|
||||||
|
self.assertEqual(0, len(response))
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added a new endpoint /v2.0/octavia/amphorae to expose internal details
|
||||||
|
about amphorae. This endpoint is admin only.
|
Loading…
Reference in New Issue
Block a user