Allow run metadata api per cell
Adds configuration option ``[api]/local_metadata_per_cell`` to allow user run Nova metadata API service per cell. Doing this can avoid query API DB for instance information each time an instance query for its metadata. Implements blueprint run-meta-api-per-cell Change-Id: I2e6ebb551e782e8aa0ac90169f4d4b8895311b3c
This commit is contained in:
parent
a1c01f97ae
commit
e2e372b2b1
@ -22,7 +22,9 @@ Description
|
|||||||
===========
|
===========
|
||||||
|
|
||||||
:program:`nova-api-metadata` is a server daemon that serves the Nova Metadata
|
:program:`nova-api-metadata` is a server daemon that serves the Nova Metadata
|
||||||
API.
|
API. This daemon routes database requests via the ``nova-conductor`` service,
|
||||||
|
so there are some considerations about using this in a
|
||||||
|
:ref:`multi-cell layout <cells-v2-layout-metadata-api>`.
|
||||||
|
|
||||||
Options
|
Options
|
||||||
=======
|
=======
|
||||||
|
@ -291,14 +291,35 @@ documentation
|
|||||||
<configuration/opts.html#oslo_messaging_notifications.transport_url>` for more
|
<configuration/opts.html#oslo_messaging_notifications.transport_url>` for more
|
||||||
details.
|
details.
|
||||||
|
|
||||||
|
.. _cells-v2-layout-metadata-api:
|
||||||
|
|
||||||
Nova Metadata API service
|
Nova Metadata API service
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The Nova metadata API service should be global across all cells, and
|
Starting from the Stein release, the Nova Metadata API service
|
||||||
thus be configured as an API-level service with access to the
|
can be run either globally or per cell using the
|
||||||
``[api_database]/connection`` information. The nova metadata API service must
|
:oslo.config:option:`api.local_metadata_per_cell` configuration option.
|
||||||
not be run as a standalone service (e.g. must not be run via the
|
|
||||||
nova-api-metadata script).
|
**Global**
|
||||||
|
|
||||||
|
If you have networks that span cells, you might need to run Nova metadata API
|
||||||
|
globally. When running globally, it should be configured as an API-level
|
||||||
|
service with access to the :oslo.config:option:`api_database.connection`
|
||||||
|
information. The nova metadata API service must not be run as a standalone
|
||||||
|
service in this case (e.g. must not be run via the nova-api-metadata script).
|
||||||
|
|
||||||
|
**Local per cell**
|
||||||
|
|
||||||
|
Running Nova metadata API per cell can have better performance and data
|
||||||
|
isolation in a muli-cell deployment. If your networks are segmented along
|
||||||
|
cell boundaries, then you can run Nova metadata API service per cell. If
|
||||||
|
you choose to run it per cell, you should also configure each
|
||||||
|
`Neutron metadata-agent`_ to point to the corresponding nova-metadata-api.
|
||||||
|
The nova metadata API service must be run as a standalone service in this
|
||||||
|
case (e.g. must be run via the nova-api-metadata script).
|
||||||
|
|
||||||
|
.. _Neutron metadata-agent: https://docs.openstack.org/neutron/latest/configuration/metadata-agent.html?#DEFAULT.nova_metadata_host
|
||||||
|
|
||||||
|
|
||||||
Consoleauth service and console proxies
|
Consoleauth service and console proxies
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -680,6 +680,12 @@ def get_metadata_by_instance_id(instance_id, address, ctxt=None):
|
|||||||
'metadata', 'system_metadata',
|
'metadata', 'system_metadata',
|
||||||
'security_groups', 'keypairs',
|
'security_groups', 'keypairs',
|
||||||
'device_metadata']
|
'device_metadata']
|
||||||
|
|
||||||
|
if CONF.api.local_metadata_per_cell:
|
||||||
|
instance = objects.Instance.get_by_uuid(ctxt, instance_id,
|
||||||
|
expected_attrs=attrs)
|
||||||
|
return InstanceMetadata(instance, address)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
im = objects.InstanceMapping.get_by_instance_uuid(ctxt, instance_id)
|
im = objects.InstanceMapping.get_by_instance_uuid(ctxt, instance_id)
|
||||||
except exception.InstanceMappingNotFound:
|
except exception.InstanceMappingNotFound:
|
||||||
|
@ -17,6 +17,7 @@ import six
|
|||||||
from six.moves import range
|
from six.moves import range
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
|
import nova.conf
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
@ -24,6 +25,8 @@ from nova import objects
|
|||||||
from nova import utils
|
from nova import utils
|
||||||
|
|
||||||
|
|
||||||
|
CONF = nova.conf.CONF
|
||||||
|
|
||||||
CHUNKS = 4
|
CHUNKS = 4
|
||||||
CHUNK_LENGTH = 255
|
CHUNK_LENGTH = 255
|
||||||
MAX_SIZE = CHUNKS * CHUNK_LENGTH
|
MAX_SIZE = CHUNKS * CHUNK_LENGTH
|
||||||
@ -68,12 +71,18 @@ def handle_password(req, meta_data):
|
|||||||
msg = _("Request is too large.")
|
msg = _("Request is too large.")
|
||||||
raise exc.HTTPBadRequest(explanation=msg)
|
raise exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
im = objects.InstanceMapping.get_by_instance_uuid(ctxt, meta_data.uuid)
|
if CONF.api.local_metadata_per_cell:
|
||||||
with context.target_cell(ctxt, im.cell_mapping) as cctxt:
|
instance = objects.Instance.get_by_uuid(ctxt, meta_data.uuid)
|
||||||
try:
|
else:
|
||||||
instance = objects.Instance.get_by_uuid(cctxt, meta_data.uuid)
|
im = objects.InstanceMapping.get_by_instance_uuid(
|
||||||
except exception.InstanceNotFound as e:
|
ctxt, meta_data.uuid)
|
||||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
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.system_metadata.update(convert_password(ctxt, req.body))
|
||||||
instance.save()
|
instance.save()
|
||||||
else:
|
else:
|
||||||
|
@ -195,6 +195,19 @@ metadata caching is disabled entirely; this is generally not recommended for
|
|||||||
performance reasons. Increasing this setting should improve response times
|
performance reasons. Increasing this setting should improve response times
|
||||||
of the metadata API when under heavy load. Higher values may increase memory
|
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.
|
usage, and result in longer times for host metadata changes to take effect.
|
||||||
|
"""),
|
||||||
|
cfg.BoolOpt("local_metadata_per_cell",
|
||||||
|
default=False,
|
||||||
|
help="""
|
||||||
|
Indicates that the nova-metadata API service has been deployed per-cell, so
|
||||||
|
that we can have better performance and data isolation in a multi-cell
|
||||||
|
deployment. Users should consider the use of this configuration depending on
|
||||||
|
how neutron is setup. If you have networks that span cells, you might need to
|
||||||
|
run nova-metadata API service globally. If your networks are segmented along
|
||||||
|
cell boundaries, then you can run nova-metadata API service per cell. When
|
||||||
|
running nova-metadata API service per cell, you should also configure each
|
||||||
|
Neutron metadata-agent to point to the corresponding nova-metadata API
|
||||||
|
service.
|
||||||
"""),
|
"""),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1617,6 +1617,23 @@ class MetadataHandlerTestCase(test.TestCase):
|
|||||||
mock_get_im.assert_called_once_with(ctxt, 'foo')
|
mock_get_im.assert_called_once_with(ctxt, 'foo')
|
||||||
imd.assert_called_once_with(inst, 'bar')
|
imd.assert_called_once_with(inst, 'bar')
|
||||||
|
|
||||||
|
@mock.patch.object(objects.Instance, 'get_by_uuid')
|
||||||
|
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
||||||
|
def test_get_metadata_by_instance_id_with_local_meta(self, mock_get_im,
|
||||||
|
mock_get_inst):
|
||||||
|
# Test that if local_metadata_per_cell is set to True, we don't
|
||||||
|
# query API DB for instance mapping.
|
||||||
|
self.flags(local_metadata_per_cell=True, group='api')
|
||||||
|
ctxt = context.RequestContext()
|
||||||
|
inst = objects.Instance()
|
||||||
|
mock_get_inst.return_value = inst
|
||||||
|
|
||||||
|
with mock.patch.object(base, 'InstanceMetadata') as imd:
|
||||||
|
base.get_metadata_by_instance_id('foo', 'bar', ctxt=ctxt)
|
||||||
|
|
||||||
|
mock_get_im.assert_not_called()
|
||||||
|
imd.assert_called_once_with(inst, 'bar')
|
||||||
|
|
||||||
|
|
||||||
class MetadataPasswordTestCase(test.TestCase):
|
class MetadataPasswordTestCase(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -1655,7 +1672,10 @@ class MetadataPasswordTestCase(test.TestCase):
|
|||||||
|
|
||||||
@mock.patch('nova.objects.InstanceMapping.get_by_instance_uuid')
|
@mock.patch('nova.objects.InstanceMapping.get_by_instance_uuid')
|
||||||
@mock.patch('nova.objects.Instance.get_by_uuid')
|
@mock.patch('nova.objects.Instance.get_by_uuid')
|
||||||
def _try_set_password(self, get_by_uuid, get_mapping, val=b'bar'):
|
def _try_set_password(self, get_by_uuid, get_mapping, val=b'bar',
|
||||||
|
use_local_meta=False):
|
||||||
|
if use_local_meta:
|
||||||
|
self.flags(local_metadata_per_cell=True, group='api')
|
||||||
request = webob.Request.blank('')
|
request = webob.Request.blank('')
|
||||||
request.method = 'POST'
|
request.method = 'POST'
|
||||||
request.body = val
|
request.body = val
|
||||||
@ -1667,12 +1687,19 @@ class MetadataPasswordTestCase(test.TestCase):
|
|||||||
save.assert_called_once_with()
|
save.assert_called_once_with()
|
||||||
|
|
||||||
self.assertIn('password_0', self.instance.system_metadata)
|
self.assertIn('password_0', self.instance.system_metadata)
|
||||||
get_mapping.assert_called_once_with(mock.ANY, self.instance.uuid)
|
if use_local_meta:
|
||||||
|
get_mapping.assert_not_called()
|
||||||
|
else:
|
||||||
|
get_mapping.assert_called_once_with(mock.ANY, self.instance.uuid)
|
||||||
|
|
||||||
def test_set_password(self):
|
def test_set_password(self):
|
||||||
self.mdinst.password = ''
|
self.mdinst.password = ''
|
||||||
self._try_set_password()
|
self._try_set_password()
|
||||||
|
|
||||||
|
def test_set_password_local_meta(self):
|
||||||
|
self.mdinst.password = ''
|
||||||
|
self._try_set_password(use_local_meta=True)
|
||||||
|
|
||||||
def test_conflict(self):
|
def test_conflict(self):
|
||||||
self.mdinst.password = 'foo'
|
self.mdinst.password = 'foo'
|
||||||
self.assertRaises(webob.exc.HTTPConflict,
|
self.assertRaises(webob.exc.HTTPConflict,
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added configuration option ``[api]/local_metadata_per_cell`` to allow
|
||||||
|
users to run Nova metadata API service per cell. Doing this could provide
|
||||||
|
performance improvement and data isolation in a multi-cell deployment.
|
||||||
|
But it has some caveats, see the
|
||||||
|
`Metadata api service in cells v2 layout`_ for more details.
|
||||||
|
|
||||||
|
.. _Metadata api service in cells v2 layout: https://docs.openstack.org/nova/latest/user/cellsv2-layout.html#nova-metadata-api-service
|
Loading…
Reference in New Issue
Block a user