Added support for Security Groups via a new extension.
- Added migrate changes for new database artifacts. - Added models for Security Groups and Security Group Rules. - Definition and addition of Controllers for API. - Views for Security Groups and Security Group rules. - Extended taskmanager to use newly created Security Group on instance creation, and to clean it up on deletion if extension is enabled - Added new flag to conf to enable "reddwarf_security_groups_support" - Integration tests. Implements blueprint: security-groups Change-Id: I44053359c12e53a9e549bd334e628ecd249d8ba0
This commit is contained in:
parent
01a7590b58
commit
8d89bae2ad
@ -81,6 +81,12 @@ agent_call_high_timeout = 150
|
||||
# Reboot time out for instances
|
||||
reboot_time_out = 60
|
||||
|
||||
# Reddwarf Security Groups for Instances
|
||||
reddwarf_security_groups_support = True
|
||||
reddwarf_security_group_rule_protocol = tcp
|
||||
reddwarf_security_group_rule_port = 3306
|
||||
|
||||
|
||||
# ============ notifer queue kombu connection options ========================
|
||||
|
||||
notifier_queue_hostname = localhost
|
||||
|
@ -108,6 +108,9 @@ common_opts = [
|
||||
cfg.IntOpt('http_put_rate', default=200),
|
||||
cfg.BoolOpt('hostname_require_ipv4', default=True,
|
||||
help="Require user hostnames to be IPv4 addresses."),
|
||||
cfg.BoolOpt('reddwarf_security_groups_support', default=True),
|
||||
cfg.StrOpt('reddwarf_security_group_rule_protocol', default='tcp'),
|
||||
cfg.IntOpt('reddwarf_security_group_rule_port', default=3306),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,7 +23,6 @@ from reddwarf.openstack.common.gettextutils import _
|
||||
|
||||
from webob import exc
|
||||
|
||||
|
||||
ClientConnectionError = openstack_exception.ClientConnectionError
|
||||
ProcessExecutionError = processutils.ProcessExecutionError
|
||||
DatabaseMigrationError = openstack_exception.DatabaseMigrationError
|
||||
@ -230,3 +229,23 @@ class BackupCreationError(ReddwarfError):
|
||||
|
||||
class BackupUpdateError(ReddwarfError):
|
||||
message = _("Unable to update Backup table in db")
|
||||
|
||||
|
||||
class SecurityGroupCreationError(ReddwarfError):
|
||||
|
||||
message = _("Failed to create Security Group.")
|
||||
|
||||
|
||||
class SecurityGroupDeletionError(ReddwarfError):
|
||||
|
||||
message = _("Failed to delete Security Group.")
|
||||
|
||||
|
||||
class SecurityGroupRuleCreationError(ReddwarfError):
|
||||
|
||||
message = _("Failed to create Security Group Rule.")
|
||||
|
||||
|
||||
class SecurityGroupRuleDeletionError(ReddwarfError):
|
||||
|
||||
message = _("Failed to delete Security Group Rule.")
|
||||
|
@ -86,6 +86,12 @@ CUSTOM_SERIALIZER_METADATA = {
|
||||
'database': {'name': ''},
|
||||
'user': {'name': '', 'password': ''},
|
||||
'account': {'id': ''},
|
||||
'security_group': {'id': '', 'name': '', 'description': '', 'user': '',
|
||||
'tenant_id': ''},
|
||||
'security_group_rule': {'id': '', 'group_id': '', 'protocol': '',
|
||||
'from_port': '', 'to_port': '', 'cidr': ''},
|
||||
'security_group_instance_association': {'id': '', 'security_group_id': '',
|
||||
'instance_id': ''},
|
||||
# mgmt/host
|
||||
'host': {'instanceCount': '', 'name': '', 'usedRAM': '', 'totalRAM': '',
|
||||
'percentUsed': ''},
|
||||
@ -104,6 +110,7 @@ CUSTOM_SERIALIZER_METADATA = {
|
||||
'version': '', 'vmRss': '', 'fdSize': ''},
|
||||
#mgmt/instance/root
|
||||
'root_history': {'enabled': '', 'id': '', 'user': ''},
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -373,7 +380,7 @@ class Controller(object):
|
||||
exception.ModelNotFoundError,
|
||||
exception.UserNotFound,
|
||||
exception.DatabaseNotFound,
|
||||
exception.QuotaResourceUnknown
|
||||
exception.QuotaResourceUnknown,
|
||||
],
|
||||
webob.exc.HTTPConflict: [],
|
||||
webob.exc.HTTPRequestEntityTooLarge: [
|
||||
|
@ -29,7 +29,10 @@ class DatabaseModelBase(models.ModelBase):
|
||||
|
||||
@classmethod
|
||||
def create(cls, **values):
|
||||
values['id'] = utils.generate_uuid()
|
||||
if 'id' not in values:
|
||||
values['id'] = utils.generate_uuid()
|
||||
if hasattr(cls, 'deleted') and 'deleted' not in values:
|
||||
values['deleted'] = False
|
||||
values['created'] = utils.utcnow()
|
||||
instance = cls(**values).save()
|
||||
if not instance.is_valid():
|
||||
@ -40,6 +43,10 @@ class DatabaseModelBase(models.ModelBase):
|
||||
def db_api(self):
|
||||
return get_db_api()
|
||||
|
||||
@property
|
||||
def preserve_on_delete(self):
|
||||
return hasattr(self, 'deleted') and hasattr(self, 'deleted_at')
|
||||
|
||||
def save(self):
|
||||
if not self.is_valid():
|
||||
raise exception.InvalidModelError(errors=self.errors)
|
||||
@ -52,7 +59,13 @@ class DatabaseModelBase(models.ModelBase):
|
||||
self['updated'] = utils.utcnow()
|
||||
LOG.debug(_("Deleting %s: %s") %
|
||||
(self.__class__.__name__, self.__dict__))
|
||||
return self.db_api.delete(self)
|
||||
|
||||
if self.preserve_on_delete:
|
||||
self['deleted_at'] = utils.utcnow()
|
||||
self['deleted'] = True
|
||||
return self.db_api.save(self)
|
||||
else:
|
||||
return self.db_api.delete(self)
|
||||
|
||||
def update(self, **values):
|
||||
for key in values:
|
||||
|
@ -46,6 +46,13 @@ def map(engine, models):
|
||||
Table('reservations', meta, autoload=True))
|
||||
orm.mapper(models['backups'],
|
||||
Table('backups', meta, autoload=True))
|
||||
orm.mapper(models['security_group'],
|
||||
Table('security_groups', meta, autoload=True))
|
||||
orm.mapper(models['security_group_rule'],
|
||||
Table('security_group_rules', meta, autoload=True))
|
||||
orm.mapper(models['security_group_instance_association'],
|
||||
Table('security_group_instance_associations', meta,
|
||||
autoload=True))
|
||||
|
||||
|
||||
def mapping_exists(model):
|
||||
|
@ -0,0 +1,98 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.schema import Column
|
||||
from sqlalchemy.schema import MetaData
|
||||
|
||||
from reddwarf.db.sqlalchemy.migrate_repo.schema import create_tables
|
||||
from reddwarf.db.sqlalchemy.migrate_repo.schema import drop_tables
|
||||
from reddwarf.db.sqlalchemy.migrate_repo.schema import Boolean
|
||||
from reddwarf.db.sqlalchemy.migrate_repo.schema import Integer
|
||||
from reddwarf.db.sqlalchemy.migrate_repo.schema import String
|
||||
from reddwarf.db.sqlalchemy.migrate_repo.schema import DateTime
|
||||
from reddwarf.db.sqlalchemy.migrate_repo.schema import Table
|
||||
|
||||
|
||||
meta = MetaData()
|
||||
|
||||
security_groups = Table(
|
||||
'security_groups',
|
||||
meta,
|
||||
Column('id', String(length=36), primary_key=True, nullable=False),
|
||||
Column('name', String(length=255)),
|
||||
Column('description', String(length=255)),
|
||||
Column('user', String(length=255)),
|
||||
Column('tenant_id', String(length=255)),
|
||||
Column('created', DateTime()),
|
||||
Column('updated', DateTime()),
|
||||
Column('deleted', Boolean(), default=0),
|
||||
Column('deleted_at', DateTime()),
|
||||
)
|
||||
|
||||
security_group_instance_associations = Table(
|
||||
'security_group_instance_associations',
|
||||
meta,
|
||||
Column('id', String(length=36), primary_key=True, nullable=False),
|
||||
Column('security_group_id', String(length=36),
|
||||
ForeignKey('security_groups.id', ondelete="CASCADE",
|
||||
onupdate="CASCADE")),
|
||||
Column('instance_id', String(length=36),
|
||||
ForeignKey('instances.id', ondelete="CASCADE",
|
||||
onupdate="CASCADE")),
|
||||
Column('created', DateTime()),
|
||||
Column('updated', DateTime()),
|
||||
Column('deleted', Boolean(), default=0),
|
||||
Column('deleted_at', DateTime()),
|
||||
)
|
||||
|
||||
security_group_rules = Table(
|
||||
'security_group_rules',
|
||||
meta,
|
||||
Column('id', String(length=36), primary_key=True, nullable=False),
|
||||
Column('group_id', String(length=36),
|
||||
ForeignKey('security_groups.id', ondelete="CASCADE",
|
||||
onupdate="CASCADE")),
|
||||
Column('parent_group_id', String(length=36),
|
||||
ForeignKey('security_groups.id', ondelete="CASCADE",
|
||||
onupdate="CASCADE")),
|
||||
Column('protocol', String(length=255)),
|
||||
Column('from_port', Integer()),
|
||||
Column('to_port', Integer()),
|
||||
Column('cidr', String(length=255)),
|
||||
Column('created', DateTime()),
|
||||
Column('updated', DateTime()),
|
||||
Column('deleted', Boolean(), default=0),
|
||||
Column('deleted_at', DateTime()),
|
||||
)
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
instances = Table(
|
||||
'instances',
|
||||
meta,
|
||||
autoload=True,
|
||||
)
|
||||
create_tables([security_groups, security_group_rules,
|
||||
security_group_instance_associations])
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
drop_tables([security_group_instance_associations,
|
||||
security_group_rules, security_groups])
|
@ -47,6 +47,7 @@ def configure_db(options, models_mapper=None):
|
||||
from reddwarf.guestagent import models as agent_models
|
||||
from reddwarf.quota import models as quota_models
|
||||
from reddwarf.backup import models as backup_models
|
||||
from reddwarf.extensions.security_group import models as secgrp_models
|
||||
|
||||
model_modules = [
|
||||
base_models,
|
||||
@ -55,6 +56,7 @@ def configure_db(options, models_mapper=None):
|
||||
agent_models,
|
||||
quota_models,
|
||||
backup_models,
|
||||
secgrp_models,
|
||||
]
|
||||
|
||||
models = {}
|
||||
|
73
reddwarf/extensions/security_group.py
Normal file
73
reddwarf/extensions/security_group.py
Normal file
@ -0,0 +1,73 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
from reddwarf.openstack.common import log as logging
|
||||
|
||||
from reddwarf.common import extensions
|
||||
from reddwarf.common import wsgi
|
||||
from reddwarf.common import cfg
|
||||
from reddwarf.extensions.security_group import service
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
# The Extensions module from openstack common expects the classname of the
|
||||
# extension to be loaded to be the exact same as the filename, except with
|
||||
# a capital first letter. That's the reason this class has such a funky name.
|
||||
class Security_group(extensions.ExtensionsDescriptor):
|
||||
|
||||
def get_name(self):
|
||||
return "SecurityGroup"
|
||||
|
||||
def get_description(self):
|
||||
return "Security Group related operations such as list \
|
||||
security groups and manage security group rules."
|
||||
|
||||
def get_alias(self):
|
||||
return "SecurityGroup"
|
||||
|
||||
def get_namespace(self):
|
||||
return "http://TBD"
|
||||
|
||||
def get_updated(self):
|
||||
return "2012-02-26T17:25:27-08:00"
|
||||
|
||||
def get_resources(self):
|
||||
resources = []
|
||||
serializer = wsgi.ReddwarfResponseSerializer(
|
||||
body_serializers={'application/xml':
|
||||
wsgi.ReddwarfXMLDictSerializer()})
|
||||
|
||||
if CONF.reddwarf_security_groups_support:
|
||||
security_groups = extensions.ResourceExtension(
|
||||
'{tenant_id}/security_groups',
|
||||
service.SecurityGroupController(),
|
||||
deserializer=wsgi.ReddwarfRequestDeserializer(),
|
||||
serializer=serializer)
|
||||
resources.append(security_groups)
|
||||
|
||||
security_group_rules = extensions.ResourceExtension(
|
||||
'{tenant_id}/security_group_rules',
|
||||
service.SecurityGroupRuleController(),
|
||||
deserializer=wsgi.ReddwarfRequestDeserializer(),
|
||||
serializer=serializer)
|
||||
resources.append(security_group_rules)
|
||||
|
||||
return resources
|
17
reddwarf/extensions/security_group/__init__.py
Normal file
17
reddwarf/extensions/security_group/__init__.py
Normal file
@ -0,0 +1,17 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
264
reddwarf/extensions/security_group/models.py
Normal file
264
reddwarf/extensions/security_group/models.py
Normal file
@ -0,0 +1,264 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""
|
||||
Model classes for Security Groups and Security Group Rules on instances.
|
||||
"""
|
||||
import reddwarf.common.remote
|
||||
from reddwarf.common import cfg
|
||||
from reddwarf.common import exception
|
||||
from reddwarf.db.models import DatabaseModelBase
|
||||
from reddwarf.common.models import NovaRemoteModelBase
|
||||
from reddwarf.openstack.common import log as logging
|
||||
from reddwarf.openstack.common.gettextutils import _
|
||||
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def persisted_models():
|
||||
return {
|
||||
'security_group': SecurityGroup,
|
||||
'security_group_rule': SecurityGroupRule,
|
||||
'security_group_instance_association':
|
||||
SecurityGroupInstanceAssociation,
|
||||
}
|
||||
|
||||
|
||||
class SecurityGroup(DatabaseModelBase):
|
||||
_data_fields = ['id', 'name', 'description', 'user', 'tenant_id',
|
||||
'created', 'updated', 'deleted', 'deleted_at']
|
||||
|
||||
@classmethod
|
||||
def create_sec_group(cls, name, description, context):
|
||||
try:
|
||||
remote_sec_group = RemoteSecurityGroup.create(name,
|
||||
description,
|
||||
context)
|
||||
|
||||
if not remote_sec_group:
|
||||
raise exception.SecurityGroupCreationError(
|
||||
"Failed to create Security Group")
|
||||
else:
|
||||
return cls.create(
|
||||
id=remote_sec_group.data()['id'],
|
||||
name=name,
|
||||
description=description,
|
||||
user=context.user,
|
||||
tenant_id=context.tenant)
|
||||
|
||||
except exception.SecurityGroupCreationError, e:
|
||||
LOG.exception("Failed to create remote security group")
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
def create_for_instance(cls, instance_id, context):
|
||||
# Create a new security group
|
||||
name = _("SecGroup_%s") % instance_id
|
||||
description = \
|
||||
_("Default Security Group For DBaaS Instance <%s>") % instance_id
|
||||
sec_group = cls.create_sec_group(name, description, context)
|
||||
|
||||
# Currently this locked down by default, since we don't create any
|
||||
# default security group rules for the security group.
|
||||
|
||||
# Create security group instance association
|
||||
SecurityGroupInstanceAssociation.create(
|
||||
security_group_id=sec_group["id"],
|
||||
instance_id=instance_id)
|
||||
|
||||
return sec_group
|
||||
|
||||
@classmethod
|
||||
def get_security_group_by_id_or_instance_id(self, id, tenant_id):
|
||||
try:
|
||||
return SecurityGroup.find_by(id=id,
|
||||
tenant_id=tenant_id,
|
||||
deleted=False)
|
||||
except exception.ModelNotFoundError:
|
||||
return SecurityGroupInstanceAssociation.\
|
||||
get_security_group_by_instance_id(id)
|
||||
|
||||
def get_rules(self):
|
||||
return SecurityGroupRule.find_all(group_id=self.id,
|
||||
deleted=False)
|
||||
|
||||
def delete(self, context):
|
||||
try:
|
||||
sec_group_rules = self.get_rules()
|
||||
if sec_group_rules:
|
||||
for rule in sec_group_rules:
|
||||
rule.delete(context)
|
||||
|
||||
RemoteSecurityGroup.delete(self.id, context)
|
||||
super(SecurityGroup, self).delete()
|
||||
|
||||
except exception.ReddwarfError:
|
||||
LOG.exception('Failed to delete security group')
|
||||
raise exception.ReddwarfError("Failed to delete Security Group")
|
||||
|
||||
@classmethod
|
||||
def delete_for_instance(cls, instance_id, context):
|
||||
association = SecurityGroupInstanceAssociation.find_by(
|
||||
instance_id=instance_id,
|
||||
deleted=False)
|
||||
if association:
|
||||
sec_group = association.get_security_group()
|
||||
sec_group.delete(context)
|
||||
association.delete()
|
||||
|
||||
|
||||
class SecurityGroupRule(DatabaseModelBase):
|
||||
_data_fields = ['id', 'parent_group_id', 'protocol', 'from_port',
|
||||
'to_port', 'cidr', 'group_id', 'created', 'updated',
|
||||
'deleted', 'deleted_at']
|
||||
|
||||
@classmethod
|
||||
def create_sec_group_rule(cls, sec_group, protocol, from_port,
|
||||
to_port, cidr, context):
|
||||
try:
|
||||
remote_rule_id = RemoteSecurityGroup.add_rule(
|
||||
sec_group_id=sec_group['id'],
|
||||
protocol=protocol,
|
||||
from_port=from_port,
|
||||
to_port=to_port,
|
||||
cidr=cidr,
|
||||
context=context)
|
||||
|
||||
if not remote_rule_id:
|
||||
raise exception.SecurityGroupRuleCreationError(
|
||||
"Failed to create Security Group Rule")
|
||||
else:
|
||||
# Create db record
|
||||
return cls.create(
|
||||
id=remote_rule_id,
|
||||
protocol=protocol,
|
||||
from_port=from_port,
|
||||
to_port=to_port,
|
||||
cidr=cidr,
|
||||
group_id=sec_group['id'])
|
||||
|
||||
except exception.SecurityGroupRuleCreationError, e:
|
||||
LOG.exception("Failed to create remote security group")
|
||||
raise e
|
||||
|
||||
def get_security_group(self, tenant_id):
|
||||
return SecurityGroup.find_by(id=self.group_id,
|
||||
tenant_id=tenant_id,
|
||||
deleted=False)
|
||||
|
||||
def delete(self, context):
|
||||
try:
|
||||
# Delete Remote Security Group Rule
|
||||
RemoteSecurityGroup.delete_rule(self.id, context)
|
||||
super(SecurityGroupRule, self).delete()
|
||||
except exception.ReddwarfError:
|
||||
LOG.exception('Failed to delete security group')
|
||||
raise exception.SecurityGroupRuleDeletionError(
|
||||
"Failed to delete Security Group")
|
||||
|
||||
|
||||
class SecurityGroupInstanceAssociation(DatabaseModelBase):
|
||||
_data_fields = ['id', 'security_group_id', 'instance_id',
|
||||
'created', 'updated', 'deleted', 'deleted_at']
|
||||
|
||||
def get_security_group(self):
|
||||
return SecurityGroup.find_by(id=self.security_group_id,
|
||||
deleted=False)
|
||||
|
||||
@classmethod
|
||||
def get_security_group_by_instance_id(cls, id):
|
||||
association = SecurityGroupInstanceAssociation.find_by(
|
||||
instance_id=id,
|
||||
deleted=False)
|
||||
return association.get_security_group()
|
||||
|
||||
|
||||
class RemoteSecurityGroup(NovaRemoteModelBase):
|
||||
|
||||
_data_fields = ['id', 'name', 'description', 'rules']
|
||||
|
||||
def __init__(self, security_group=None, id=None, context=None):
|
||||
if id is None and security_group is None:
|
||||
msg = "Security Group does not have id defined!"
|
||||
raise exception.InvalidModelError(msg)
|
||||
elif security_group is None:
|
||||
try:
|
||||
client = reddwarf.common.remote.create_nova_client(context)
|
||||
self._data_object = client.security_groups.get(id)
|
||||
except nova_exceptions.NotFound, e:
|
||||
raise exception.NotFound(id=id)
|
||||
except nova_exceptions.ClientException, e:
|
||||
raise exception.ReddwarfError(str(e))
|
||||
else:
|
||||
self._data_object = security_group
|
||||
|
||||
@classmethod
|
||||
def create(cls, name, description, context):
|
||||
"""Creates a new Security Group"""
|
||||
client = reddwarf.common.remote.create_nova_client(context)
|
||||
try:
|
||||
sec_group = client.security_groups.create(name=name,
|
||||
description=description)
|
||||
except nova_exceptions.ClientException, e:
|
||||
LOG.exception('Failed to create remote security group')
|
||||
raise exception.SecurityGroupCreationError(str(e))
|
||||
|
||||
return RemoteSecurityGroup(security_group=sec_group)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, sec_group_id, context):
|
||||
client = reddwarf.common.remote.create_nova_client(context)
|
||||
|
||||
try:
|
||||
client.security_groups.delete(sec_group_id)
|
||||
except nova_exceptions.ClientException, e:
|
||||
LOG.exception('Failed to delete remote security group')
|
||||
raise exception.SecurityGroupDeletionError(str(e))
|
||||
|
||||
@classmethod
|
||||
def add_rule(cls, sec_group_id, protocol, from_port,
|
||||
to_port, cidr, context):
|
||||
|
||||
client = reddwarf.common.remote.create_nova_client(context)
|
||||
|
||||
try:
|
||||
sec_group_rule = client.security_group_rules.create(
|
||||
parent_group_id=sec_group_id,
|
||||
ip_protocol=protocol,
|
||||
from_port=from_port,
|
||||
to_port=to_port,
|
||||
cidr=cidr)
|
||||
|
||||
return sec_group_rule.id
|
||||
except nova_exceptions.ClientException, e:
|
||||
LOG.exception('Failed to add rule to remote security group')
|
||||
raise exception.SecurityGroupRuleCreationError(str(e))
|
||||
|
||||
@classmethod
|
||||
def delete_rule(cls, sec_group_rule_id, context):
|
||||
client = reddwarf.common.remote.create_nova_client(context)
|
||||
|
||||
try:
|
||||
client.security_group_rules.delete(sec_group_rule_id)
|
||||
|
||||
except nova_exceptions.ClientException, e:
|
||||
LOG.exception('Failed to delete rule to remote security group')
|
||||
raise exception.SecurityGroupRuleDeletionError(str(e))
|
117
reddwarf/extensions/security_group/service.py
Normal file
117
reddwarf/extensions/security_group/service.py
Normal file
@ -0,0 +1,117 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
|
||||
from reddwarf.common import exception
|
||||
from reddwarf.common import wsgi
|
||||
from reddwarf.common import cfg
|
||||
from reddwarf.extensions.security_group import models
|
||||
from reddwarf.extensions.security_group import views
|
||||
from reddwarf.openstack.common import log as logging
|
||||
from reddwarf.openstack.common.gettextutils import _
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SecurityGroupController(wsgi.Controller):
|
||||
"""Controller for security groups functionality"""
|
||||
|
||||
def index(self, req, tenant_id):
|
||||
"""Return all security groups tied to a particular tenant_id."""
|
||||
LOG.debug("Index() called with %s" % (tenant_id))
|
||||
|
||||
sec_groups = models.SecurityGroup().find_all(tenant_id=tenant_id,
|
||||
deleted=False)
|
||||
|
||||
# Construct the mapping from Security Groups to Security Group Rules
|
||||
rules_map = dict([(g.id, g.get_rules()) for g in sec_groups])
|
||||
|
||||
return wsgi.Result(
|
||||
views.SecurityGroupsView(sec_groups,
|
||||
rules_map,
|
||||
req, tenant_id).list(), 200)
|
||||
|
||||
def show(self, req, tenant_id, id):
|
||||
"""Return a single security group."""
|
||||
LOG.debug("Show() called with %s, %s" % (tenant_id, id))
|
||||
|
||||
sec_group = \
|
||||
models.SecurityGroup.get_security_group_by_id_or_instance_id(
|
||||
id, tenant_id)
|
||||
|
||||
return wsgi.Result(
|
||||
views.SecurityGroupView(sec_group,
|
||||
sec_group.get_rules(),
|
||||
req, tenant_id).show(), 200)
|
||||
|
||||
|
||||
class SecurityGroupRuleController(wsgi.Controller):
|
||||
"""Controller for security group rule functionality"""
|
||||
|
||||
def delete(self, req, tenant_id, id):
|
||||
LOG.debug("Delete Security Group Rule called %s, %s" % (tenant_id, id))
|
||||
|
||||
context = req.environ[wsgi.CONTEXT_KEY]
|
||||
sec_group_rule = models.SecurityGroupRule.find_by(id=id, deleted=False)
|
||||
sec_group = sec_group_rule.get_security_group(tenant_id)
|
||||
|
||||
if sec_group is None:
|
||||
LOG.error("Attempting to delete Group Rule that does not exist or "
|
||||
"does not belong to tenant %s" % tenant_id)
|
||||
raise exception.Forbidden("Unauthorized")
|
||||
|
||||
sec_group_rule.delete(context)
|
||||
return wsgi.Result(None, 204)
|
||||
|
||||
def create(self, req, body, tenant_id):
|
||||
LOG.debug("Creating a Security Group Rule for tenant '%s'" % tenant_id)
|
||||
|
||||
context = req.environ[wsgi.CONTEXT_KEY]
|
||||
self._validate_create_body(body)
|
||||
|
||||
sec_group_id = body['security_group_rule']['group_id']
|
||||
sec_group = models.SecurityGroup.find_by(id=sec_group_id,
|
||||
tenant_id=tenant_id,
|
||||
deleted=False)
|
||||
|
||||
sec_group_rule = models.SecurityGroupRule.create_sec_group_rule(
|
||||
sec_group,
|
||||
CONF.reddwarf_security_group_rule_protocol,
|
||||
CONF.reddwarf_security_group_rule_port,
|
||||
CONF.reddwarf_security_group_rule_port,
|
||||
body['security_group_rule']['cidr'],
|
||||
context)
|
||||
|
||||
resultView = views.SecurityGroupRulesView(sec_group_rule,
|
||||
req,
|
||||
tenant_id).create()
|
||||
return wsgi.Result(resultView, 201)
|
||||
|
||||
def _validate_create_body(self, body):
|
||||
try:
|
||||
# TODO: Add some better validation here around ports,
|
||||
# protocol, and cidr values.
|
||||
body['security_group_rule']
|
||||
body['security_group_rule']['group_id']
|
||||
body['security_group_rule']['cidr']
|
||||
except KeyError as e:
|
||||
LOG.error(_("Create Security Group Rules Required field(s) "
|
||||
"- %s") % e)
|
||||
raise exception.SecurityGroupRuleCreationError(
|
||||
"Required element/key - %s was not specified" % e)
|
124
reddwarf/extensions/security_group/views.py
Normal file
124
reddwarf/extensions/security_group/views.py
Normal file
@ -0,0 +1,124 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
from reddwarf.openstack.common import log as logging
|
||||
import os
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _base_url(req):
|
||||
return req.application_url
|
||||
|
||||
|
||||
class SecurityGroupView(object):
|
||||
|
||||
def __init__(self, secgroup, rules, req, tenant_id):
|
||||
self.secgroup = secgroup
|
||||
self.rules = rules
|
||||
self.request = req
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
def _build_links(self):
|
||||
"""Build the links for the secgroup"""
|
||||
base_url = _base_url(self.request)
|
||||
href = os.path.join(base_url, self.tenant_id,
|
||||
"security-groups", str(self.secgroup['id']))
|
||||
links = [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': href
|
||||
}
|
||||
]
|
||||
return links
|
||||
|
||||
def _build_rules(self):
|
||||
rules = []
|
||||
|
||||
if self.rules is None:
|
||||
return rules
|
||||
|
||||
for rule in self.rules:
|
||||
rules.append({'id': str(rule['id']),
|
||||
'protocol': rule['protocol'],
|
||||
'from_port': rule['from_port'],
|
||||
'to_port': rule['to_port'],
|
||||
'cidr': rule['cidr'],
|
||||
})
|
||||
return rules
|
||||
|
||||
def data(self):
|
||||
return {"id": self.secgroup['id'],
|
||||
"name": self.secgroup['name'],
|
||||
"description": self.secgroup['description'],
|
||||
"rules": self._build_rules(),
|
||||
"links": self._build_links(),
|
||||
"created": self.secgroup['created'],
|
||||
"updated": self.secgroup['updated']
|
||||
}
|
||||
|
||||
def show(self):
|
||||
return {"security_group": self.data()}
|
||||
|
||||
def create(self):
|
||||
return self.show()
|
||||
|
||||
|
||||
class SecurityGroupsView(object):
|
||||
|
||||
def __init__(self, secgroups, rules_dict, req, tenant_id):
|
||||
self.secgroups = secgroups
|
||||
self.rules = rules_dict
|
||||
self.request = req
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
def list(self):
|
||||
groups_data = []
|
||||
|
||||
for secgroup in self.secgroups:
|
||||
rules = \
|
||||
self.rules[secgroup['id']] if self.rules is not None else None
|
||||
groups_data.append(SecurityGroupView(secgroup,
|
||||
rules,
|
||||
self.request,
|
||||
self.tenant_id).data())
|
||||
|
||||
return {"security_groups": groups_data}
|
||||
|
||||
|
||||
class SecurityGroupRulesView(object):
|
||||
|
||||
def __init__(self, rule, req, tenant_id):
|
||||
self.rule = rule
|
||||
self.request = req
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
def _build_create(self):
|
||||
return {"security_group_rule":
|
||||
{"id": str(self.rule['id']),
|
||||
"security_group_id": self.rule['group_id'],
|
||||
"protocol": self.rule['protocol'],
|
||||
"from_port": self.rule['from_port'],
|
||||
"to_port": self.rule['to_port'],
|
||||
"cidr": self.rule['cidr'],
|
||||
"created": self.rule['created']
|
||||
}
|
||||
}
|
||||
|
||||
def create(self):
|
||||
return self._build_create()
|
@ -29,6 +29,7 @@ from reddwarf.common.remote import create_dns_client
|
||||
from reddwarf.common.remote import create_guest_client
|
||||
from reddwarf.common.remote import create_nova_client
|
||||
from reddwarf.common.remote import create_nova_volume_client
|
||||
from reddwarf.extensions.security_group.models import SecurityGroup
|
||||
from reddwarf.db import models as dbmodels
|
||||
from reddwarf.instance.tasks import InstanceTask
|
||||
from reddwarf.instance.tasks import InstanceTasks
|
||||
@ -372,6 +373,10 @@ class BaseInstance(SimpleInstance):
|
||||
time_now = datetime.now()
|
||||
self.update_db(deleted=True, deleted_at=time_now,
|
||||
task_status=InstanceTasks.NONE)
|
||||
# Delete associated security group
|
||||
if CONF.reddwarf_security_groups_support:
|
||||
SecurityGroup.delete_for_instance(self.db_info.id,
|
||||
self.context)
|
||||
|
||||
@property
|
||||
def guest(self):
|
||||
@ -428,6 +433,7 @@ class Instance(BuiltInstance):
|
||||
databases, users, service_type, volume_size):
|
||||
def _create_resources():
|
||||
client = create_nova_client(context)
|
||||
security_groups = None
|
||||
try:
|
||||
flavor = client.flavors.get(flavor_id)
|
||||
except nova_exceptions.NotFound:
|
||||
@ -450,10 +456,17 @@ class Instance(BuiltInstance):
|
||||
db_info.hostname = hostname
|
||||
db_info.save()
|
||||
|
||||
if CONF.reddwarf_security_groups_support:
|
||||
security_group = SecurityGroup.create_for_instance(
|
||||
db_info.id,
|
||||
context)
|
||||
security_groups = [security_group["name"]]
|
||||
|
||||
task_api.API(context).create_instance(db_info.id, name, flavor_id,
|
||||
flavor.ram, image_id,
|
||||
databases, users,
|
||||
service_type, volume_size)
|
||||
service_type, volume_size,
|
||||
security_groups)
|
||||
|
||||
return SimpleInstance(context, db_info, service_status)
|
||||
|
||||
|
@ -89,9 +89,11 @@ class API(ManagerAPI):
|
||||
self._cast("delete_instance", instance_id=instance_id)
|
||||
|
||||
def create_instance(self, instance_id, name, flavor_id, flavor_ram,
|
||||
image_id, databases, users, service_type, volume_size):
|
||||
image_id, databases, users, service_type, volume_size,
|
||||
security_groups):
|
||||
LOG.debug("Making async call to create instance %s " % instance_id)
|
||||
self._cast("create_instance", instance_id=instance_id, name=name,
|
||||
flavor_id=flavor_id, flavor_ram=flavor_ram,
|
||||
image_id=image_id, databases=databases, users=users,
|
||||
service_type=service_type, volume_size=volume_size)
|
||||
service_type=service_type, volume_size=volume_size,
|
||||
security_groups=security_groups)
|
||||
|
@ -70,8 +70,8 @@ class Manager(periodic_task.PeriodicTasks):
|
||||
|
||||
def create_instance(self, context, instance_id, name, flavor_id,
|
||||
flavor_ram, image_id, databases, users, service_type,
|
||||
volume_size):
|
||||
volume_size, security_groups):
|
||||
instance_tasks = FreshInstanceTasks.load(context, instance_id)
|
||||
instance_tasks.create_instance(flavor_id, flavor_ram, image_id,
|
||||
databases, users, service_type,
|
||||
volume_size)
|
||||
volume_size, security_groups)
|
||||
|
@ -58,17 +58,20 @@ use_nova_server_volume = CONF.use_nova_server_volume
|
||||
class FreshInstanceTasks(FreshInstance):
|
||||
|
||||
def create_instance(self, flavor_id, flavor_ram, image_id,
|
||||
databases, users, service_type, volume_size):
|
||||
databases, users, service_type, volume_size,
|
||||
security_groups):
|
||||
if use_nova_server_volume:
|
||||
server, volume_info = self._create_server_volume(
|
||||
flavor_id,
|
||||
image_id,
|
||||
security_groups,
|
||||
service_type,
|
||||
volume_size)
|
||||
else:
|
||||
server, volume_info = self._create_server_volume_individually(
|
||||
flavor_id,
|
||||
image_id,
|
||||
security_groups,
|
||||
service_type,
|
||||
volume_size)
|
||||
try:
|
||||
@ -85,8 +88,8 @@ class FreshInstanceTasks(FreshInstance):
|
||||
if not self.db_info.task_status.is_error:
|
||||
self.update_db(task_status=inst_models.InstanceTasks.NONE)
|
||||
|
||||
def _create_server_volume(self, flavor_id, image_id, service_type,
|
||||
volume_size):
|
||||
def _create_server_volume(self, flavor_id, image_id, security_groups,
|
||||
service_type, volume_size):
|
||||
server = None
|
||||
try:
|
||||
nova_client = create_nova_client(self.context)
|
||||
@ -99,8 +102,10 @@ class FreshInstanceTasks(FreshInstance):
|
||||
volume_ref = {'size': volume_size, 'name': volume_name,
|
||||
'description': volume_desc}
|
||||
|
||||
server = nova_client.servers.create(name, image_id, flavor_id,
|
||||
files=files, volume=volume_ref)
|
||||
server = nova_client.servers.create(
|
||||
name, image_id, flavor_id,
|
||||
files=files, volume=volume_ref,
|
||||
security_groups=security_groups)
|
||||
LOG.debug(_("Created new compute instance %s.") % server.id)
|
||||
|
||||
server_dict = server._info
|
||||
@ -123,7 +128,8 @@ class FreshInstanceTasks(FreshInstance):
|
||||
return server, volume_info
|
||||
|
||||
def _create_server_volume_individually(self, flavor_id, image_id,
|
||||
service_type, volume_size):
|
||||
security_groups, service_type,
|
||||
volume_size):
|
||||
volume_info = None
|
||||
block_device_mapping = None
|
||||
server = None
|
||||
@ -136,8 +142,8 @@ class FreshInstanceTasks(FreshInstance):
|
||||
self._log_and_raise(e, msg, err)
|
||||
|
||||
try:
|
||||
server = self._create_server(flavor_id, image_id, service_type,
|
||||
block_device_mapping)
|
||||
server = self._create_server(flavor_id, image_id, security_groups,
|
||||
service_type, block_device_mapping)
|
||||
server_id = server.id
|
||||
# Save server ID.
|
||||
self.update_db(compute_instance_id=server_id)
|
||||
@ -212,7 +218,7 @@ class FreshInstanceTasks(FreshInstance):
|
||||
'volumes': volumes}
|
||||
return volume_info
|
||||
|
||||
def _create_server(self, flavor_id, image_id,
|
||||
def _create_server(self, flavor_id, image_id, security_groups,
|
||||
service_type, block_device_mapping):
|
||||
nova_client = create_nova_client(self.context)
|
||||
files = {"/etc/guest_info": ("[DEFAULT]\nguest_id=%s\n"
|
||||
@ -222,6 +228,7 @@ class FreshInstanceTasks(FreshInstance):
|
||||
bdmap = block_device_mapping
|
||||
server = nova_client.servers.create(name, image_id, flavor_id,
|
||||
files=files,
|
||||
security_groups=security_groups,
|
||||
block_device_mapping=bdmap)
|
||||
LOG.debug(_("Created new compute instance %s.") % server.id)
|
||||
return server
|
||||
|
@ -31,6 +31,7 @@ GROUP_STOP = "dbaas.guest.shutdown"
|
||||
GROUP_USERS = "dbaas.api.users"
|
||||
GROUP_ROOT = "dbaas.api.root"
|
||||
GROUP_DATABASES = "dbaas.api.databases"
|
||||
GROUP_SECURITY_GROUPS = "dbaas.api.security_groups"
|
||||
|
||||
from datetime import datetime
|
||||
from nose.plugins.skip import SkipTest
|
||||
@ -51,6 +52,7 @@ from proboscis.asserts import assert_not_equal
|
||||
from proboscis.asserts import assert_raises
|
||||
from proboscis.asserts import assert_is
|
||||
from proboscis.asserts import assert_is_none
|
||||
from proboscis.asserts import assert_is_not_none
|
||||
from proboscis.asserts import assert_is_not
|
||||
from proboscis.asserts import assert_true
|
||||
from proboscis.asserts import Check
|
||||
@ -67,6 +69,7 @@ from reddwarf.tests.util import skip_if_xml
|
||||
from reddwarf.tests.util import string_in_list
|
||||
from reddwarf.tests.util import poll_until
|
||||
from reddwarf.tests.util.check import AttrCheck
|
||||
from reddwarf.tests.util.check import TypeCheck
|
||||
|
||||
|
||||
class InstanceTestInfo(object):
|
||||
@ -451,6 +454,106 @@ def assert_unprocessable(func, *args):
|
||||
pass # Good
|
||||
|
||||
|
||||
@test(depends_on_classes=[CreateInstance],
|
||||
groups=[GROUP, GROUP_SECURITY_GROUPS],
|
||||
runs_after_groups=[tests.PRE_INSTANCES])
|
||||
class SecurityGroupsTest(object):
|
||||
|
||||
@before_class
|
||||
def setUp(self):
|
||||
self.testSecurityGroup = dbaas.security_groups.get(
|
||||
instance_info.id)
|
||||
self.secGroupName = "SecGroup_%s" % instance_info.id
|
||||
self.secGroupDescription = \
|
||||
"Default Security Group For DBaaS Instance <%s>" % instance_info.id
|
||||
|
||||
@test
|
||||
def test_created_security_group(self):
|
||||
assert_is_not_none(self.testSecurityGroup)
|
||||
with TypeCheck('SecurityGroup', self.testSecurityGroup) as secGrp:
|
||||
secGrp.has_field('id', basestring)
|
||||
secGrp.has_field('name', basestring)
|
||||
secGrp.has_field('description', basestring)
|
||||
secGrp.has_field('created', basestring)
|
||||
secGrp.has_field('updated', basestring)
|
||||
assert_equal(self.testSecurityGroup.name, self.secGroupName)
|
||||
assert_equal(self.testSecurityGroup.description,
|
||||
self.secGroupDescription)
|
||||
|
||||
@test
|
||||
def test_list_security_group(self):
|
||||
securityGroupList = dbaas.security_groups.list()
|
||||
assert_is_not_none(securityGroupList)
|
||||
securityGroup = [x for x in securityGroupList
|
||||
if x.name in self.secGroupName]
|
||||
assert_is_not_none(securityGroup)
|
||||
|
||||
@test
|
||||
def test_get_security_group(self):
|
||||
securityGroup = dbaas.security_groups.get(self.testSecurityGroup.id)
|
||||
assert_is_not_none(securityGroup)
|
||||
assert_equal(securityGroup.name, self.secGroupName)
|
||||
assert_equal(securityGroup.description, self.secGroupDescription)
|
||||
|
||||
|
||||
@test(depends_on_classes=[SecurityGroupsTest],
|
||||
groups=[GROUP, GROUP_SECURITY_GROUPS],
|
||||
runs_after_groups=[tests.PRE_INSTANCES])
|
||||
class SecurityGroupsRulesTest(object):
|
||||
|
||||
@before_class
|
||||
def setUp(self):
|
||||
self.testSecurityGroup = dbaas.security_groups.get(
|
||||
instance_info.id)
|
||||
self.secGroupName = "SecGroup_%s" % instance_info.id
|
||||
self.secGroupDescription = \
|
||||
"Default Security Group For DBaaS Instance <%s>" % instance_info.id
|
||||
|
||||
@test
|
||||
def test_create_security_group_rule(self):
|
||||
self.testSecurityGroupRule = dbaas.security_group_rules.create(
|
||||
group_id=self.testSecurityGroup.id,
|
||||
protocol="tcp",
|
||||
from_port=3306,
|
||||
to_port=3306,
|
||||
cidr="0.0.0.0/0")
|
||||
assert_is_not_none(self.testSecurityGroupRule)
|
||||
with TypeCheck('SecurityGroupRule',
|
||||
self.testSecurityGroupRule) as secGrpRule:
|
||||
secGrpRule.has_field('id', basestring)
|
||||
secGrpRule.has_field('security_group_id', basestring)
|
||||
secGrpRule.has_field('protocol', basestring)
|
||||
secGrpRule.has_field('cidr', basestring)
|
||||
secGrpRule.has_field('from_port', int)
|
||||
secGrpRule.has_field('to_port', int)
|
||||
secGrpRule.has_field('created', basestring)
|
||||
assert_equal(self.testSecurityGroupRule.security_group_id,
|
||||
self.testSecurityGroup.id)
|
||||
assert_equal(self.testSecurityGroupRule.protocol, "tcp")
|
||||
assert_equal(int(self.testSecurityGroupRule.from_port), 3306)
|
||||
assert_equal(int(self.testSecurityGroupRule.to_port), 3306)
|
||||
assert_equal(self.testSecurityGroupRule.cidr, "0.0.0.0/0")
|
||||
|
||||
@test
|
||||
def test_deep_list_security_group_with_rules(self):
|
||||
securityGroupList = dbaas.security_groups.list()
|
||||
assert_is_not_none(securityGroupList)
|
||||
securityGroup = [x for x in securityGroupList
|
||||
if x.name in self.secGroupName]
|
||||
assert_is_not_none(securityGroup[0])
|
||||
assert_equal(len(securityGroup[0].rules), 1)
|
||||
|
||||
@test
|
||||
def test_delete_security_group_rule(self):
|
||||
dbaas.security_group_rules.delete(self.testSecurityGroupRule.id)
|
||||
securityGroupList = dbaas.security_groups.list()
|
||||
assert_is_not_none(securityGroupList)
|
||||
securityGroup = [x for x in securityGroupList
|
||||
if x.name in self.secGroupName]
|
||||
assert_is_not_none(securityGroup[0])
|
||||
assert_equal(len(securityGroup[0].rules), 0)
|
||||
|
||||
|
||||
@test(depends_on_classes=[CreateInstance],
|
||||
groups=[GROUP,
|
||||
GROUP_START,
|
||||
|
@ -256,7 +256,7 @@ class FakeServers(object):
|
||||
server.owner.tenant == self.context.tenant)
|
||||
|
||||
def create(self, name, image_id, flavor_ref, files=None,
|
||||
block_device_mapping=None, volume=None):
|
||||
block_device_mapping=None, volume=None, security_groups=None):
|
||||
id = "FAKE_%s" % uuid.uuid4()
|
||||
if volume:
|
||||
volume = self.volumes.create(volume['size'], volume['name'],
|
||||
@ -644,6 +644,93 @@ class FakeRdStorages(object):
|
||||
return [self.storages[name] for name in self.storages]
|
||||
|
||||
|
||||
class FakeSecurityGroup(object):
|
||||
|
||||
def __init__(self, name=None, description=None, context=None):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.id = "FAKE_SECGRP_%s" % uuid.uuid4()
|
||||
self.rules = {}
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
|
||||
def add_rule(self, fakeSecGroupRule):
|
||||
self.rules.append(fakeSecGroupRule)
|
||||
return self.rules
|
||||
|
||||
def get_rules(self):
|
||||
result = ""
|
||||
for rule in self.rules:
|
||||
result = result + rule.data()
|
||||
return result
|
||||
|
||||
def data(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'description': self.description
|
||||
}
|
||||
|
||||
|
||||
class FakeSecurityGroups(object):
|
||||
|
||||
def __init__(self, context=None):
|
||||
self.context = context
|
||||
self.securityGroups = {}
|
||||
|
||||
def create(self, name=None, description=None):
|
||||
secGrp = FakeSecurityGroup(name, description)
|
||||
self.securityGroups[secGrp.get_id()] = secGrp
|
||||
return secGrp
|
||||
|
||||
def list(self):
|
||||
pass
|
||||
|
||||
|
||||
class FakeSecurityGroupRule(object):
|
||||
|
||||
def __init__(self, ip_protocol=None, from_port=None, to_port=None,
|
||||
cidr=None, parent_group_id=None, context=None):
|
||||
self.group_id = parent_group_id
|
||||
self.protocol = ip_protocol
|
||||
self.from_port = from_port
|
||||
self.to_port = to_port
|
||||
self.cidr = cidr
|
||||
self.context = context
|
||||
self.id = "FAKE_SECGRP_RULE_%s" % uuid.uuid4()
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
|
||||
def data(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'group_id': self.group_id,
|
||||
'protocol': self.protocol,
|
||||
'from_port': self.from_port,
|
||||
'to_port': self.to_port,
|
||||
'cidr': self.cidr
|
||||
}
|
||||
|
||||
|
||||
class FakeSecurityGroupRules(object):
|
||||
|
||||
def __init__(self, context=None):
|
||||
self.context = context
|
||||
self.securityGroupRules = {}
|
||||
|
||||
def create(self, parent_group_id, ip_protocol, from_port, to_port, cidr):
|
||||
secGrpRule = FakeSecurityGroupRule(ip_protocol, from_port, to_port,
|
||||
cidr, parent_group_id)
|
||||
self.securityGroupRules[secGrpRule.get_id()] = secGrpRule
|
||||
return secGrpRule
|
||||
|
||||
def delete(self, id):
|
||||
if id in self.securityGroupRules:
|
||||
del self.securityGroupRules[id]
|
||||
|
||||
|
||||
class FakeClient(object):
|
||||
|
||||
def __init__(self, context):
|
||||
@ -656,6 +743,8 @@ class FakeClient(object):
|
||||
self.rdhosts = FakeHosts(self.servers)
|
||||
self.rdstorage = FakeRdStorages()
|
||||
self.rdservers = FakeRdServers(self.servers)
|
||||
self.security_groups = FakeSecurityGroups(context)
|
||||
self.security_group_rules = FakeSecurityGroupRules(context)
|
||||
|
||||
def get_server_volumes(self, server_id):
|
||||
return self.servers.get_server_volumes(server_id)
|
||||
|
16
reddwarf/tests/unittests/secgroups/__init__.py
Normal file
16
reddwarf/tests/unittests/secgroups/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
80
reddwarf/tests/unittests/secgroups/test_security_group.py
Normal file
80
reddwarf/tests/unittests/secgroups/test_security_group.py
Normal file
@ -0,0 +1,80 @@
|
||||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# 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.
|
||||
|
||||
import testtools
|
||||
import reddwarf.common.remote
|
||||
from mock import Mock
|
||||
from reddwarf.extensions.security_group import models
|
||||
from reddwarf.common import exception
|
||||
from reddwarf.tests.fakes import nova
|
||||
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
|
||||
|
||||
"""
|
||||
Unit tests for testing the exceptions raised by Security Groups
|
||||
"""
|
||||
|
||||
|
||||
class Security_Group_Exceptions_Test(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(Security_Group_Exceptions_Test, self).setUp()
|
||||
self.createNovaClient = reddwarf.common.remote.create_nova_client
|
||||
self.context = Mock()
|
||||
self.FakeClient = nova.fake_create_nova_client(self.context)
|
||||
|
||||
fException = Mock(side_effect=
|
||||
lambda *args, **kwargs:
|
||||
self._raise(nova_exceptions.ClientException("Test")))
|
||||
|
||||
self.FakeClient.security_groups.create = fException
|
||||
self.FakeClient.security_groups.delete = fException
|
||||
self.FakeClient.security_group_rules.create = fException
|
||||
self.FakeClient.security_group_rules.delete = fException
|
||||
|
||||
reddwarf.common.remote.create_nova_client = \
|
||||
lambda c: self._return_mocked_nova_client(c)
|
||||
|
||||
def tearDown(self):
|
||||
super(Security_Group_Exceptions_Test, self).tearDown()
|
||||
reddwarf.common.remote.create_nova_client = self.createNovaClient
|
||||
|
||||
def _return_mocked_nova_client(self, context):
|
||||
return self.FakeClient
|
||||
|
||||
def _raise(self, ex):
|
||||
raise ex
|
||||
|
||||
def test_failed_to_create_security_group(self):
|
||||
self.assertRaises(exception.SecurityGroupCreationError,
|
||||
models.RemoteSecurityGroup.create,
|
||||
"TestName",
|
||||
"TestDescription",
|
||||
self.context)
|
||||
|
||||
def test_failed_to_delete_security_group(self):
|
||||
self.assertRaises(exception.SecurityGroupDeletionError,
|
||||
models.RemoteSecurityGroup.delete,
|
||||
1, self.context)
|
||||
|
||||
def test_failed_to_create_security_group_rule(self):
|
||||
self.assertRaises(exception.SecurityGroupRuleCreationError,
|
||||
models.RemoteSecurityGroup.add_rule,
|
||||
1, "tcp", 3306, 3306, "0.0.0.0/0", self.context)
|
||||
|
||||
def test_failed_to_delete_security_group_rule(self):
|
||||
self.assertRaises(exception.SecurityGroupRuleDeletionError,
|
||||
models.RemoteSecurityGroup.delete_rule,
|
||||
1, self.context)
|
Loading…
Reference in New Issue
Block a user