nova-net: Remove nova-network security group driver

This is another self-explanatory change. We remove the driver along with
related tests. Some additional API tests need to be fixed since these
were using the nova-network security group driver.

Change-Id: Ia05215b2e7168563c54b78263625125537b7234c
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
This commit is contained in:
Stephen Finucane 2019-12-06 16:58:40 +00:00
parent d5c9423e40
commit bf0d099f4b
4 changed files with 25 additions and 452 deletions

View File

@ -63,7 +63,6 @@ from nova import network
from nova.network import model as network_model
from nova.network.neutronv2 import constants
from nova.network.security_group import openstack_driver
from nova.network.security_group import security_group_base
from nova import objects
from nova.objects import base as obj_base
from nova.objects import block_device as block_device_obj
@ -97,8 +96,6 @@ wrap_exception = functools.partial(exception_wrapper.wrap_exception,
binary='nova-api')
CONF = nova.conf.CONF
RO_SECURITY_GROUPS = ['default']
AGGREGATE_ACTION_UPDATE = 'Update'
AGGREGATE_ACTION_UPDATE_META = 'UpdateMeta'
AGGREGATE_ACTION_DELETE = 'Delete'
@ -385,13 +382,7 @@ class API(base.Base):
raise exception.SecurityGroupNotFoundForProject(
project_id=context.project_id, security_group_id=secgroup)
# Check to see if it's a nova-network or neutron type.
if isinstance(secgroup_dict['id'], int):
# This is nova-network so just return the requested name.
security_groups.append(secgroup)
else:
# The id for neutron is a uuid, so we return the id (uuid).
security_groups.append(secgroup_dict['id'])
security_groups.append(secgroup_dict['id'])
return security_groups
@ -6132,334 +6123,3 @@ class KeypairAPI(base.Base):
def get_key_pair(self, context, user_id, key_name):
"""Get a keypair by name."""
return objects.KeyPair.get_by_name(context, user_id, key_name)
class SecurityGroupAPI(base.Base, security_group_base.SecurityGroupBase):
"""Sub-set of the Compute API related to managing security groups
and security group rules
"""
# The nova security group api does not use a uuid for the id.
id_is_uuid = False
def __init__(self, **kwargs):
super(SecurityGroupAPI, self).__init__(**kwargs)
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
def validate_property(self, value, property, allowed):
"""Validate given security group property.
:param value: the value to validate, as a string or unicode
:param property: the property, either 'name' or 'description'
:param allowed: the range of characters allowed
"""
try:
val = value.strip()
except AttributeError:
msg = _("Security group %s is not a string or unicode") % property
self.raise_invalid_property(msg)
utils.check_string_length(val, name=property, min_length=1,
max_length=255)
if allowed and not re.match(allowed, val):
# Some validation to ensure that values match API spec.
# - Alphanumeric characters, spaces, dashes, and underscores.
# TODO(Daviey): LP: #813685 extend beyond group_name checking, and
# probably create a param validator that can be used elsewhere.
msg = (_("Value (%(value)s) for parameter Group%(property)s is "
"invalid. Content limited to '%(allowed)s'.") %
{'value': value, 'allowed': allowed,
'property': property.capitalize()})
self.raise_invalid_property(msg)
def ensure_default(self, context):
"""Ensure that a context has a security group.
Creates a security group for the security context if it does not
already exist.
:param context: the security context
"""
self.db.security_group_ensure_default(context)
def create_security_group(self, context, name, description):
try:
objects.Quotas.check_deltas(context, {'security_groups': 1},
context.project_id,
user_id=context.user_id)
except exception.OverQuota:
msg = _("Quota exceeded, too many security groups.")
self.raise_over_quota(msg)
LOG.info("Create Security Group %s", name)
self.ensure_default(context)
group = {'user_id': context.user_id,
'project_id': context.project_id,
'name': name,
'description': description}
try:
group_ref = self.db.security_group_create(context, group)
except exception.SecurityGroupExists:
msg = _('Security group %s already exists') % name
self.raise_group_already_exists(msg)
# NOTE(melwitt): We recheck the quota after creating the object to
# prevent users from allocating more resources than their allowed quota
# in the event of a race. This is configurable because it can be
# expensive if strict quota limits are not required in a deployment.
if CONF.quota.recheck_quota:
try:
objects.Quotas.check_deltas(context, {'security_groups': 0},
context.project_id,
user_id=context.user_id)
except exception.OverQuota:
self.db.security_group_destroy(context, group_ref['id'])
msg = _("Quota exceeded, too many security groups.")
self.raise_over_quota(msg)
return group_ref
def update_security_group(self, context, security_group,
name, description):
if security_group['name'] in RO_SECURITY_GROUPS:
msg = (_("Unable to update system group '%s'") %
security_group['name'])
self.raise_invalid_group(msg)
group = {'name': name,
'description': description}
columns_to_join = ['rules.grantee_group']
group_ref = self.db.security_group_update(context,
security_group['id'],
group,
columns_to_join=columns_to_join)
return group_ref
def get(self, context, name=None, id=None, map_exception=False):
self.ensure_default(context)
cols = ['rules']
try:
if name:
return self.db.security_group_get_by_name(context,
context.project_id,
name,
columns_to_join=cols)
elif id:
return self.db.security_group_get(context, id,
columns_to_join=cols)
except exception.NotFound as exp:
if map_exception:
msg = exp.format_message()
self.raise_not_found(msg)
else:
raise
def list(self, context, names=None, ids=None, project=None,
search_opts=None):
self.ensure_default(context)
groups = []
if names or ids:
if names:
for name in names:
groups.append(self.db.security_group_get_by_name(context,
project,
name))
if ids:
for id in ids:
groups.append(self.db.security_group_get(context, id))
elif context.is_admin:
# TODO(eglynn): support a wider set of search options than just
# all_tenants, at least include the standard filters defined for
# the EC2 DescribeSecurityGroups API for the non-admin case also
if (search_opts and 'all_tenants' in search_opts):
groups = self.db.security_group_get_all(context)
else:
groups = self.db.security_group_get_by_project(context,
project)
elif project:
groups = self.db.security_group_get_by_project(context, project)
return groups
def destroy(self, context, security_group):
if security_group['name'] in RO_SECURITY_GROUPS:
msg = _("Unable to delete system group '%s'") % \
security_group['name']
self.raise_invalid_group(msg)
if self.db.security_group_in_use(context, security_group['id']):
msg = _("Security group is still in use")
self.raise_invalid_group(msg)
LOG.info("Delete security group %s", security_group['name'])
self.db.security_group_destroy(context, security_group['id'])
def is_associated_with_server(self, security_group, instance_uuid):
"""Check if the security group is already associated
with the instance. If Yes, return True.
"""
if not security_group:
return False
instances = security_group.get('instances')
if not instances:
return False
for inst in instances:
if (instance_uuid == inst['uuid']):
return True
return False
def add_to_instance(self, context, instance, security_group_name):
"""Add security group to the instance."""
security_group = self.db.security_group_get_by_name(context,
context.project_id,
security_group_name)
instance_uuid = instance.uuid
# check if the security group is associated with the server
if self.is_associated_with_server(security_group, instance_uuid):
raise exception.SecurityGroupExistsForInstance(
security_group_id=security_group['id'],
instance_id=instance_uuid)
self.db.instance_add_security_group(context.elevated(),
instance_uuid,
security_group['id'])
if instance.host:
self.compute_rpcapi.refresh_instance_security_rules(
context, instance, instance.host)
def remove_from_instance(self, context, instance, security_group_name):
"""Remove the security group associated with the instance."""
security_group = self.db.security_group_get_by_name(context,
context.project_id,
security_group_name)
instance_uuid = instance.uuid
# check if the security group is associated with the server
if not self.is_associated_with_server(security_group, instance_uuid):
raise exception.SecurityGroupNotExistsForInstance(
security_group_id=security_group['id'],
instance_id=instance_uuid)
self.db.instance_remove_security_group(context.elevated(),
instance_uuid,
security_group['id'])
if instance.host:
self.compute_rpcapi.refresh_instance_security_rules(
context, instance, instance.host)
def get_rule(self, context, id):
self.ensure_default(context)
try:
return self.db.security_group_rule_get(context, id)
except exception.NotFound:
msg = _("Rule (%s) not found") % id
self.raise_not_found(msg)
def add_rules(self, context, id, name, vals):
"""Add security group rule(s) to security group.
Note: the Nova security group API doesn't support adding multiple
security group rules at once but the EC2 one does. Therefore,
this function is written to support both.
"""
try:
objects.Quotas.check_deltas(context,
{'security_group_rules': len(vals)},
id)
except exception.OverQuota:
msg = _("Quota exceeded, too many security group rules.")
self.raise_over_quota(msg)
msg = ("Security group %(name)s added %(protocol)s ingress "
"(%(from_port)s:%(to_port)s)")
rules = []
for v in vals:
rule = self.db.security_group_rule_create(context, v)
# NOTE(melwitt): We recheck the quota after creating the object to
# prevent users from allocating more resources than their allowed
# quota in the event of a race. This is configurable because it can
# be expensive if strict quota limits are not required in a
# deployment.
if CONF.quota.recheck_quota:
try:
objects.Quotas.check_deltas(context,
{'security_group_rules': 0},
id)
except exception.OverQuota:
self.db.security_group_rule_destroy(context, rule['id'])
msg = _("Quota exceeded, too many security group rules.")
self.raise_over_quota(msg)
rules.append(rule)
LOG.info(msg, {'name': name,
'protocol': rule.protocol,
'from_port': rule.from_port,
'to_port': rule.to_port})
self.trigger_rules_refresh(context, id=id)
return rules
def remove_rules(self, context, security_group, rule_ids):
msg = ("Security group %(name)s removed %(protocol)s ingress "
"(%(from_port)s:%(to_port)s)")
for rule_id in rule_ids:
rule = self.get_rule(context, rule_id)
LOG.info(msg, {'name': security_group['name'],
'protocol': rule.protocol,
'from_port': rule.from_port,
'to_port': rule.to_port})
self.db.security_group_rule_destroy(context, rule_id)
# NOTE(vish): we removed some rules, so refresh
self.trigger_rules_refresh(context, id=security_group['id'])
def validate_id(self, id):
try:
return int(id)
except ValueError:
msg = _("Security group id should be integer")
self.raise_invalid_property(msg)
def _refresh_instance_security_rules(self, context, instances):
for instance in instances:
if instance.host is not None:
self.compute_rpcapi.refresh_instance_security_rules(
context, instance, instance.host)
def trigger_rules_refresh(self, context, id):
"""Called when a rule is added to or removed from a security_group."""
instances = objects.InstanceList.get_by_security_group_id(context, id)
self._refresh_instance_security_rules(context, instances)
def trigger_members_refresh(self, context, group_ids):
"""Called when a security group gains a new or loses a member.
Sends an update request to each compute node for each instance for
which this is relevant.
"""
instances = objects.InstanceList.get_by_grantee_security_group_ids(
context, group_ids)
self._refresh_instance_security_rules(context, instances)
def get_instance_security_groups(self, context, instance, detailed=False):
if detailed:
return self.db.security_group_get_by_instance(context,
instance.uuid)
return [{'name': group.name} for group in instance.security_groups]

View File

@ -26,6 +26,7 @@ from nova.i18n import _
from nova.objects import security_group as security_group_obj
# TODO(stephenfin): Merge this into the only implementation that exists now
class SecurityGroupBase(object):
def parse_cidr(self, cidr):

View File

@ -324,13 +324,6 @@ class BaseTestCase(test.TestCase):
return inst
def _create_group(self):
values = {'name': 'testgroup',
'description': 'testgroup',
'user_id': self.user_id,
'project_id': self.project_id}
return db.security_group_create(self.context, values)
def _init_aggregate_with_host(self, aggr, aggr_name, zone, host):
if not aggr:
aggr = self.api.create_aggregate(self.context, aggr_name, zone)
@ -8527,14 +8520,8 @@ class ComputeAPITestCase(BaseTestCase):
self.useFixture(fixtures.SpawnIsSynchronousFixture())
self.stub_out('nova.network.api.API.get_instance_nw_info',
fake_get_nw_info)
# NOTE(mriedem): Everything in here related to the security group API
# is written for nova-network and using the database. Neutron-specific
# security group API tests are covered in
# nova.tests.unit.network.security_group.test_neutron_driver.
self.security_group_api = compute.SecurityGroupAPI()
self.compute_api = compute.API(
security_group_api=self.security_group_api)
self.compute_api = compute.API()
self.fake_image = {
'id': 'f9000000-0000-0000-0000-000000000000',
'name': 'fake_name',
@ -8766,10 +8753,14 @@ class ComputeAPITestCase(BaseTestCase):
def test_create_instance_associates_security_groups(self):
# Make sure create associates security groups.
group = self._create_group()
with mock.patch.object(self.compute_api.compute_task_api,
'schedule_and_build_instances') as mock_sbi:
(ref, resv_id) = self.compute_api.create(
group = {'id': uuids.secgroup_id, 'name': 'testgroup'}
with test.nested(
mock.patch.object(self.compute_api.compute_task_api,
'schedule_and_build_instances'),
mock.patch.object(self.compute_api.security_group_api, 'get',
return_value=group),
) as (mock_sbi, mock_secgroups):
self.compute_api.create(
self.context,
instance_type=self.default_flavor,
image_href=uuids.image_href_id,
@ -8779,18 +8770,25 @@ class ComputeAPITestCase(BaseTestCase):
reqspec = build_call[1]['request_spec'][0]
self.assertEqual(1, len(reqspec.security_groups))
self.assertEqual(group.name, reqspec.security_groups[0].name)
self.assertEqual(group['id'], reqspec.security_groups[0].uuid)
mock_secgroups.assert_called_once_with(mock.ANY, 'testgroup')
def test_create_instance_with_invalid_security_group_raises(self):
pre_build_len = len(db.instance_get_all(self.context))
self.assertRaises(exception.SecurityGroupNotFoundForProject,
self.compute_api.create,
self.context,
instance_type=self.default_flavor,
image_href=None,
security_groups=['this_is_a_fake_sec_group'])
with test.nested(
mock.patch.object(self.compute_api.security_group_api, 'get',
return_value=None),
) as (mock_secgroups, ):
self.assertRaises(exception.SecurityGroupNotFoundForProject,
self.compute_api.create,
self.context,
instance_type=self.default_flavor,
image_href=None,
security_groups=['invalid_sec_group'])
self.assertEqual(pre_build_len,
len(db.instance_get_all(self.context)))
mock_secgroups.assert_called_once_with(mock.ANY, 'invalid_sec_group')
def test_create_with_malformed_user_data(self):
# Test an instance type with malformed user data.
@ -11089,21 +11087,6 @@ class ComputeAPITestCase(BaseTestCase):
self.context, instance, CONF.host, action='unlock',
source='nova-api')
def test_add_remove_security_group(self):
instance = self._create_fake_instance_obj()
self.compute.build_and_run_instance(self.context,
instance, {}, {}, {}, block_device_mapping=[])
instance = self.compute_api.get(self.context, instance.uuid)
security_group_name = self._create_group()['name']
self.security_group_api.add_to_instance(self.context,
instance,
security_group_name)
self.security_group_api.remove_from_instance(self.context,
instance,
security_group_name)
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_diagnostics')
def test_get_diagnostics(self, mock_get):
instance = self._create_fake_instance_obj()
@ -11120,51 +11103,6 @@ class ComputeAPITestCase(BaseTestCase):
mock_get.assert_called_once_with(self.context, instance=instance)
@mock.patch.object(compute_rpcapi.ComputeAPI,
'refresh_instance_security_rules')
def test_refresh_instance_security_rules(self, mock_refresh):
inst1 = self._create_fake_instance_obj()
inst2 = self._create_fake_instance_obj({'host': None})
self.security_group_api._refresh_instance_security_rules(
self.context, [inst1, inst2])
mock_refresh.assert_called_once_with(self.context, inst1, inst1.host)
@mock.patch.object(compute_rpcapi.ComputeAPI,
'refresh_instance_security_rules')
def test_refresh_instance_security_rules_empty(self, mock_refresh):
self.security_group_api._refresh_instance_security_rules(self.context,
[])
self.assertFalse(mock_refresh.called)
@mock.patch.object(compute.SecurityGroupAPI,
'_refresh_instance_security_rules')
@mock.patch.object(objects.InstanceList,
'get_by_grantee_security_group_ids')
def test_secgroup_refresh(self, mock_get, mock_refresh):
mock_get.return_value = mock.sentinel.instances
self.security_group_api.trigger_members_refresh(mock.sentinel.ctxt,
mock.sentinel.ids)
mock_get.assert_called_once_with(mock.sentinel.ctxt, mock.sentinel.ids)
mock_refresh.assert_called_once_with(mock.sentinel.ctxt,
mock.sentinel.instances)
@mock.patch.object(compute.SecurityGroupAPI,
'_refresh_instance_security_rules')
@mock.patch.object(objects.InstanceList,
'get_by_security_group_id')
def test_secrule_refresh(self, mock_get, mock_refresh):
mock_get.return_value = mock.sentinel.instances
self.security_group_api.trigger_rules_refresh(mock.sentinel.ctxt,
mock.sentinel.id)
mock_get.assert_called_once_with(mock.sentinel.ctxt, mock.sentinel.id)
mock_refresh.assert_called_once_with(mock.sentinel.ctxt,
mock.sentinel.instances)
def _test_live_migrate(self, force=None):
instance, instance_uuid = self._run_instance()

View File

@ -6913,29 +6913,3 @@ class DiffDictTestCase(test.NoDBTestCase):
diff = compute_api._diff_dict(old, new)
self.assertEqual(diff, dict(b=['-']))
class SecurityGroupAPITest(test.NoDBTestCase):
def setUp(self):
super(SecurityGroupAPITest, self).setUp()
self.secgroup_api = compute_api.SecurityGroupAPI()
self.user_id = 'fake'
self.project_id = 'fake'
self.context = context.RequestContext(self.user_id,
self.project_id)
def test_get_instance_security_groups(self):
groups = objects.SecurityGroupList()
groups.objects = [objects.SecurityGroup(name='foo'),
objects.SecurityGroup(name='bar')]
instance = objects.Instance(security_groups=groups)
names = self.secgroup_api.get_instance_security_groups(self.context,
instance)
self.assertEqual(sorted([{'name': 'bar'}, {'name': 'foo'}], key=str),
sorted(names, key=str))
@mock.patch('nova.objects.security_group.make_secgroup_list')
def test_populate_security_groups(self, mock_msl):
r = self.secgroup_api.populate_security_groups([mock.sentinel.group])
mock_msl.assert_called_once_with([mock.sentinel.group])
self.assertEqual(r, mock_msl.return_value)