Merge "Integrate Security Groups OVO"
This commit is contained in:
commit
c507656044
@ -19,21 +19,25 @@ from neutron_lib.callbacks import exceptions
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib.callbacks import resources
|
||||
from neutron_lib import constants
|
||||
from neutron_lib import exceptions as n_exc
|
||||
from neutron_lib.utils import helpers
|
||||
from neutron_lib.utils import net
|
||||
from oslo_utils import uuidutils
|
||||
from sqlalchemy.orm import exc
|
||||
import six
|
||||
from sqlalchemy.orm import scoped_session
|
||||
|
||||
from neutron._i18n import _
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.common import constants as n_const
|
||||
from neutron.common import utils
|
||||
from neutron.db import _model_query as model_query
|
||||
from neutron.db import _resource_extend as resource_extend
|
||||
from neutron.db import _utils as db_utils
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db.models import securitygroup as sg_models
|
||||
from neutron.extensions import securitygroup as ext_sg
|
||||
from neutron.objects import base as base_obj
|
||||
from neutron.objects import securitygroup as sg_obj
|
||||
|
||||
|
||||
@resource_extend.has_resource_extenders
|
||||
@ -87,40 +91,39 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
return self.get_security_group(context, existing_def_sg_id)
|
||||
|
||||
with db_api.context_manager.writer.using(context):
|
||||
security_group_db = sg_models.SecurityGroup(id=s.get('id') or (
|
||||
uuidutils.generate_uuid()),
|
||||
description=s['description'],
|
||||
tenant_id=tenant_id,
|
||||
name=s['name'])
|
||||
context.session.add(security_group_db)
|
||||
if default_sg:
|
||||
context.session.add(sg_models.DefaultSecurityGroup(
|
||||
security_group=security_group_db,
|
||||
tenant_id=security_group_db['tenant_id']))
|
||||
sg = sg_obj.SecurityGroup(
|
||||
context, id=s.get('id') or uuidutils.generate_uuid(),
|
||||
description=s['description'], project_id=tenant_id,
|
||||
name=s['name'], is_default=default_sg)
|
||||
sg.create()
|
||||
|
||||
for ethertype in ext_sg.sg_supported_ethertypes:
|
||||
if default_sg:
|
||||
# Allow intercommunication
|
||||
ingress_rule = sg_models.SecurityGroupRule(
|
||||
id=uuidutils.generate_uuid(), tenant_id=tenant_id,
|
||||
security_group=security_group_db,
|
||||
direction='ingress',
|
||||
ethertype=ethertype,
|
||||
source_group=security_group_db)
|
||||
context.session.add(ingress_rule)
|
||||
ingress_rule = sg_obj.SecurityGroupRule(
|
||||
context, id=uuidutils.generate_uuid(),
|
||||
project_id=tenant_id, security_group_id=sg.id,
|
||||
direction='ingress', ethertype=ethertype,
|
||||
remote_group_id=sg.id)
|
||||
ingress_rule.create()
|
||||
sg.rules.append(ingress_rule)
|
||||
|
||||
egress_rule = sg_models.SecurityGroupRule(
|
||||
id=uuidutils.generate_uuid(), tenant_id=tenant_id,
|
||||
security_group=security_group_db,
|
||||
direction='egress',
|
||||
ethertype=ethertype)
|
||||
context.session.add(egress_rule)
|
||||
egress_rule = sg_obj.SecurityGroupRule(
|
||||
context, id=uuidutils.generate_uuid(),
|
||||
project_id=tenant_id, security_group_id=sg.id,
|
||||
direction='egress', ethertype=ethertype)
|
||||
egress_rule.create()
|
||||
sg.rules.append(egress_rule)
|
||||
sg.obj_reset_changes(['rules'])
|
||||
|
||||
self._registry_notify(resources.SECURITY_GROUP,
|
||||
events.PRECOMMIT_CREATE,
|
||||
exc_cls=ext_sg.SecurityGroupConflict,
|
||||
**kwargs)
|
||||
|
||||
secgroup_dict = self._make_security_group_dict(security_group_db)
|
||||
# fetch sg from db to load the sg rules with sg model.
|
||||
sg = sg_obj.SecurityGroup.get_object(context, id=sg.id)
|
||||
secgroup_dict = self._make_security_group_dict(sg.db_obj)
|
||||
|
||||
kwargs['security_group'] = secgroup_dict
|
||||
registry.notify(resources.SECURITY_GROUP, events.AFTER_CREATE, self,
|
||||
@ -136,6 +139,7 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
# so this can be done recursively. Context.tenant_id is checked
|
||||
# because all the unit tests do not explicitly set the context on
|
||||
# GETS. TODO(arosen) context handling can probably be improved here.
|
||||
filters = filters or {}
|
||||
if not default_sg and context.tenant_id:
|
||||
tenant_id = filters.get('tenant_id')
|
||||
if tenant_id:
|
||||
@ -143,20 +147,20 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
else:
|
||||
tenant_id = context.tenant_id
|
||||
self._ensure_default_security_group(context, tenant_id)
|
||||
marker_obj = db_utils.get_marker_obj(self, context, 'security_group',
|
||||
limit, marker)
|
||||
return model_query.get_collection(context,
|
||||
sg_models.SecurityGroup,
|
||||
self._make_security_group_dict,
|
||||
filters=filters, fields=fields,
|
||||
sorts=sorts,
|
||||
limit=limit, marker_obj=marker_obj,
|
||||
page_reverse=page_reverse)
|
||||
|
||||
pager = base_obj.Pager(
|
||||
sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse)
|
||||
|
||||
sg_objs = sg_obj.SecurityGroup.get_objects(
|
||||
context, _pager=pager, validate_filters=False, **filters)
|
||||
|
||||
return [self._make_security_group_dict(obj, fields) for obj in sg_objs]
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_security_groups_count(self, context, filters=None):
|
||||
return model_query.get_collection_count(
|
||||
context, sg_models.SecurityGroup, filters=filters)
|
||||
filters = filters or {}
|
||||
return sg_obj.SecurityGroup.count(
|
||||
context, validate_filters=False, **filters)
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_security_group(self, context, id, fields=None, tenant_id=None):
|
||||
@ -180,12 +184,8 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
return ret
|
||||
|
||||
def _get_security_group(self, context, id):
|
||||
try:
|
||||
query = model_query.query_with_hooks(
|
||||
context, sg_models.SecurityGroup)
|
||||
sg = query.filter(sg_models.SecurityGroup.id == id).one()
|
||||
|
||||
except exc.NoResultFound:
|
||||
sg = sg_obj.SecurityGroup.get_object(context, id=id)
|
||||
if sg is None:
|
||||
raise ext_sg.SecurityGroupNotFound(id=id)
|
||||
return sg
|
||||
|
||||
@ -224,7 +224,7 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
events.PRECOMMIT_DELETE,
|
||||
exc_cls=ext_sg.SecurityGroupInUse, id=id,
|
||||
**kwargs)
|
||||
context.session.delete(sg)
|
||||
sg.delete()
|
||||
|
||||
kwargs.pop('security_group')
|
||||
registry.notify(resources.SECURITY_GROUP, events.AFTER_DELETE,
|
||||
@ -244,13 +244,14 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
|
||||
with db_api.context_manager.writer.using(context):
|
||||
sg = self._get_security_group(context, id)
|
||||
if sg['name'] == 'default' and 'name' in s:
|
||||
if sg.name == 'default' and 'name' in s:
|
||||
raise ext_sg.SecurityGroupCannotUpdateDefault()
|
||||
self._registry_notify(
|
||||
resources.SECURITY_GROUP,
|
||||
events.PRECOMMIT_UPDATE,
|
||||
exc_cls=ext_sg.SecurityGroupConflict, **kwargs)
|
||||
sg.update(s)
|
||||
sg.update_fields(s)
|
||||
sg.update()
|
||||
sg_dict = self._make_security_group_dict(sg)
|
||||
|
||||
kwargs['security_group'] = sg_dict
|
||||
@ -349,28 +350,45 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
events.BEFORE_CREATE,
|
||||
exc_cls=ext_sg.SecurityGroupConflict, **kwargs)
|
||||
|
||||
remote_ip_prefix = rule_dict.get('remote_ip_prefix')
|
||||
if remote_ip_prefix:
|
||||
remote_ip_prefix = utils.AuthenticIPNetwork(remote_ip_prefix)
|
||||
|
||||
protocol = rule_dict.get('protocol')
|
||||
if protocol:
|
||||
# object expects strings only
|
||||
protocol = six.text_type(protocol)
|
||||
|
||||
args = {
|
||||
'id': (rule_dict.get('id') or uuidutils.generate_uuid()),
|
||||
'project_id': rule_dict['tenant_id'],
|
||||
'security_group_id': rule_dict['security_group_id'],
|
||||
'direction': rule_dict['direction'],
|
||||
'remote_group_id': rule_dict.get('remote_group_id'),
|
||||
'ethertype': rule_dict['ethertype'],
|
||||
'protocol': protocol,
|
||||
'remote_ip_prefix': remote_ip_prefix,
|
||||
'description': rule_dict.get('description'),
|
||||
}
|
||||
|
||||
port_range_min = self._safe_int(rule_dict['port_range_min'])
|
||||
if port_range_min is not None:
|
||||
args['port_range_min'] = port_range_min
|
||||
|
||||
port_range_max = self._safe_int(rule_dict['port_range_max'])
|
||||
if port_range_max is not None:
|
||||
args['port_range_max'] = port_range_max
|
||||
|
||||
with db_api.context_manager.writer.using(context):
|
||||
if validate:
|
||||
self._check_for_duplicate_rules_in_db(context,
|
||||
security_group_rule)
|
||||
db = sg_models.SecurityGroupRule(
|
||||
id=(rule_dict.get('id') or uuidutils.generate_uuid()),
|
||||
tenant_id=rule_dict['tenant_id'],
|
||||
security_group_id=rule_dict['security_group_id'],
|
||||
direction=rule_dict['direction'],
|
||||
remote_group_id=rule_dict.get('remote_group_id'),
|
||||
ethertype=rule_dict['ethertype'],
|
||||
protocol=rule_dict['protocol'],
|
||||
port_range_min=rule_dict['port_range_min'],
|
||||
port_range_max=rule_dict['port_range_max'],
|
||||
remote_ip_prefix=rule_dict.get('remote_ip_prefix'),
|
||||
description=rule_dict.get('description')
|
||||
)
|
||||
context.session.add(db)
|
||||
sg_rule = sg_obj.SecurityGroupRule(context, **args)
|
||||
sg_rule.create()
|
||||
self._registry_notify(resources.SECURITY_GROUP_RULE,
|
||||
events.PRECOMMIT_CREATE,
|
||||
exc_cls=ext_sg.SecurityGroupConflict, **kwargs)
|
||||
return self._make_security_group_rule_dict(db)
|
||||
return self._make_security_group_rule_dict(sg_rule.db_obj)
|
||||
|
||||
def _get_ip_proto_number(self, protocol):
|
||||
if protocol is None:
|
||||
@ -394,6 +412,15 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
protocol]
|
||||
return [protocol, protocol]
|
||||
|
||||
def _safe_int(self, port_range):
|
||||
if port_range is None:
|
||||
return
|
||||
try:
|
||||
return int(port_range)
|
||||
except (ValueError, TypeError):
|
||||
msg = "port range must be an integer"
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
|
||||
def _validate_port_range(self, rule):
|
||||
"""Check that port_range is valid."""
|
||||
if (rule['port_range_min'] is None and
|
||||
@ -574,6 +601,8 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
raise ext_sg.SecurityGroupRuleExists(rule_id=rule_id)
|
||||
|
||||
def _validate_duplicate_ip_prefix(self, ip_prefix, other_ip_prefix):
|
||||
if other_ip_prefix is not None:
|
||||
other_ip_prefix = str(other_ip_prefix)
|
||||
all_address = ['0.0.0.0/0', '::/0', None]
|
||||
if ip_prefix == other_ip_prefix:
|
||||
return True
|
||||
@ -603,21 +632,23 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
def get_security_group_rules(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None, marker=None,
|
||||
page_reverse=False):
|
||||
marker_obj = db_utils.get_marker_obj(self, context,
|
||||
'security_group_rule',
|
||||
limit, marker)
|
||||
return model_query.get_collection(context,
|
||||
sg_models.SecurityGroupRule,
|
||||
self._make_security_group_rule_dict,
|
||||
filters=filters, fields=fields,
|
||||
sorts=sorts,
|
||||
limit=limit, marker_obj=marker_obj,
|
||||
page_reverse=page_reverse)
|
||||
filters = filters or {}
|
||||
pager = base_obj.Pager(
|
||||
sorts=sorts, marker=marker, limit=limit, page_reverse=page_reverse)
|
||||
|
||||
rule_objs = sg_obj.SecurityGroupRule.get_objects(
|
||||
context, _pager=pager, validate_filters=False, **filters
|
||||
)
|
||||
return [
|
||||
self._make_security_group_rule_dict(obj, fields)
|
||||
for obj in rule_objs
|
||||
]
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_security_group_rules_count(self, context, filters=None):
|
||||
return model_query.get_collection_count(
|
||||
context, sg_models.SecurityGroupRule, filters=filters)
|
||||
filters = filters or {}
|
||||
return sg_obj.SecurityGroupRule.count(
|
||||
context, validate_filters=False, **filters)
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_security_group_rule(self, context, id, fields=None):
|
||||
@ -625,11 +656,8 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
return self._make_security_group_rule_dict(security_group_rule, fields)
|
||||
|
||||
def _get_security_group_rule(self, context, id):
|
||||
try:
|
||||
query = model_query.query_with_hooks(
|
||||
context, sg_models.SecurityGroupRule)
|
||||
sgr = query.filter(sg_models.SecurityGroupRule.id == id).one()
|
||||
except exc.NoResultFound:
|
||||
sgr = sg_obj.SecurityGroupRule.get_object(context, id=id)
|
||||
if sgr is None:
|
||||
raise ext_sg.SecurityGroupRuleNotFound(id=id)
|
||||
return sgr
|
||||
|
||||
@ -644,24 +672,15 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
exc_cls=ext_sg.SecurityGroupRuleInUse, **kwargs)
|
||||
|
||||
with db_api.context_manager.writer.using(context):
|
||||
query = model_query.query_with_hooks(
|
||||
context, sg_models.SecurityGroupRule).filter(
|
||||
sg_models.SecurityGroupRule.id == id)
|
||||
|
||||
self._registry_notify(resources.SECURITY_GROUP_RULE,
|
||||
events.PRECOMMIT_DELETE,
|
||||
exc_cls=ext_sg.SecurityGroupRuleInUse, id=id,
|
||||
**kwargs)
|
||||
|
||||
try:
|
||||
sg_rule = query.one()
|
||||
# As there is a filter on a primary key it is not possible for
|
||||
# MultipleResultsFound to be raised
|
||||
context.session.delete(sg_rule)
|
||||
except exc.NoResultFound:
|
||||
raise ext_sg.SecurityGroupRuleNotFound(id=id)
|
||||
sgr = self._get_security_group_rule(context, id)
|
||||
sgr.delete()
|
||||
|
||||
kwargs['security_group_id'] = sg_rule['security_group_id']
|
||||
kwargs['security_group_id'] = sgr['security_group_id']
|
||||
|
||||
registry.notify(
|
||||
resources.SECURITY_GROUP_RULE, events.AFTER_DELETE, self,
|
||||
@ -690,13 +709,12 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
list(security_group_ids) or [])
|
||||
|
||||
def _get_default_sg_id(self, context, tenant_id):
|
||||
try:
|
||||
query = model_query.query_with_hooks(
|
||||
context, sg_models.DefaultSecurityGroup)
|
||||
default_group = query.filter_by(tenant_id=tenant_id).one()
|
||||
return default_group['security_group_id']
|
||||
except exc.NoResultFound:
|
||||
pass
|
||||
default_group = sg_obj.DefaultSecurityGroup.get_object(
|
||||
context,
|
||||
project_id=tenant_id,
|
||||
)
|
||||
if default_group:
|
||||
return default_group.security_group_id
|
||||
|
||||
@registry.receives(resources.PORT, [events.BEFORE_CREATE])
|
||||
@registry.receives(resources.NETWORK, [events.BEFORE_CREATE])
|
||||
@ -710,9 +728,10 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
||||
|
||||
:returns: the default security group id for given tenant.
|
||||
"""
|
||||
existing = self._get_default_sg_id(context, tenant_id)
|
||||
if existing is not None:
|
||||
return existing
|
||||
default_group_id = self._get_default_sg_id(context, tenant_id)
|
||||
if default_group_id:
|
||||
return default_group_id
|
||||
|
||||
security_group = {
|
||||
'security_group':
|
||||
{'name': 'default',
|
||||
|
@ -51,7 +51,7 @@ class SecurityGroup(base.NeutronDbObject):
|
||||
with db_api.autonested_transaction(self.obj_context.session):
|
||||
super(SecurityGroup, self).create()
|
||||
if is_default:
|
||||
default_group = _DefaultSecurityGroup(
|
||||
default_group = DefaultSecurityGroup(
|
||||
self.obj_context,
|
||||
project_id=self.project_id,
|
||||
security_group_id=self.id)
|
||||
@ -66,7 +66,7 @@ class SecurityGroup(base.NeutronDbObject):
|
||||
|
||||
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class _DefaultSecurityGroup(base.NeutronDbObject):
|
||||
class DefaultSecurityGroup(base.NeutronDbObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
|
@ -27,15 +27,31 @@ from neutron.extensions import securitygroup
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
|
||||
FAKE_SECGROUP = {'security_group': {"tenant_id": 'fake', 'description':
|
||||
'fake', 'name': 'fake'}}
|
||||
FAKE_SECGROUP = {
|
||||
'security_group': {
|
||||
"tenant_id": 'fake',
|
||||
'description': 'fake',
|
||||
'name': 'fake'
|
||||
}
|
||||
}
|
||||
|
||||
FAKE_SECGROUP_RULE = {'security_group_rule': {"tenant_id": 'fake',
|
||||
'description': 'fake', 'name': 'fake', 'port_range_min':
|
||||
'21', 'protocol': 'tcp', 'port_range_max': '23',
|
||||
'remote_ip_prefix': '10.0.0.1', 'ethertype': 'IPv4',
|
||||
'remote_group_id': None, 'security_group_id': 'None',
|
||||
'direction': 'ingress'}}
|
||||
FAKE_SECGROUP_RULE = {
|
||||
'security_group_rule': {
|
||||
"tenant_id": 'fake',
|
||||
'description': 'fake',
|
||||
'name': 'fake',
|
||||
'port_range_min': '21',
|
||||
'protocol': 'tcp',
|
||||
'port_range_max': '23',
|
||||
'remote_ip_prefix': '10.0.0.1',
|
||||
'ethertype': 'IPv4',
|
||||
'remote_group_id': None,
|
||||
'security_group_id': 'None',
|
||||
'direction': 'ingress'
|
||||
}
|
||||
}
|
||||
|
||||
DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
|
||||
|
||||
|
||||
def fake_callback(resource, event, *args, **kwargs):
|
||||
@ -51,6 +67,7 @@ class SecurityGroupDbMixinTestCase(testlib_api.SqlTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SecurityGroupDbMixinTestCase, self).setUp()
|
||||
self.setup_coreplugin(core_plugin=DB_PLUGIN_KLASS)
|
||||
self.ctx = context.get_admin_context()
|
||||
self.mixin = SecurityGroupDbMixinImpl()
|
||||
|
||||
|
@ -26,11 +26,11 @@ from neutron.tests import base as test_base
|
||||
# corresponding version bump in the affected objects. Please keep the list in
|
||||
# alphabetic order.
|
||||
object_data = {
|
||||
'_DefaultSecurityGroup': '1.0-971520cb2e0ec06d747885a0cf78347f',
|
||||
'AddressScope': '1.0-dd0dfdb67775892d3adc090e28e43bd8',
|
||||
'Agent': '1.0-7106cb40117a8d1f042545796ed8787d',
|
||||
'AllowedAddressPair': '1.0-9f9186b6f952fbf31d257b0458b852c0',
|
||||
'AutoAllocatedTopology': '1.0-74642e58c53bf3610dc224c59f81b242',
|
||||
'DefaultSecurityGroup': '1.0-971520cb2e0ec06d747885a0cf78347f',
|
||||
'DistributedPortBinding': '1.0-39c0d17b281991dcb66716fee5a8bef2',
|
||||
'DNSNameServer': '1.0-bf87a85327e2d812d1666ede99d9918b',
|
||||
'ExternalNetwork': '1.0-53d885e033cb931f9bb3bdd6bbe3f0ce',
|
||||
|
@ -41,7 +41,7 @@ class SecurityGroupDbObjTestCase(test_base.BaseDbObjectTestCase,
|
||||
sg_obj.is_default = True
|
||||
sg_obj.create()
|
||||
|
||||
default_sg_obj = securitygroup._DefaultSecurityGroup.get_object(
|
||||
default_sg_obj = securitygroup.DefaultSecurityGroup.get_object(
|
||||
self.context,
|
||||
project_id=sg_obj.project_id,
|
||||
security_group_id=sg_obj.id)
|
||||
@ -60,7 +60,7 @@ class SecurityGroupDbObjTestCase(test_base.BaseDbObjectTestCase,
|
||||
sg_obj.is_default = False
|
||||
sg_obj.create()
|
||||
|
||||
default_sg_obj = securitygroup._DefaultSecurityGroup.get_object(
|
||||
default_sg_obj = securitygroup.DefaultSecurityGroup.get_object(
|
||||
self.context,
|
||||
project_id=sg_obj.project_id,
|
||||
security_group_id=sg_obj.id)
|
||||
@ -100,13 +100,13 @@ class SecurityGroupDbObjTestCase(test_base.BaseDbObjectTestCase,
|
||||
|
||||
class DefaultSecurityGroupIfaceObjTestCase(test_base.BaseObjectIfaceTestCase):
|
||||
|
||||
_test_class = securitygroup._DefaultSecurityGroup
|
||||
_test_class = securitygroup.DefaultSecurityGroup
|
||||
|
||||
|
||||
class DefaultSecurityGroupDbObjTestCase(test_base.BaseDbObjectTestCase,
|
||||
testlib_api.SqlTestCase):
|
||||
|
||||
_test_class = securitygroup._DefaultSecurityGroup
|
||||
_test_class = securitygroup.DefaultSecurityGroup
|
||||
|
||||
def setUp(self):
|
||||
super(DefaultSecurityGroupDbObjTestCase, self).setUp()
|
||||
|
Loading…
Reference in New Issue
Block a user