glance: support relying on tags to extract image id
Deprecated amp_image_id option with the new amp_image_tag option. Also switched devstack plugin to rely on the tag to update the image used for new load balancers. Implements: blueprint use-glance-tags-to-manage-image Change-Id: Ibc28b2220565667e15ca2b2674e55074d6126ec3
This commit is contained in:
parent
ad84b40f42
commit
fb53fe2340
@ -177,7 +177,10 @@ function octavia_start {
|
||||
fi
|
||||
|
||||
OCTAVIA_AMP_IMAGE_ID=$(glance image-list | grep ${OCTAVIA_AMP_IMAGE_NAME} | awk '{print $2}')
|
||||
iniset $OCTAVIA_CONF controller_worker amp_image_id ${OCTAVIA_AMP_IMAGE_ID}
|
||||
if [ -n "$OCTAVIA_AMP_IMAGE_ID" ]; then
|
||||
glance image-tag-update ${OCTAVIA_AMP_IMAGE_ID} ${OCTAVIA_AMP_IMAGE_TAG}
|
||||
fi
|
||||
iniset $OCTAVIA_CONF controller_worker amp_image_tag ${OCTAVIA_AMP_IMAGE_TAG}
|
||||
|
||||
create_amphora_flavor
|
||||
|
||||
|
@ -37,6 +37,7 @@ OCTAVIA_AMP_SSH_KEY_NAME=${OCTAVIA_AMP_SSH_KEY_NAME:-"octavia_ssh_key"}
|
||||
OCTAVIA_AMP_FLAVOR_ID=${OCTAVIA_AMP_FLAVOR_ID:-"10"}
|
||||
OCTAVIA_AMP_IMAGE_NAME=${OCTAVIA_AMP_IMAGE_NAME:-"amphora-x64-haproxy"}
|
||||
OCTAVIA_AMP_IMAGE_FILE=${OCTAVIA_AMP_IMAGE_FILE:-${OCTAVIA_DIR}/diskimage-create/${OCTAVIA_AMP_IMAGE_NAME}.qcow2}
|
||||
OCTAVIA_AMP_IMAGE_TAG="amphora"
|
||||
|
||||
OCTAVIA_HEALTH_KEY=${OCTAVIA_HEALTH_KEY:-"insecure"}
|
||||
|
||||
|
@ -132,9 +132,12 @@
|
||||
[controller_worker]
|
||||
# amp_active_retries = 10
|
||||
# amp_active_wait_sec = 10
|
||||
# Glance parameters to extract image ID to use for amphora. Only one of
|
||||
# parameters is needed. Using tags is the recommended way to refer to images.
|
||||
# amp_image_id =
|
||||
# amp_image_tag =
|
||||
# Nova parameters to use when booting amphora
|
||||
# amp_flavor_id =
|
||||
# amp_image_id =
|
||||
# amp_ssh_key_name =
|
||||
# amp_ssh_allowed_access = True
|
||||
# amp_network =
|
||||
@ -218,6 +221,19 @@
|
||||
# vrrp_garp_refresh_interval = 5
|
||||
# vrrp_garp_refresh_count = 2
|
||||
|
||||
[glance]
|
||||
# The name of the glance service in the keystone catalog
|
||||
# service_name =
|
||||
# Custom glance endpoint if override is necessary
|
||||
# endpoint =
|
||||
|
||||
# Region in Identity service catalog to use for communication with the OpenStack services.
|
||||
# region_name =
|
||||
|
||||
# Endpoint type in Identity service catalog to use for communication with
|
||||
# the OpenStack services.
|
||||
# endpoint_type = publicURL
|
||||
|
||||
[nova]
|
||||
# The name of the nova service in the keystone catalog
|
||||
# service_name =
|
||||
|
@ -10,6 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from glanceclient import client as glance_client
|
||||
from neutronclient.neutron import client as neutron_client
|
||||
from novaclient import client as nova_client
|
||||
from oslo_log import log as logging
|
||||
@ -19,6 +20,7 @@ from octavia.common import keystone
|
||||
from octavia.i18n import _LE
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
GLANCE_VERSION = '2'
|
||||
NEUTRON_VERSION = '2.0'
|
||||
NOVA_VERSION = '2'
|
||||
|
||||
@ -85,3 +87,35 @@ class NeutronAuth(object):
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Error creating Neutron client."))
|
||||
return cls.neutron_client
|
||||
|
||||
|
||||
class GlanceAuth(object):
|
||||
glance_client = None
|
||||
|
||||
@classmethod
|
||||
def get_glance_client(cls, region, service_name=None, endpoint=None,
|
||||
endpoint_type='publicURL'):
|
||||
"""Create glance client object.
|
||||
|
||||
:param region: The region of the service
|
||||
:param service_name: The name of the glance service in the catalog
|
||||
:param endpoint: The endpoint of the service
|
||||
:param endpoint_type: The endpoint_type of the service
|
||||
:return: a Glance Client object.
|
||||
:raises Exception: if the client cannot be created
|
||||
"""
|
||||
if not cls.glance_client:
|
||||
kwargs = {'region_name': region,
|
||||
'session': keystone.get_session(),
|
||||
'interface': endpoint_type}
|
||||
if service_name:
|
||||
kwargs['service_name'] = service_name
|
||||
if endpoint:
|
||||
kwargs['endpoint'] = endpoint
|
||||
try:
|
||||
cls.glance_client = glance_client.Client(
|
||||
GLANCE_VERSION, **kwargs)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Error creating Glance client."))
|
||||
return cls.glance_client
|
||||
|
@ -210,8 +210,16 @@ controller_worker_opts = [
|
||||
cfg.StrOpt('amp_flavor_id',
|
||||
default='',
|
||||
help=_('Nova instance flavor id for the Amphora')),
|
||||
cfg.StrOpt('amp_image_tag',
|
||||
default='',
|
||||
help=_('Glance image tag for the Amphora image to boot. '
|
||||
'Use this option to be able to update the image '
|
||||
'without reconfiguring Octavia. '
|
||||
'Ignored if amp_image_id is defined.')),
|
||||
cfg.StrOpt('amp_image_id',
|
||||
default='',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Superseded by amp_image_tag option.',
|
||||
help=_('Glance image id for the Amphora image to boot')),
|
||||
cfg.StrOpt('amp_ssh_key_name',
|
||||
default='',
|
||||
@ -377,6 +385,20 @@ neutron_opts = [
|
||||
help=_('Endpoint interface in identity service to use')),
|
||||
]
|
||||
|
||||
glance_opts = [
|
||||
cfg.StrOpt('service_name',
|
||||
help=_('The name of the glance service in the '
|
||||
'keystone catalog')),
|
||||
cfg.StrOpt('endpoint', help=_('A new endpoint to override the endpoint '
|
||||
'in the keystone catalog.')),
|
||||
cfg.StrOpt('region_name',
|
||||
help=_('Region in Identity service catalog to use for '
|
||||
'communication with the OpenStack services.')),
|
||||
cfg.StrOpt('endpoint_type', default='publicURL',
|
||||
help=_('Endpoint interface in identity service to use')),
|
||||
]
|
||||
|
||||
|
||||
# Register the configuration options
|
||||
cfg.CONF.register_opts(core_opts)
|
||||
cfg.CONF.register_opts(amphora_agent_opts, group='amphora_agent')
|
||||
@ -396,6 +418,7 @@ cfg.CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
|
||||
cfg.CONF.register_opts(keystone_authtoken_v3_opts,
|
||||
group='keystone_authtoken_v3')
|
||||
cfg.CONF.register_opts(nova_opts, group='nova')
|
||||
cfg.CONF.register_opts(glance_opts, group='glance')
|
||||
cfg.CONF.register_opts(neutron_opts, group='neutron')
|
||||
|
||||
|
||||
|
@ -174,6 +174,10 @@ class NoReadyAmphoraeException(OctaviaException):
|
||||
message = _LE('There are not any READY amphora available.')
|
||||
|
||||
|
||||
class GlanceNoTaggedImages(OctaviaException):
|
||||
message = _LE("No Glance images are tagged with %(tag)s tag.")
|
||||
|
||||
|
||||
class NoSuitableAmphoraException(OctaviaException):
|
||||
message = _LE('Unable to allocate an amphora due to: %(msg)s')
|
||||
|
||||
|
@ -21,7 +21,8 @@ import six
|
||||
class ComputeBase(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
|
||||
def build(self, name="amphora_name", amphora_flavor=None,
|
||||
image_id=None, image_tag=None,
|
||||
key_name=None, sec_groups=None, network_ids=None,
|
||||
config_drive_files=None, user_data=None, server_group_id=None):
|
||||
"""Build a new amphora.
|
||||
@ -29,6 +30,7 @@ class ComputeBase(object):
|
||||
:param name: Optional name for Amphora
|
||||
:param amphora_flavor: Optionally specify a flavor
|
||||
:param image_id: ID of the base image for the amphora instance
|
||||
:param image_tag: tag of the base image for the amphora instance
|
||||
:param key_name: Optionally specify a keypair
|
||||
:param sec_groups: Optionally specify list of security groups
|
||||
:param network_ids: A list of network IDs to attach to the amphora
|
||||
|
@ -27,21 +27,23 @@ class NoopManager(object):
|
||||
super(NoopManager, self).__init__()
|
||||
self.computeconfig = {}
|
||||
|
||||
def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
|
||||
def build(self, name="amphora_name", amphora_flavor=None,
|
||||
image_id=None, image_tag=None,
|
||||
key_name=None, sec_groups=None, network_ids=None,
|
||||
config_drive_files=None, user_data=None, port_ids=None,
|
||||
server_group_id=None):
|
||||
LOG.debug("Compute %s no-op, build name %s, amphora_flavor %s, "
|
||||
"image_id %s, key_name %s, sec_groups %s, network_ids %s,"
|
||||
"config_drive_files %s, user_data %s, port_ids %s,"
|
||||
"server_group_id %s",
|
||||
self.__class__.__name__, name, amphora_flavor, image_id,
|
||||
"image_id %s, image_tag %s, key_name %s, sec_groups %s, "
|
||||
"network_ids %s, config_drive_files %s, user_data %s, "
|
||||
"port_ids %s, server_group_id %s",
|
||||
self.__class__.__name__,
|
||||
name, amphora_flavor, image_id, image_tag,
|
||||
key_name, sec_groups, network_ids, config_drive_files,
|
||||
user_data, port_ids, server_group_id)
|
||||
self.computeconfig[(name, amphora_flavor, image_id, key_name,
|
||||
user_data)] = (
|
||||
self.computeconfig[(name, amphora_flavor, image_id, image_tag,
|
||||
key_name, user_data)] = (
|
||||
name, amphora_flavor,
|
||||
image_id, key_name, sec_groups,
|
||||
image_id, image_tag, key_name, sec_groups,
|
||||
network_ids, config_drive_files,
|
||||
user_data, port_ids, 'build')
|
||||
compute_id = uuidutils.generate_uuid()
|
||||
@ -84,12 +86,14 @@ class NoopComputeDriver(driver_base.ComputeBase):
|
||||
super(NoopComputeDriver, self).__init__()
|
||||
self.driver = NoopManager()
|
||||
|
||||
def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
|
||||
def build(self, name="amphora_name", amphora_flavor=None,
|
||||
image_id=None, image_tag=None,
|
||||
key_name=None, sec_groups=None, network_ids=None,
|
||||
config_drive_files=None, user_data=None, port_ids=None,
|
||||
server_group_id=None):
|
||||
|
||||
compute_id = self.driver.build(name, amphora_flavor, image_id,
|
||||
compute_id = self.driver.build(name, amphora_flavor,
|
||||
image_id, image_tag,
|
||||
key_name, sec_groups, network_ids,
|
||||
config_drive_files, user_data, port_ids,
|
||||
server_group_id)
|
||||
|
@ -26,11 +26,40 @@ from octavia.i18n import _LE, _LW
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_group('glance', 'octavia.common.config')
|
||||
CONF.import_group('keystone_authtoken', 'octavia.common.config')
|
||||
CONF.import_group('networking', 'octavia.common.config')
|
||||
CONF.import_group('nova', 'octavia.common.config')
|
||||
|
||||
|
||||
def _extract_amp_image_id_by_tag(client, image_tag):
|
||||
images = list(client.images.list(
|
||||
filters={'tag': [image_tag]},
|
||||
sort='created_at'))
|
||||
if not images:
|
||||
raise exceptions.GlanceNoTaggedImages(tag=image_tag)
|
||||
image_id = images[-1]['id']
|
||||
num_images = len(images)
|
||||
if num_images > 1:
|
||||
LOG.warn(
|
||||
_LW("A single Glance image should be tagged with %(tag)s tag, "
|
||||
"but %(num)d found. Using %(image_id)s."),
|
||||
{'tag': image_tag, 'num': num_images, 'image_id': image_id}
|
||||
)
|
||||
return image_id
|
||||
|
||||
|
||||
def _get_image_uuid(client, image_id, image_tag):
|
||||
if image_id:
|
||||
if image_tag:
|
||||
LOG.warn(
|
||||
_LW("Both amp_image_id and amp_image_tag options defined. "
|
||||
"Using the former."))
|
||||
return image_id
|
||||
|
||||
return _extract_amp_image_id_by_tag(client, image_tag)
|
||||
|
||||
|
||||
class VirtualMachineManager(compute_base.ComputeBase):
|
||||
'''Compute implementation of virtual machines via nova.'''
|
||||
|
||||
@ -41,10 +70,16 @@ class VirtualMachineManager(compute_base.ComputeBase):
|
||||
endpoint=CONF.nova.endpoint,
|
||||
region=CONF.nova.region_name,
|
||||
endpoint_type=CONF.nova.endpoint_type)
|
||||
self._glance_client = clients.GlanceAuth.get_glance_client(
|
||||
service_name=CONF.glance.service_name,
|
||||
endpoint=CONF.glance.endpoint,
|
||||
region=CONF.glance.region_name,
|
||||
endpoint_type=CONF.glance.endpoint_type)
|
||||
self.manager = self._nova_client.servers
|
||||
self.server_groups = self._nova_client.server_groups
|
||||
|
||||
def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
|
||||
def build(self, name="amphora_name", amphora_flavor=None,
|
||||
image_id=None, image_tag=None,
|
||||
key_name=None, sec_groups=None, network_ids=None,
|
||||
port_ids=None, config_drive_files=None, user_data=None,
|
||||
server_group_id=None):
|
||||
@ -53,6 +88,7 @@ class VirtualMachineManager(compute_base.ComputeBase):
|
||||
:param name: optional name for amphora
|
||||
:param amphora_flavor: image flavor for virtual machine
|
||||
:param image_id: image ID for virtual machine
|
||||
:param image_tag: image tag for virtual machine
|
||||
:param key_name: keypair to add to the virtual machine
|
||||
:param sec_groups: Security group IDs for virtual machine
|
||||
:param network_ids: Network IDs to include on virtual machine
|
||||
@ -84,6 +120,8 @@ class VirtualMachineManager(compute_base.ComputeBase):
|
||||
server_group = None if server_group_id is None else {
|
||||
"group": server_group_id}
|
||||
|
||||
image_id = _get_image_uuid(
|
||||
self._glance_client, image_id, image_tag)
|
||||
amphora = self.manager.create(
|
||||
name=name, image=image_id, flavor=amphora_flavor,
|
||||
key_name=key_name, security_groups=sec_groups,
|
||||
|
@ -78,6 +78,7 @@ class ComputeCreate(BaseComputeTask):
|
||||
name="amphora-" + amphora_id,
|
||||
amphora_flavor=CONF.controller_worker.amp_flavor_id,
|
||||
image_id=CONF.controller_worker.amp_image_id,
|
||||
image_tag=CONF.controller_worker.amp_image_tag,
|
||||
key_name=key_name,
|
||||
sec_groups=CONF.controller_worker.amp_secgroup_list,
|
||||
network_ids=[CONF.controller_worker.amp_network],
|
||||
|
@ -10,6 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import glanceclient.v2
|
||||
import mock
|
||||
import neutronclient.v2_0
|
||||
import novaclient.v2
|
||||
@ -98,3 +99,42 @@ class TestNeutronAuth(base.TestCase):
|
||||
region="test-region", service_name="neutronEndpoint1",
|
||||
endpoint="test-endpoint", endpoint_type='publicURL')
|
||||
self.assertIs(bc1, bc2)
|
||||
|
||||
|
||||
class TestGlanceAuth(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
CONF.set_override(group='keystone_authtoken', name='auth_version',
|
||||
override='2', enforce_type=True)
|
||||
# Reset the session and client
|
||||
clients.GlanceAuth.glance_client = None
|
||||
keystone._SESSION = None
|
||||
|
||||
super(TestGlanceAuth, self).setUp()
|
||||
|
||||
def test_get_glance_client(self):
|
||||
# There should be no existing client
|
||||
self.assertIsNone(
|
||||
clients.GlanceAuth.glance_client
|
||||
)
|
||||
|
||||
# Mock out the keystone session and get the client
|
||||
keystone._SESSION = mock.MagicMock()
|
||||
bc1 = clients.GlanceAuth.get_glance_client(
|
||||
region=None, endpoint_type='publicURL')
|
||||
|
||||
# Our returned client should also be the saved client
|
||||
self.assertIsInstance(
|
||||
clients.GlanceAuth.glance_client,
|
||||
glanceclient.v2.client.Client
|
||||
)
|
||||
self.assertIs(
|
||||
clients.GlanceAuth.glance_client,
|
||||
bc1
|
||||
)
|
||||
|
||||
# Getting the session again should return the same object
|
||||
bc2 = clients.GlanceAuth.get_glance_client(
|
||||
region="test-region", service_name="glanceEndpoint1",
|
||||
endpoint="test-endpoint", endpoint_type='publicURL')
|
||||
self.assertIs(bc1, bc2)
|
||||
|
@ -30,6 +30,7 @@ class TestNoopComputeDriver(base.TestCase):
|
||||
self.name = "amphora_name"
|
||||
self.amphora_flavor = "m1.tiny"
|
||||
self.image_id = self.FAKE_UUID_2
|
||||
self.image_tag = "faketag"
|
||||
self.key_name = "key_name"
|
||||
self.sec_groups = "default"
|
||||
self.network_ids = self.FAKE_UUID_3
|
||||
@ -41,18 +42,21 @@ class TestNoopComputeDriver(base.TestCase):
|
||||
self.server_group_id = self.FAKE_UUID_1
|
||||
|
||||
def build(self):
|
||||
self.driver.build(self.name, self.amphora_flavor, self.image_id,
|
||||
self.driver.build(self.name, self.amphora_flavor,
|
||||
self.image_id, self.image_tag,
|
||||
self.key_name, self.sec_groups, self.network_ids,
|
||||
self.confdrivefiles, self.user_data,
|
||||
self.server_group_id)
|
||||
|
||||
self.assertEqual((self.name, self.amphora_flavor, self.image_id,
|
||||
self.assertEqual((self.name, self.amphora_flavor,
|
||||
self.image_id, self.image_tag,
|
||||
self.key_name, self.sec_groups, self.network_ids,
|
||||
self.config_drive_files, self.user_data,
|
||||
self.server_group_id, 'build'),
|
||||
self.driver.driver.computeconfig[(self.name,
|
||||
self.amphora_flavor,
|
||||
self.image_id,
|
||||
self.image_tag,
|
||||
self.key_name,
|
||||
self.sec_groups,
|
||||
self.network_ids,
|
||||
|
@ -17,6 +17,7 @@ from novaclient import exceptions as nova_exceptions
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from octavia.common import clients
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models as models
|
||||
from octavia.common import exceptions
|
||||
@ -27,6 +28,62 @@ import octavia.tests.unit.base as base
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class Test_GetImageUuid(base.TestCase):
|
||||
|
||||
def test__get_image_uuid_tag(self):
|
||||
client = mock.Mock()
|
||||
with mock.patch.object(nova_common,
|
||||
'_extract_amp_image_id_by_tag',
|
||||
return_value='fakeid') as extract:
|
||||
image_id = nova_common._get_image_uuid(client, '', 'faketag')
|
||||
self.assertEqual('fakeid', image_id)
|
||||
extract.assert_called_with(client, 'faketag')
|
||||
|
||||
def test__get_image_uuid_notag(self):
|
||||
client = mock.Mock()
|
||||
image_id = nova_common._get_image_uuid(client, 'fakeid', '')
|
||||
self.assertEqual('fakeid', image_id)
|
||||
|
||||
def test__get_image_uuid_id_beats_tag(self):
|
||||
client = mock.Mock()
|
||||
image_id = nova_common._get_image_uuid(client, 'fakeid', 'faketag')
|
||||
self.assertEqual('fakeid', image_id)
|
||||
|
||||
|
||||
class Test_ExtractAmpImageIdByTag(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(Test_ExtractAmpImageIdByTag, self).setUp()
|
||||
client_mock = mock.patch.object(clients.GlanceAuth,
|
||||
'get_glance_client')
|
||||
self.client = client_mock.start().return_value
|
||||
|
||||
def test_no_images(self):
|
||||
self.client.images.list.return_value = []
|
||||
self.assertRaises(
|
||||
exceptions.GlanceNoTaggedImages,
|
||||
nova_common._extract_amp_image_id_by_tag, self.client, 'faketag')
|
||||
|
||||
def test_single_image(self):
|
||||
images = [
|
||||
{'id': uuidutils.generate_uuid(), 'tag': 'faketag'}
|
||||
]
|
||||
self.client.images.list.return_value = images
|
||||
image_id = nova_common._extract_amp_image_id_by_tag(self.client,
|
||||
'faketag')
|
||||
self.assertIn(image_id, images[0]['id'])
|
||||
|
||||
def test_multiple_images_returns_one_of_images(self):
|
||||
images = [
|
||||
{'id': image_id, 'tag': 'faketag'}
|
||||
for image_id in [uuidutils.generate_uuid() for i in range(10)]
|
||||
]
|
||||
self.client.images.list.return_value = images
|
||||
image_id = nova_common._extract_amp_image_id_by_tag(self.client,
|
||||
'faketag')
|
||||
self.assertIn(image_id, [image['id'] for image in images])
|
||||
|
||||
|
||||
class TestNovaClient(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -108,6 +165,15 @@ class TestNovaClient(base.TestCase):
|
||||
self.manager.manager.create.side_effect = Exception
|
||||
self.assertRaises(exceptions.ComputeBuildException, self.manager.build)
|
||||
|
||||
def test_build_extracts_image_id_by_tag(self):
|
||||
expected_id = 'fakeid-by-tag'
|
||||
with mock.patch.object(nova_common, '_get_image_uuid',
|
||||
return_value=expected_id):
|
||||
self.manager.build(image_id='fakeid', image_tag='tag')
|
||||
|
||||
self.assertEqual(expected_id,
|
||||
self.manager.manager.create.call_args[1]['image'])
|
||||
|
||||
def test_delete(self):
|
||||
amphora_id = self.manager.build(amphora_flavor=1, image_id=1,
|
||||
key_name=1, sec_groups=1,
|
||||
|
@ -27,6 +27,7 @@ import octavia.tests.unit.base as base
|
||||
|
||||
AMP_FLAVOR_ID = 10
|
||||
AMP_IMAGE_ID = 11
|
||||
AMP_IMAGE_TAG = 'glance_tag'
|
||||
AMP_SSH_KEY_NAME = None
|
||||
AMP_NET = uuidutils.generate_uuid()
|
||||
AMP_SEC_GROUPS = []
|
||||
@ -62,6 +63,7 @@ class TestComputeTasks(base.TestCase):
|
||||
conf = oslo_fixture.Config(cfg.CONF)
|
||||
conf.config(group="controller_worker", amp_flavor_id=AMP_FLAVOR_ID)
|
||||
conf.config(group="controller_worker", amp_image_id=AMP_IMAGE_ID)
|
||||
conf.config(group="controller_worker", amp_image_tag=AMP_IMAGE_TAG)
|
||||
conf.config(group="controller_worker",
|
||||
amp_ssh_key_name=AMP_SSH_KEY_NAME)
|
||||
conf.config(group="controller_worker", amp_network=AMP_NET)
|
||||
@ -95,6 +97,7 @@ class TestComputeTasks(base.TestCase):
|
||||
name="amphora-" + _amphora_mock.id,
|
||||
amphora_flavor=AMP_FLAVOR_ID,
|
||||
image_id=AMP_IMAGE_ID,
|
||||
image_tag=AMP_IMAGE_TAG,
|
||||
key_name=AMP_SSH_KEY_NAME,
|
||||
sec_groups=AMP_SEC_GROUPS,
|
||||
network_ids=[AMP_NET],
|
||||
@ -154,6 +157,7 @@ class TestComputeTasks(base.TestCase):
|
||||
name="amphora-" + _amphora_mock.id,
|
||||
amphora_flavor=AMP_FLAVOR_ID,
|
||||
image_id=AMP_IMAGE_ID,
|
||||
image_tag=AMP_IMAGE_TAG,
|
||||
key_name=AMP_SSH_KEY_NAME,
|
||||
sec_groups=AMP_SEC_GROUPS,
|
||||
network_ids=[AMP_NET],
|
||||
@ -212,6 +216,7 @@ class TestComputeTasks(base.TestCase):
|
||||
name="amphora-" + _amphora_mock.id,
|
||||
amphora_flavor=AMP_FLAVOR_ID,
|
||||
image_id=AMP_IMAGE_ID,
|
||||
image_tag=AMP_IMAGE_TAG,
|
||||
key_name=None,
|
||||
sec_groups=AMP_SEC_GROUPS,
|
||||
network_ids=[AMP_NET],
|
||||
@ -268,6 +273,7 @@ class TestComputeTasks(base.TestCase):
|
||||
name="amphora-" + _amphora_mock.id,
|
||||
amphora_flavor=AMP_FLAVOR_ID,
|
||||
image_id=AMP_IMAGE_ID,
|
||||
image_tag=AMP_IMAGE_TAG,
|
||||
key_name=AMP_SSH_KEY_NAME,
|
||||
sec_groups=AMP_SEC_GROUPS,
|
||||
network_ids=[AMP_NET],
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- Glance image containing the latest Amphora image can now be referenced
|
||||
using a Glance tag. To use the feature, set amp_image_tag in
|
||||
[controller_worker]. Note that amp_image_id should be unset for the new
|
||||
feature to take into effect.
|
||||
upgrade:
|
||||
- amp_image_id option is deprecated and will be removed in one of the next
|
||||
releases. Operators are adviced to migrate to the new amp_image_tag option.
|
@ -28,6 +28,7 @@ oslo.service>=1.0.0 # Apache-2.0
|
||||
oslo.utils>=3.5.0 # Apache-2.0
|
||||
PyMySQL>=0.6.2 # MIT License
|
||||
python-barbicanclient>=3.3.0 # Apache-2.0
|
||||
python-glanceclient>=1.2.0 # Apache-2.0
|
||||
python-novaclient!=2.33.0,>=2.29.0 # Apache-2.0
|
||||
pyOpenSSL>=0.14 # Apache-2.0
|
||||
WSME>=0.8 # MIT
|
||||
|
147
specs/version1/use_glance_tag_to_refer_to_image.rst
Normal file
147
specs/version1/use_glance_tag_to_refer_to_image.rst
Normal file
@ -0,0 +1,147 @@
|
||||
..
|
||||
This work is licensed under a Creative Commons Attribution 3.0 Unported
|
||||
License.
|
||||
|
||||
http://creativecommons.org/licenses/by/3.0/legalcode
|
||||
|
||||
===============================================================
|
||||
Allow to use Glance image tag to refer to desired Amphora image
|
||||
===============================================================
|
||||
|
||||
https://blueprints.launchpad.net/octavia/+spec/use-glance-tags-to-manage-image
|
||||
|
||||
Currently, Octavia allows to define the Glance image ID to be used to boot new
|
||||
Amphoras. This spec suggests another way to define the desired image, by using
|
||||
Glance tagging mechanism.
|
||||
|
||||
|
||||
Problem description
|
||||
===================
|
||||
|
||||
The need to hardcode image ID in the service configuration file has drawbacks.
|
||||
|
||||
Specifically, when an updated image is uploaded into Glance, the operator is
|
||||
required to orchestrate configuration file update on all Octavia nodes and then
|
||||
restart all Octavia workers to apply the change. It is both complex and error
|
||||
prone.
|
||||
|
||||
|
||||
Proposed change
|
||||
===============
|
||||
|
||||
The spec suggests an alternative way to configure the desired Glance image to
|
||||
be used for Octavia: using Glance image tagging feature.
|
||||
|
||||
Glance allows to tag an image with any tag which is represented by a string
|
||||
value.
|
||||
|
||||
With the proposed change, Octavia operator will be able to tell Octavia to use
|
||||
an image with the specified tag. Then Octavia will talk to Glance to determine
|
||||
the exact image ID that is marked with the tag, before booting a new Amphora.
|
||||
|
||||
|
||||
Alternatives
|
||||
------------
|
||||
|
||||
Alternatively, we could make Nova talk to Glance to determine the desired image
|
||||
ID based on the tag provided by Octavia. This approach is not supported by Nova
|
||||
community because they don't want to impose the complexity into their code
|
||||
base.
|
||||
|
||||
Another alternative is to use image name instead of its ID. Nova is capable of
|
||||
fetching the right image from Glance by name as long as the name is unique.
|
||||
This is not optimal in case when the operator does not want to remove the old
|
||||
Amphora image right after a new image is uploaded (for example, if the operator
|
||||
wants to test the new image before cleaning up the old one).
|
||||
|
||||
Data model impact
|
||||
-----------------
|
||||
|
||||
None.
|
||||
|
||||
REST API impact
|
||||
---------------
|
||||
|
||||
None.
|
||||
|
||||
Security impact
|
||||
---------------
|
||||
|
||||
Image tags should be managed by the same user that owns the images themselves.
|
||||
|
||||
Notifications impact
|
||||
--------------------
|
||||
|
||||
None.
|
||||
|
||||
Other end user impact
|
||||
---------------------
|
||||
|
||||
The proposed change should not break existing mechanism. To achieve that, the
|
||||
new mechanism will be guarded with a new configuration option that will store
|
||||
the desired Glance tag.
|
||||
|
||||
Performance Impact
|
||||
------------------
|
||||
|
||||
If the feature is used, Octavia will need to reach to Glance before booting a
|
||||
new Amphora. The performance impact is well isolated and is not expected to be
|
||||
significant.
|
||||
|
||||
Other deployer impact
|
||||
---------------------
|
||||
|
||||
The change couples Octavia with Glance. It should not be an issue since there
|
||||
are no use cases to use Octavia without Glance installed.
|
||||
|
||||
The new feature deprecates amp_image_id option. Operators that still use the
|
||||
old image referencing mechanism will be adviced to switch to the new option.
|
||||
|
||||
Eventually, the old mechanism will be removed from the tree.
|
||||
|
||||
Developer impact
|
||||
----------------
|
||||
|
||||
None.
|
||||
|
||||
Implementation
|
||||
==============
|
||||
|
||||
Assignee(s)
|
||||
-----------
|
||||
|
||||
Primary assignee:
|
||||
ihrachys (Ihar Hrachyshka)
|
||||
|
||||
Work Items
|
||||
----------
|
||||
|
||||
* introduce glanceclient integration into nova compute driver
|
||||
* introduce new configuration option to store the glance tag
|
||||
* introduce devstack plugin support to configure the feature
|
||||
* provide documentation for the new feature
|
||||
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
None.
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
Unit tests will be written to cover the feature.
|
||||
|
||||
Octavia plugin will be switched to using the new glance image referencing
|
||||
mechanism. Tempest tests will be implemented to test the new feature.
|
||||
|
||||
|
||||
Documentation Impact
|
||||
====================
|
||||
|
||||
New feature should be documented in operator visible guides.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
Loading…
Reference in New Issue
Block a user