From 6ee20b272ec526c41e1916d224f7ef1d2a179336 Mon Sep 17 00:00:00 2001 From: Adam Harwell Date: Tue, 27 Feb 2018 02:39:09 +0000 Subject: [PATCH] Add image_id to amphora table Story: 2001491 Task: 6215 Change-Id: I5ab6707591c856e43a0e0f49c84e1e721f01893c --- api-ref/source/parameters.yaml | 6 ++ api-ref/source/v2/amphora.inc | 2 + .../v2/examples/amphora-list-response.json | 6 +- .../v2/examples/amphora-show-response.json | 3 +- octavia/api/v2/types/amphora.py | 1 + octavia/common/data_models.py | 3 +- octavia/compute/drivers/nova_driver.py | 3 +- .../controller/worker/tasks/database_tasks.py | 3 +- .../034756a182a2_amphora_add_image_id.py | 35 +++++++++ octavia/db/models.py | 1 + .../tests/functional/api/v2/test_amphora.py | 1 + octavia/tests/unit/common/test_data_models.py | 76 +++++++++++++++++++ .../unit/compute/drivers/test_nova_driver.py | 4 +- .../worker/tasks/test_database_tasks.py | 31 ++++++++ .../new-amphora-fields-fa3ffc5801b5e551.yaml | 5 ++ 15 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 octavia/db/migration/alembic_migrations/versions/034756a182a2_amphora_add_image_id.py create mode 100644 releasenotes/notes/new-amphora-fields-fa3ffc5801b5e551.yaml diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index d62c2b1c64..0c6928287f 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -423,6 +423,12 @@ id: in: body required: true type: uuid +image-id: + description: | + The ID of the glance image used for the amphora. + in: body + required: true + type: uuid insert_headers: description: | A dictionary of optional headers to insert into the request before it is diff --git a/api-ref/source/v2/amphora.inc b/api-ref/source/v2/amphora.inc index afdf5ac825..e49435348a 100644 --- a/api-ref/source/v2/amphora.inc +++ b/api-ref/source/v2/amphora.inc @@ -69,6 +69,7 @@ Response Parameters - cached_zone: cached-zone - created_at: created_at - updated_at: updated_at + - image_id: image-id Response Example ---------------- @@ -142,6 +143,7 @@ Response Parameters - cached_zone: cached-zone - created_at: created_at - updated_at: updated_at + - image_id: image-id Response Example ---------------- diff --git a/api-ref/source/v2/examples/amphora-list-response.json b/api-ref/source/v2/examples/amphora-list-response.json index 2fa4b959bd..e607475716 100644 --- a/api-ref/source/v2/examples/amphora-list-response.json +++ b/api-ref/source/v2/examples/amphora-list-response.json @@ -18,7 +18,8 @@ "vrrp_priority": 100, "cached_zone": "zone1", "created_at": "2017-05-10T18:14:44", - "updated_at": "2017-05-10T23:08:12" + "updated_at": "2017-05-10T23:08:12", + "image_id": "c1c2ad6f-1c1e-4744-8d1a-d0ef36289e74" }, { "id": "89c186a3-cb16-497b-b099-c4bd40316642", @@ -38,7 +39,8 @@ "vrrp_priority": 200, "cached_zone": "zone2", "created_at": "2017-06-11T19:15:45", - "updated_at": "2017-06-11T24:09:13" + "updated_at": "2017-06-11T24:09:13", + "image_id": "1014292d-cbaa-4ad6-b38b-2e138389f87f" } ] } diff --git a/api-ref/source/v2/examples/amphora-show-response.json b/api-ref/source/v2/examples/amphora-show-response.json index aeb29a8d69..d5668b8cd9 100644 --- a/api-ref/source/v2/examples/amphora-show-response.json +++ b/api-ref/source/v2/examples/amphora-show-response.json @@ -17,6 +17,7 @@ "vrrp_priority": 100, "cached_zone": "zone1", "created_at": "2017-05-10T18:14:44", - "updated_at": "2017-05-10T23:08:12" + "updated_at": "2017-05-10T23:08:12", + "image_id": "c1c2ad6f-1c1e-4744-8d1a-d0ef36289e74" } } diff --git a/octavia/api/v2/types/amphora.py b/octavia/api/v2/types/amphora.py index 90715e0e6f..f47af60602 100644 --- a/octavia/api/v2/types/amphora.py +++ b/octavia/api/v2/types/amphora.py @@ -42,6 +42,7 @@ class AmphoraResponse(BaseAmphoraType): cached_zone = wtypes.wsattr(wtypes.StringType()) created_at = wtypes.wsattr(wtypes.datetime.datetime) updated_at = wtypes.wsattr(wtypes.datetime.datetime) + image_id = wtypes.wsattr(wtypes.UuidType()) @classmethod def from_data_model(cls, data_model, children=False): diff --git a/octavia/common/data_models.py b/octavia/common/data_models.py index aafc5a2382..2d78ec71f8 100644 --- a/octavia/common/data_models.py +++ b/octavia/common/data_models.py @@ -501,7 +501,7 @@ class Amphora(BaseDataModel): load_balancer=None, role=None, cert_expiration=None, cert_busy=False, vrrp_interface=None, vrrp_id=None, vrrp_priority=None, cached_zone=None, created_at=None, - updated_at=None): + updated_at=None, image_id=None): self.id = id self.load_balancer_id = load_balancer_id self.compute_id = compute_id @@ -521,6 +521,7 @@ class Amphora(BaseDataModel): self.cached_zone = cached_zone self.created_at = created_at self.updated_at = updated_at + self.image_id = image_id def delete(self): for amphora in self.load_balancer.amphorae: diff --git a/octavia/compute/drivers/nova_driver.py b/octavia/compute/drivers/nova_driver.py index 2ec6327c2e..18bb940c21 100644 --- a/octavia/compute/drivers/nova_driver.py +++ b/octavia/compute/drivers/nova_driver.py @@ -245,7 +245,8 @@ class VirtualMachineManager(compute_base.ComputeBase): compute_id=nova_response.id, status=nova_response.status, lb_network_ip=lb_network_ip, - cached_zone=availability_zone + cached_zone=availability_zone, + image_id=nova_response.image.get("id") ) return response, fault diff --git a/octavia/controller/worker/tasks/database_tasks.py b/octavia/controller/worker/tasks/database_tasks.py index b2ee0339cf..c43d5946f5 100644 --- a/octavia/controller/worker/tasks/database_tasks.py +++ b/octavia/controller/worker/tasks/database_tasks.py @@ -876,7 +876,8 @@ class UpdateAmphoraInfo(BaseDatabaseTask): self.amphora_repo.update( db_apis.get_session(), amphora_id, lb_network_ip=compute_obj.lb_network_ip, - cached_zone=compute_obj.cached_zone) + cached_zone=compute_obj.cached_zone, + image_id=compute_obj.image_id) return self.amphora_repo.get(db_apis.get_session(), id=amphora_id) diff --git a/octavia/db/migration/alembic_migrations/versions/034756a182a2_amphora_add_image_id.py b/octavia/db/migration/alembic_migrations/versions/034756a182a2_amphora_add_image_id.py new file mode 100644 index 0000000000..745132ff92 --- /dev/null +++ b/octavia/db/migration/alembic_migrations/versions/034756a182a2_amphora_add_image_id.py @@ -0,0 +1,35 @@ +# Copyright 2017 GoDaddy +# +# 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. + +"""amphora add image id + +Revision ID: 034756a182a2 +Revises: 10d38216ad34 +Create Date: 2018-02-26 17:38:37.971677 + +""" + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = '034756a182a2' +down_revision = '10d38216ad34' + + +def upgrade(): + op.add_column( + u'amphora', + sa.Column(u'image_id', sa.String(36), nullable=True) + ) diff --git a/octavia/db/models.py b/octavia/db/models.py index 1f7d105390..44402171b3 100644 --- a/octavia/db/models.py +++ b/octavia/db/models.py @@ -518,6 +518,7 @@ class Amphora(base_models.BASE, base_models.IdMixin, models.TimestampMixin): vrrp_id = sa.Column(sa.Integer(), nullable=True) vrrp_priority = sa.Column(sa.Integer(), nullable=True) cached_zone = sa.Column(sa.String(255), nullable=True) + image_id = sa.Column(sa.String(36), nullable=True) class AmphoraHealth(base_models.BASE): diff --git a/octavia/tests/functional/api/v2/test_amphora.py b/octavia/tests/functional/api/v2/test_amphora.py index 1ab9711d5a..8d97f9645d 100644 --- a/octavia/tests/functional/api/v2/test_amphora.py +++ b/octavia/tests/functional/api/v2/test_amphora.py @@ -55,6 +55,7 @@ class TestAmphora(base.BaseAPITest): 'cached_zone': None, 'created_at': datetime.datetime.now(), 'updated_at': datetime.datetime.now(), + 'image_id': uuidutils.generate_uuid(), } self.amp = self.amphora_repo.create(self.session, **self.amp_args) self.amp_id = self.amp.id diff --git a/octavia/tests/unit/common/test_data_models.py b/octavia/tests/unit/common/test_data_models.py index 9903c7b7c8..37e72fc2f6 100644 --- a/octavia/tests/unit/common/test_data_models.py +++ b/octavia/tests/unit/common/test_data_models.py @@ -17,6 +17,7 @@ import datetime from oslo_utils import uuidutils +from octavia.common import constants from octavia.common import data_models import octavia.tests.unit.base as base @@ -36,6 +37,9 @@ class TestDataModels(base.TestCase): self.VIP_PORT_ID = uuidutils.generate_uuid() self.VIP_QOS_ID = uuidutils.generate_uuid() self.POOL_ID = uuidutils.generate_uuid() + self.AMP_ID = uuidutils.generate_uuid() + self.COMPUTE_ID = uuidutils.generate_uuid() + self.IMAGE_ID = uuidutils.generate_uuid() self.LB_obj = data_models.LoadBalancer( id=self.LB_ID, @@ -89,6 +93,29 @@ class TestDataModels(base.TestCase): cookie_name='chocolate', pool=None) + self.AMP_obj = data_models.Amphora( + id=self.AMP_ID, + load_balancer_id=self.LB_ID, + compute_id=self.COMPUTE_ID, + status=constants.ACTIVE, + lb_network_ip=None, + vrrp_ip=None, + ha_ip=None, + vrrp_port_id=None, + ha_port_id=self.VIP_PORT_ID, + load_balancer=self.LB_obj, + role=constants.ROLE_MASTER, + cert_expiration=None, + cert_busy=False, + vrrp_interface=None, + vrrp_id=None, + vrrp_priority=constants.ROLE_MASTER_PRIORITY, + cached_zone=None, + created_at=self.CREATED_AT, + updated_at=self.UPDATED_AT, + image_id=self.IMAGE_ID + ) + super(TestDataModels, self).setUp() def test_LoadBalancer_update(self): @@ -336,3 +363,52 @@ class TestDataModels(base.TestCase): test_Pool_obj.update(update_dict) self.assertEqual(reference_SP_obj, test_Pool_obj.session_persistence) + + def test_Amphora_update(self): + + new_id = uuidutils.generate_uuid() + new_status = constants.ERROR + new_role = constants.ROLE_BACKUP + new_vrrp_priority = constants.ROLE_BACKUP_PRIORITY + new_created_at = self.CREATED_AT + datetime.timedelta(minutes=5) + new_updated_at = self.UPDATED_AT + datetime.timedelta(minutes=10) + new_image_id = uuidutils.generate_uuid() + + update_dict = { + 'id': new_id, + 'status': new_status, + 'role': new_role, + 'vrrp_priority': new_vrrp_priority, + 'created_at': new_created_at, + 'updated_at': new_updated_at, + 'image_id': new_image_id + } + + test_Amp_obj = copy.deepcopy(self.AMP_obj) + + reference_Amp_obj = data_models.Amphora( + id=new_id, + load_balancer_id=self.LB_ID, + compute_id=self.COMPUTE_ID, + status=new_status, + lb_network_ip=None, + vrrp_ip=None, + ha_ip=None, + vrrp_port_id=None, + ha_port_id=self.VIP_PORT_ID, + load_balancer=self.LB_obj, + role=new_role, + cert_expiration=None, + cert_busy=False, + vrrp_interface=None, + vrrp_id=None, + vrrp_priority=constants.ROLE_BACKUP_PRIORITY, + cached_zone=None, + created_at=new_created_at, + updated_at=new_updated_at, + image_id=new_image_id + ) + + test_Amp_obj.update(update_dict) + + self.assertEqual(reference_Amp_obj, test_Amp_obj) diff --git a/octavia/tests/unit/compute/drivers/test_nova_driver.py b/octavia/tests/unit/compute/drivers/test_nova_driver.py index 7435cbc6b7..1d80adc7e0 100644 --- a/octavia/tests/unit/compute/drivers/test_nova_driver.py +++ b/octavia/tests/unit/compute/drivers/test_nova_driver.py @@ -100,7 +100,8 @@ class TestNovaClient(base.TestCase): self.amphora = models.Amphora( compute_id=uuidutils.generate_uuid(), status='ACTIVE', - lb_network_ip='10.0.0.1' + lb_network_ip='10.0.0.1', + image_id=uuidutils.generate_uuid() ) self.nova_response = mock.Mock() @@ -108,6 +109,7 @@ class TestNovaClient(base.TestCase): self.nova_response.status = 'ACTIVE' self.nova_response.fault = 'FAKE_FAULT' setattr(self.nova_response, 'OS-EXT-AZ:availability_zone', None) + self.nova_response.image = {'id': self.amphora.image_id} self.interface_list = mock.MagicMock() self.interface_list.net_id = '1' diff --git a/octavia/tests/unit/controller/worker/tasks/test_database_tasks.py b/octavia/tests/unit/controller/worker/tasks/test_database_tasks.py index 4e9eae2f11..308183c090 100644 --- a/octavia/tests/unit/controller/worker/tasks/test_database_tasks.py +++ b/octavia/tests/unit/controller/worker/tasks/test_database_tasks.py @@ -49,6 +49,8 @@ HA_IP = '192.0.5.4' AMP_ROLE = 'FAKE_ROLE' VRRP_ID = random.randrange(255) VRRP_PRIORITY = random.randrange(100) +CACHED_ZONE = 'zone1' +IMAGE_ID = uuidutils.generate_uuid() _amphora_mock = mock.MagicMock() _amphora_mock.id = AMP_ID @@ -116,6 +118,10 @@ zfJ3Bo+P7In9fsHbyDAqIhMwDQYJKoZIhvcNAQELBQADQQBenkZ2k7RgZqgj+dxA D7BF8MN1oUAOpyYqAjkGddSEuMyNmwtHKZI1dyQ0gBIQdiU9yAG2oTbUIK4msbBV uJIQ -----END CERTIFICATE-----""" +_compute_mock = mock.MagicMock() +_compute_mock.lb_network_ip = LB_NET_IP +_compute_mock.cached_zone = CACHED_ZONE +_compute_mock.image_id = IMAGE_ID @mock.patch('octavia.db.repositories.AmphoraRepository.delete') @@ -862,6 +868,31 @@ class TestDatabaseTasks(base.TestCase): compute_id=COMPUTE_ID, lb_network_ip=LB_NET_IP) + @mock.patch('octavia.db.repositories.AmphoraRepository.get') + def test_update_amphora_info(self, + mock_amphora_repo_get, + mock_generate_uuid, + mock_LOG, + mock_get_session, + mock_loadbalancer_repo_update, + mock_listener_repo_update, + mock_amphora_repo_update, + mock_amphora_repo_delete): + + update_amphora_info = database_tasks.UpdateAmphoraInfo() + update_amphora_info.execute(AMP_ID, _compute_mock) + + repo.AmphoraRepository.update.assert_called_once_with( + 'TEST', + AMP_ID, + lb_network_ip=LB_NET_IP, + cached_zone=CACHED_ZONE, + image_id=IMAGE_ID) + + repo.AmphoraRepository.get.assert_called_once_with( + 'TEST', + id=AMP_ID) + def test_mark_listener_active_in_db(self, mock_generate_uuid, mock_LOG, diff --git a/releasenotes/notes/new-amphora-fields-fa3ffc5801b5e551.yaml b/releasenotes/notes/new-amphora-fields-fa3ffc5801b5e551.yaml new file mode 100644 index 0000000000..3106a284aa --- /dev/null +++ b/releasenotes/notes/new-amphora-fields-fa3ffc5801b5e551.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Amphora API now returns the field `image_id` which is the ID of the glance + image used to boot the amphora.