Merge "Add neutron support"
This commit is contained in:
commit
c68fef2b7a
@ -60,6 +60,7 @@ trove_auth_url = http://0.0.0.0:5000/v2.0
|
|||||||
#nova_compute_url = http://localhost:8774/v2
|
#nova_compute_url = http://localhost:8774/v2
|
||||||
#cinder_url = http://localhost:8776/v1
|
#cinder_url = http://localhost:8776/v1
|
||||||
#swift_url = http://localhost:8080/v1/AUTH_
|
#swift_url = http://localhost:8080/v1/AUTH_
|
||||||
|
#neutron_url = http://localhost:9696/
|
||||||
|
|
||||||
# nova_compute_url, cinder_url, swift_url, and heat_url can all be fetched
|
# nova_compute_url, cinder_url, swift_url, and heat_url can all be fetched
|
||||||
# from Keystone. To fetch from Keystone, comment out nova_compute_url,
|
# from Keystone. To fetch from Keystone, comment out nova_compute_url,
|
||||||
@ -75,6 +76,8 @@ trove_auth_url = http://0.0.0.0:5000/v2.0
|
|||||||
#swift_service_type = object-store
|
#swift_service_type = object-store
|
||||||
# Service type to use when searching catalog.
|
# Service type to use when searching catalog.
|
||||||
#heat_service_type = orchestration
|
#heat_service_type = orchestration
|
||||||
|
# Service type to use when searching catalog.
|
||||||
|
#neutron_service_type = network
|
||||||
|
|
||||||
# Config options for enabling volume service
|
# Config options for enabling volume service
|
||||||
trove_volume_support = True
|
trove_volume_support = True
|
||||||
@ -119,6 +122,10 @@ dns_instance_entry_factory = trove.dns.designate.driver.DesignateInstanceEntryFa
|
|||||||
dns_endpoint_url = http://127.0.0.1/v1/
|
dns_endpoint_url = http://127.0.0.1/v1/
|
||||||
dns_service_type = dns
|
dns_service_type = dns
|
||||||
|
|
||||||
|
# Neutron
|
||||||
|
network_driver = trove.network.nova.NovaNetwork
|
||||||
|
default_neutron_networks =
|
||||||
|
|
||||||
# Trove Security Groups for Instances
|
# Trove Security Groups for Instances
|
||||||
trove_security_groups_support = True
|
trove_security_groups_support = True
|
||||||
trove_security_group_rule_cidr = 0.0.0.0/0
|
trove_security_group_rule_cidr = 0.0.0.0/0
|
||||||
@ -132,8 +139,13 @@ agent_call_high_timeout = 150
|
|||||||
use_nova_server_volume = False
|
use_nova_server_volume = False
|
||||||
|
|
||||||
# Config option for filtering the IP address that DNS uses
|
# Config option for filtering the IP address that DNS uses
|
||||||
|
# For nova-network, set this to the appropriate network label defined in nova
|
||||||
|
# For neutron, set this to .* since users can specify custom network labels
|
||||||
|
# You can also optionally specify regex'es to match the actual IP addresses
|
||||||
|
# ip_regex (white-list) is applied before black_list_regex in the filter chain
|
||||||
network_label_regex = ^private$
|
network_label_regex = ^private$
|
||||||
#ip_regex = ^(15.|123.)
|
#ip_regex = ^(15.|123.)
|
||||||
|
#black_list_regex = ^(10.0.0.)
|
||||||
|
|
||||||
# Datastore templates
|
# Datastore templates
|
||||||
template_path = /etc/trove/templates/
|
template_path = /etc/trove/templates/
|
||||||
|
@ -76,6 +76,7 @@ trove_auth_url = http://0.0.0.0:5000/v2.0
|
|||||||
#nova_compute_url = http://localhost:8774/v2
|
#nova_compute_url = http://localhost:8774/v2
|
||||||
#cinder_url = http://localhost:8776/v1
|
#cinder_url = http://localhost:8776/v1
|
||||||
#swift_url = http://localhost:8080/v1/AUTH_
|
#swift_url = http://localhost:8080/v1/AUTH_
|
||||||
|
#neutron_url = http://localhost:9696/
|
||||||
|
|
||||||
# nova_compute_url, cinder_url, swift_url, and heat_url can all be fetched
|
# nova_compute_url, cinder_url, swift_url, and heat_url can all be fetched
|
||||||
# from Keystone. To fetch from Keystone, comment out nova_compute_url,
|
# from Keystone. To fetch from Keystone, comment out nova_compute_url,
|
||||||
@ -91,10 +92,18 @@ trove_auth_url = http://0.0.0.0:5000/v2.0
|
|||||||
#swift_service_type = object-store
|
#swift_service_type = object-store
|
||||||
# Service type to use when searching catalog.
|
# Service type to use when searching catalog.
|
||||||
#heat_service_type = orchestration
|
#heat_service_type = orchestration
|
||||||
|
# Service type to use when searching catalog.
|
||||||
|
#neutron_service_type = network
|
||||||
|
|
||||||
# Config option for showing the IP address that nova doles out
|
# Config option for showing the IP address that nova doles out
|
||||||
|
# For nova-network, set this to the appropriate network label defined in nova
|
||||||
|
# For neutron, set this to .* since users can specify custom network labels
|
||||||
|
# You can also optionally specify regex'es to match the actual IP addresses
|
||||||
|
# ip_regex (white-list) is applied before black_list_regex in the filter chain
|
||||||
network_label_regex = ^private$
|
network_label_regex = ^private$
|
||||||
|
#network_label_regex = .* //with neutron enabled
|
||||||
#ip_regex = ^(15.|123.)
|
#ip_regex = ^(15.|123.)
|
||||||
|
#black_list_regex = ^10.0.0.
|
||||||
|
|
||||||
# Config options for enabling volume service
|
# Config options for enabling volume service
|
||||||
trove_volume_support = True
|
trove_volume_support = True
|
||||||
@ -129,6 +138,11 @@ dns_instance_entry_factory = trove.dns.designate.driver.DesignateInstanceEntryFa
|
|||||||
dns_endpoint_url = http://127.0.0.1/v1/
|
dns_endpoint_url = http://127.0.0.1/v1/
|
||||||
dns_service_type = dns
|
dns_service_type = dns
|
||||||
|
|
||||||
|
# Neutron
|
||||||
|
network_driver = trove.network.nova.NovaNetwork
|
||||||
|
default_neutron_networks =
|
||||||
|
|
||||||
|
|
||||||
# Taskmanager queue name
|
# Taskmanager queue name
|
||||||
taskmanager_queue = taskmanager
|
taskmanager_queue = taskmanager
|
||||||
|
|
||||||
|
@ -79,6 +79,7 @@ nova_service_name = Compute Service
|
|||||||
# Config option for showing the IP address that nova doles out
|
# Config option for showing the IP address that nova doles out
|
||||||
network_label_regex = ^private$
|
network_label_regex = ^private$
|
||||||
ip_regex = ^(15.|123.)
|
ip_regex = ^(15.|123.)
|
||||||
|
black_list_regex = ^(10.0.0.)
|
||||||
|
|
||||||
# Config options for enabling volume service
|
# Config options for enabling volume service
|
||||||
trove_volume_support = True
|
trove_volume_support = True
|
||||||
|
@ -18,6 +18,7 @@ python-cinderclient>=1.0.7
|
|||||||
python-keystoneclient>=0.9.0
|
python-keystoneclient>=0.9.0
|
||||||
python-swiftclient>=2.0.2
|
python-swiftclient>=2.0.2
|
||||||
python-designateclient>=1.0.0
|
python-designateclient>=1.0.0
|
||||||
|
python-neutronclient>=2.3.5,<3
|
||||||
iso8601>=0.1.9
|
iso8601>=0.1.9
|
||||||
jsonschema>=2.0.0,<3.0.0
|
jsonschema>=2.0.0,<3.0.0
|
||||||
Jinja2
|
Jinja2
|
||||||
|
@ -55,6 +55,9 @@ common_opts = [
|
|||||||
cfg.StrOpt('nova_compute_url', help='URL without the tenant segment.'),
|
cfg.StrOpt('nova_compute_url', help='URL without the tenant segment.'),
|
||||||
cfg.StrOpt('nova_compute_service_type', default='compute',
|
cfg.StrOpt('nova_compute_service_type', default='compute',
|
||||||
help='Service type to use when searching catalog.'),
|
help='Service type to use when searching catalog.'),
|
||||||
|
cfg.StrOpt('neutron_url', help='URL without the tenant segment.'),
|
||||||
|
cfg.StrOpt('neutron_service_type', default='network',
|
||||||
|
help='Service type to use when searching catalog.'),
|
||||||
cfg.StrOpt('cinder_url', help='URL without the tenant segment.'),
|
cfg.StrOpt('cinder_url', help='URL without the tenant segment.'),
|
||||||
cfg.StrOpt('cinder_service_type', default='volumev2',
|
cfg.StrOpt('cinder_service_type', default='volumev2',
|
||||||
help='Service type to use when searching catalog.'),
|
help='Service type to use when searching catalog.'),
|
||||||
@ -214,6 +217,8 @@ common_opts = [
|
|||||||
default='trove.common.remote.guest_client'),
|
default='trove.common.remote.guest_client'),
|
||||||
cfg.StrOpt('remote_nova_client',
|
cfg.StrOpt('remote_nova_client',
|
||||||
default='trove.common.remote.nova_client'),
|
default='trove.common.remote.nova_client'),
|
||||||
|
cfg.StrOpt('remote_neutron_client',
|
||||||
|
default='trove.common.remote.neutron_client'),
|
||||||
cfg.StrOpt('remote_cinder_client',
|
cfg.StrOpt('remote_cinder_client',
|
||||||
default='trove.common.remote.cinder_client'),
|
default='trove.common.remote.cinder_client'),
|
||||||
cfg.StrOpt('remote_heat_client',
|
cfg.StrOpt('remote_heat_client',
|
||||||
@ -240,6 +245,7 @@ common_opts = [
|
|||||||
help="Admin tenant used to connect to nova.", secret=True),
|
help="Admin tenant used to connect to nova.", secret=True),
|
||||||
cfg.StrOpt('network_label_regex', default='^private$'),
|
cfg.StrOpt('network_label_regex', default='^private$'),
|
||||||
cfg.StrOpt('ip_regex', default=None),
|
cfg.StrOpt('ip_regex', default=None),
|
||||||
|
cfg.StrOpt('black_list_regex', default=None),
|
||||||
cfg.StrOpt('cloudinit_location', default='/etc/trove/cloudinit',
|
cfg.StrOpt('cloudinit_location', default='/etc/trove/cloudinit',
|
||||||
help="Path to folder with cloudinit scripts."),
|
help="Path to folder with cloudinit scripts."),
|
||||||
cfg.StrOpt('guest_config',
|
cfg.StrOpt('guest_config',
|
||||||
@ -259,18 +265,21 @@ common_opts = [
|
|||||||
default=['json'],
|
default=['json'],
|
||||||
help='Filetype endings not to be reattached to an ID '
|
help='Filetype endings not to be reattached to an ID '
|
||||||
'by the utils method correct_id_with_req.'),
|
'by the utils method correct_id_with_req.'),
|
||||||
cfg.ListOpt('default_neutron_networks',
|
cfg.ListOpt('default_neutron_networks', default=[],
|
||||||
default=[],
|
help='List of IDs for management networks which should be '
|
||||||
help='List of network IDs which should be attached'
|
' attached to the instance regardless of what NICs'
|
||||||
' to instance when networks are not specified'
|
' are specified in the create API call.'),
|
||||||
' in API call.'),
|
|
||||||
cfg.IntOpt('max_header_line', default=16384,
|
cfg.IntOpt('max_header_line', default=16384,
|
||||||
help='Maximum line size of message headers to be accepted. '
|
help='Maximum line size of message headers to be accepted. '
|
||||||
'max_header_line may need to be increased when using '
|
'max_header_line may need to be increased when using '
|
||||||
'large tokens (typically those generated by the '
|
'large tokens (typically those generated by the '
|
||||||
'Keystone v3 API with big service catalogs).'),
|
'Keystone v3 API with big service catalogs).'),
|
||||||
cfg.StrOpt('conductor_manager', default='trove.conductor.manager.Manager',
|
cfg.StrOpt('conductor_manager', default='trove.conductor.manager.Manager',
|
||||||
help='Qualified class name to use for conductor manager.')
|
help='Qualified class name to use for conductor manager.'),
|
||||||
|
cfg.StrOpt('network_driver', default='trove.network.nova.NovaNetwork',
|
||||||
|
help="Describes the actual network manager used for "
|
||||||
|
"the management of network attributes "
|
||||||
|
"(security groups, floating IPs, etc.)")
|
||||||
]
|
]
|
||||||
|
|
||||||
# Datastore specific option groups
|
# Datastore specific option groups
|
||||||
|
@ -15,7 +15,11 @@
|
|||||||
|
|
||||||
"""Model classes that form the core of instances functionality."""
|
"""Model classes that form the core of instances functionality."""
|
||||||
|
|
||||||
|
from trove.openstack.common.importutils import import_class
|
||||||
from trove.common import remote
|
from trove.common import remote
|
||||||
|
from trove.common import cfg
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
class ModelBase(object):
|
class ModelBase(object):
|
||||||
@ -94,6 +98,17 @@ class RemoteModelBase(ModelBase):
|
|||||||
return self._data_item(self._data_object)
|
return self._data_item(self._data_object)
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkRemoteModelBase(RemoteModelBase):
|
||||||
|
|
||||||
|
network_driver = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_driver(cls, context):
|
||||||
|
if not cls.network_driver:
|
||||||
|
cls.network_driver = import_class(CONF.network_driver)
|
||||||
|
return cls.network_driver(context)
|
||||||
|
|
||||||
|
|
||||||
class NovaRemoteModelBase(RemoteModelBase):
|
class NovaRemoteModelBase(RemoteModelBase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -162,9 +162,25 @@ def swift_client(context):
|
|||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
|
def neutron_client(context):
|
||||||
|
from neutronclient.v2_0 import client as NeutronClient
|
||||||
|
if CONF.neutron_url:
|
||||||
|
# neutron endpoint url / publicURL does not include tenant segment
|
||||||
|
url = CONF.neutron_url
|
||||||
|
else:
|
||||||
|
url = get_endpoint(context.service_catalog,
|
||||||
|
service_type=CONF.neutron_service_type,
|
||||||
|
endpoint_region=CONF.os_region_name)
|
||||||
|
|
||||||
|
client = NeutronClient.Client(token=context.auth_token,
|
||||||
|
endpoint_url=url)
|
||||||
|
return client
|
||||||
|
|
||||||
|
|
||||||
create_dns_client = import_class(CONF.remote_dns_client)
|
create_dns_client = import_class(CONF.remote_dns_client)
|
||||||
create_guest_client = import_class(CONF.remote_guest_client)
|
create_guest_client = import_class(CONF.remote_guest_client)
|
||||||
create_nova_client = import_class(CONF.remote_nova_client)
|
create_nova_client = import_class(CONF.remote_nova_client)
|
||||||
create_swift_client = import_class(CONF.remote_swift_client)
|
create_swift_client = import_class(CONF.remote_swift_client)
|
||||||
create_cinder_client = import_class(CONF.remote_cinder_client)
|
create_cinder_client = import_class(CONF.remote_cinder_client)
|
||||||
create_heat_client = import_class(CONF.remote_heat_client)
|
create_heat_client = import_class(CONF.remote_heat_client)
|
||||||
|
create_neutron_client = import_class(CONF.remote_neutron_client)
|
||||||
|
@ -17,15 +17,13 @@
|
|||||||
"""
|
"""
|
||||||
Model classes for Security Groups and Security Group Rules on instances.
|
Model classes for Security Groups and Security Group Rules on instances.
|
||||||
"""
|
"""
|
||||||
import trove.common.remote
|
|
||||||
from trove.common import cfg
|
from trove.common import cfg
|
||||||
from trove.common import exception
|
from trove.common import exception
|
||||||
from trove.db.models import DatabaseModelBase
|
from trove.db.models import DatabaseModelBase
|
||||||
from trove.common.models import NovaRemoteModelBase
|
from trove.common.models import NetworkRemoteModelBase
|
||||||
from trove.openstack.common import log as logging
|
from trove.openstack.common import log as logging
|
||||||
from trove.openstack.common.gettextutils import _
|
from trove.openstack.common.gettextutils import _
|
||||||
|
|
||||||
from novaclient import exceptions as nova_exceptions
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -207,7 +205,7 @@ class SecurityGroupInstanceAssociation(DatabaseModelBase):
|
|||||||
return association.instance_id
|
return association.instance_id
|
||||||
|
|
||||||
|
|
||||||
class RemoteSecurityGroup(NovaRemoteModelBase):
|
class RemoteSecurityGroup(NetworkRemoteModelBase):
|
||||||
|
|
||||||
_data_fields = ['id', 'name', 'description', 'rules']
|
_data_fields = ['id', 'name', 'description', 'rules']
|
||||||
|
|
||||||
@ -216,65 +214,37 @@ class RemoteSecurityGroup(NovaRemoteModelBase):
|
|||||||
msg = "Security Group does not have id defined!"
|
msg = "Security Group does not have id defined!"
|
||||||
raise exception.InvalidModelError(msg)
|
raise exception.InvalidModelError(msg)
|
||||||
elif security_group is None:
|
elif security_group is None:
|
||||||
try:
|
driver = self.get_driver(context)
|
||||||
client = trove.common.remote.create_nova_client(context)
|
self._data_object = driver.get_sec_group_by_id(group_id=id)
|
||||||
self._data_object = client.security_groups.get(id)
|
|
||||||
except nova_exceptions.NotFound as e:
|
|
||||||
raise exception.NotFound(id=id)
|
|
||||||
except nova_exceptions.ClientException as e:
|
|
||||||
raise exception.TroveError(str(e))
|
|
||||||
else:
|
else:
|
||||||
self._data_object = security_group
|
self._data_object = security_group
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, name, description, context):
|
def create(cls, name, description, context):
|
||||||
"""Creates a new Security Group."""
|
"""Creates a new Security Group."""
|
||||||
client = trove.common.remote.create_nova_client(context)
|
driver = cls.get_driver(context)
|
||||||
try:
|
sec_group = driver.create_security_group(
|
||||||
sec_group = client.security_groups.create(name=name,
|
name=name, description=description)
|
||||||
description=description)
|
|
||||||
except nova_exceptions.ClientException as e:
|
|
||||||
LOG.exception('Failed to create remote security group')
|
|
||||||
raise exception.SecurityGroupCreationError(str(e))
|
|
||||||
|
|
||||||
return RemoteSecurityGroup(security_group=sec_group)
|
return RemoteSecurityGroup(security_group=sec_group)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete(cls, sec_group_id, context):
|
def delete(cls, sec_group_id, context):
|
||||||
client = trove.common.remote.create_nova_client(context)
|
"""Deletes a Security Group."""
|
||||||
|
driver = cls.get_driver(context)
|
||||||
try:
|
driver.delete_security_group(sec_group_id)
|
||||||
client.security_groups.delete(sec_group_id)
|
|
||||||
except nova_exceptions.ClientException as e:
|
|
||||||
LOG.exception('Failed to delete remote security group')
|
|
||||||
raise exception.SecurityGroupDeletionError(str(e))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_rule(cls, sec_group_id, protocol, from_port,
|
def add_rule(cls, sec_group_id, protocol, from_port,
|
||||||
to_port, cidr, context):
|
to_port, cidr, context):
|
||||||
|
"""Adds a new rule to an existing security group."""
|
||||||
|
driver = cls.get_driver(context)
|
||||||
|
sec_group_rule = driver.add_security_group_rule(
|
||||||
|
sec_group_id, protocol, from_port, to_port, cidr)
|
||||||
|
|
||||||
client = trove.common.remote.create_nova_client(context)
|
return sec_group_rule.id
|
||||||
|
|
||||||
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 as e:
|
|
||||||
LOG.exception('Failed to add rule to remote security group')
|
|
||||||
raise exception.SecurityGroupRuleCreationError(str(e))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete_rule(cls, sec_group_rule_id, context):
|
def delete_rule(cls, sec_group_rule_id, context):
|
||||||
client = trove.common.remote.create_nova_client(context)
|
"""Deletes a rule from an existing security group."""
|
||||||
|
driver = cls.get_driver(context)
|
||||||
try:
|
driver.delete_security_group_rule(sec_group_rule_id)
|
||||||
client.security_group_rules.delete(sec_group_rule_id)
|
|
||||||
|
|
||||||
except nova_exceptions.ClientException as e:
|
|
||||||
LOG.exception('Failed to delete rule to remote security group')
|
|
||||||
raise exception.SecurityGroupRuleDeletionError(str(e))
|
|
||||||
|
@ -48,9 +48,12 @@ CONF = cfg.CONF
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def filter_ips(ips, regex):
|
def filter_ips(ips, white_list_regex, black_list_regex):
|
||||||
"""Filter out IPs not matching regex."""
|
"""Return IPs matching white_list_regex and
|
||||||
return [ip for ip in ips if re.search(regex, ip)]
|
Filter out IPs matching black_list_regex.
|
||||||
|
"""
|
||||||
|
return [ip for ip in ips if re.search(white_list_regex, ip)
|
||||||
|
and not re.search(black_list_regex, ip)]
|
||||||
|
|
||||||
|
|
||||||
def load_server(context, instance_id, server_id):
|
def load_server(context, instance_id, server_id):
|
||||||
@ -204,8 +207,8 @@ class SimpleInstance(object):
|
|||||||
IPs.extend([addr.get('addr')
|
IPs.extend([addr.get('addr')
|
||||||
for addr in self.addresses[label]])
|
for addr in self.addresses[label]])
|
||||||
# Includes ip addresses that match the regexp pattern
|
# Includes ip addresses that match the regexp pattern
|
||||||
if CONF.ip_regex:
|
if CONF.ip_regex and CONF.black_list_regex:
|
||||||
IPs = filter_ips(IPs, CONF.ip_regex)
|
IPs = filter_ips(IPs, CONF.ip_regex, CONF.black_list_regex)
|
||||||
return IPs
|
return IPs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -640,10 +643,11 @@ class Instance(BuiltInstance):
|
|||||||
datastore1=backup_info.datastore.name,
|
datastore1=backup_info.datastore.name,
|
||||||
datastore2=datastore.name)
|
datastore2=datastore.name)
|
||||||
|
|
||||||
if not nics and CONF.default_neutron_networks:
|
if not nics:
|
||||||
nics = []
|
nics = []
|
||||||
for net_id in CONF.default_neutron_networks:
|
if CONF.default_neutron_networks:
|
||||||
nics.append({"net-id": net_id})
|
nics = [{"net-id": net_id}
|
||||||
|
for net_id in CONF.default_neutron_networks] + nics
|
||||||
|
|
||||||
def _create_resources():
|
def _create_resources():
|
||||||
|
|
||||||
|
0
trove/network/__init__.py
Normal file
0
trove/network/__init__.py
Normal file
51
trove/network/base.py
Normal file
51
trove/network/base.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Copyright 2014 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.
|
||||||
|
#
|
||||||
|
import abc
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class NetworkDriver(object):
|
||||||
|
"""Base Network Driver class to abstract the network driver used."""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_sec_group_by_id(self, group_id):
|
||||||
|
"""
|
||||||
|
Returns security group with given group_id
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def create_security_group(self, name, description):
|
||||||
|
"""
|
||||||
|
Creates the security group with given name and description
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def delete_security_group(self, sec_group_id):
|
||||||
|
"""Deletes the security group by given ID."""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def add_security_group_rule(self, sec_group_id, protocol,
|
||||||
|
from_port, to_port, cidr):
|
||||||
|
"""
|
||||||
|
Adds the rule identified by the security group ID,
|
||||||
|
transport protocol, port range: from -> to, CIDR.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def delete_security_group_rule(self, sec_group_rule_id):
|
||||||
|
"""Deletes the rule by given ID."""
|
143
trove/network/neutron.py
Normal file
143
trove/network/neutron.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
# Copyright 2014 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 trove.common import exception
|
||||||
|
from trove.common import remote
|
||||||
|
from trove.network import base
|
||||||
|
from trove.openstack.common import log as logging
|
||||||
|
from neutronclient.common import exceptions as neutron_exceptions
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONST = {'IPv4': "IPv4",
|
||||||
|
'IPv6': "IPv6",
|
||||||
|
'INGRESS': "ingress",
|
||||||
|
'EGRESS': "egress",
|
||||||
|
'PROTO_NAME_TCP': 'tcp',
|
||||||
|
'PROTO_NAME_ICMP': 'icmp',
|
||||||
|
'PROTO_NAME_ICMP_V6': 'icmpv6',
|
||||||
|
'PROTO_NAME_UDP': 'udp'}
|
||||||
|
|
||||||
|
|
||||||
|
class NovaNetworkStruct(object):
|
||||||
|
def __init__(self, **properties):
|
||||||
|
self.__dict__.update(properties)
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronDriver(base.NetworkDriver):
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
try:
|
||||||
|
self.client = remote.create_neutron_client(context)
|
||||||
|
except neutron_exceptions.NeutronClientException as e:
|
||||||
|
raise exception.TroveError(str(e))
|
||||||
|
|
||||||
|
def get_sec_group_by_id(self, group_id):
|
||||||
|
try:
|
||||||
|
return self.client.show_security_group(security_group=group_id)
|
||||||
|
except neutron_exceptions.NeutronClientException as e:
|
||||||
|
LOG.exception('Failed to get remote security group')
|
||||||
|
raise exception.TroveError(str(e))
|
||||||
|
|
||||||
|
def create_security_group(self, name, description):
|
||||||
|
try:
|
||||||
|
sec_group_body = {"security_group": {"name": name,
|
||||||
|
"description": description}}
|
||||||
|
sec_group = self.client.create_security_group(body=sec_group_body)
|
||||||
|
return self._convert_to_nova_security_group_format(
|
||||||
|
sec_group.get('security_group', sec_group))
|
||||||
|
|
||||||
|
except neutron_exceptions.NeutronClientException as e:
|
||||||
|
LOG.exception('Failed to create remote security group')
|
||||||
|
raise exception.SecurityGroupCreationError(str(e))
|
||||||
|
|
||||||
|
def delete_security_group(self, sec_group_id):
|
||||||
|
try:
|
||||||
|
self.client.delete_security_group(security_group=sec_group_id)
|
||||||
|
except neutron_exceptions.NeutronClientException as e:
|
||||||
|
LOG.exception('Failed to delete remote security group')
|
||||||
|
raise exception.SecurityGroupDeletionError(str(e))
|
||||||
|
|
||||||
|
def add_security_group_rule(self, sec_group_id, protocol,
|
||||||
|
from_port, to_port, cidr,
|
||||||
|
direction=CONST['INGRESS'],
|
||||||
|
ethertype=CONST['IPv4']):
|
||||||
|
try:
|
||||||
|
secgroup_rule_body = {"security_group_rule":
|
||||||
|
{"security_group_id": sec_group_id,
|
||||||
|
"protocol": protocol,
|
||||||
|
"port_range_min": from_port,
|
||||||
|
"port_range_max": to_port,
|
||||||
|
"remote_ip_prefix": cidr,
|
||||||
|
"direction": direction, # ingress | egress
|
||||||
|
"ethertype": ethertype, # IPv4 | IPv6
|
||||||
|
}}
|
||||||
|
|
||||||
|
secgroup_rule = self.client.create_security_group_rule(
|
||||||
|
secgroup_rule_body)
|
||||||
|
return self._convert_to_nova_security_group_rule_format(
|
||||||
|
secgroup_rule.get('security_group_rule', secgroup_rule))
|
||||||
|
except neutron_exceptions.NeutronClientException as e:
|
||||||
|
# ignore error if rule already exists
|
||||||
|
if e.status_code == 409:
|
||||||
|
LOG.exception("secgroup rule already exists")
|
||||||
|
else:
|
||||||
|
LOG.exception('Failed to add rule to remote security group')
|
||||||
|
raise exception.SecurityGroupRuleCreationError(str(e))
|
||||||
|
|
||||||
|
def delete_security_group_rule(self, sec_group_rule_id):
|
||||||
|
try:
|
||||||
|
self.client.delete_security_group_rule(
|
||||||
|
security_group_rule=sec_group_rule_id)
|
||||||
|
|
||||||
|
except neutron_exceptions.NeutronClientException as e:
|
||||||
|
LOG.exception('Failed to delete rule to remote security group')
|
||||||
|
raise exception.SecurityGroupRuleDeletionError(str(e))
|
||||||
|
|
||||||
|
def _convert_to_nova_security_group_format(self, security_group):
|
||||||
|
nova_group = {}
|
||||||
|
nova_group['id'] = security_group['id']
|
||||||
|
nova_group['description'] = security_group['description']
|
||||||
|
nova_group['name'] = security_group['name']
|
||||||
|
nova_group['project_id'] = security_group['tenant_id']
|
||||||
|
nova_group['rules'] = []
|
||||||
|
for rule in security_group.get('security_group_rules', []):
|
||||||
|
if rule['direction'] == 'ingress':
|
||||||
|
nova_group['rules'].append(
|
||||||
|
self._convert_to_nova_security_group_rule_format(rule))
|
||||||
|
|
||||||
|
return NovaNetworkStruct(**nova_group)
|
||||||
|
|
||||||
|
def _convert_to_nova_security_group_rule_format(self, rule):
|
||||||
|
nova_rule = {}
|
||||||
|
nova_rule['id'] = rule['id']
|
||||||
|
nova_rule['parent_group_id'] = rule['security_group_id']
|
||||||
|
nova_rule['protocol'] = rule['protocol']
|
||||||
|
if (nova_rule['protocol'] and rule.get('port_range_min') is None and
|
||||||
|
rule.get('port_range_max') is None):
|
||||||
|
if rule['protocol'].upper() in ['TCP', 'UDP']:
|
||||||
|
nova_rule['from_port'] = 1
|
||||||
|
nova_rule['to_port'] = 65535
|
||||||
|
else:
|
||||||
|
nova_rule['from_port'] = -1
|
||||||
|
nova_rule['to_port'] = -1
|
||||||
|
else:
|
||||||
|
nova_rule['from_port'] = rule.get('port_range_min')
|
||||||
|
nova_rule['to_port'] = rule.get('port_range_max')
|
||||||
|
nova_rule['group_id'] = rule['remote_group_id']
|
||||||
|
nova_rule['cidr'] = rule.get('remote_ip_prefix')
|
||||||
|
return NovaNetworkStruct(**nova_rule)
|
80
trove/network/nova.py
Normal file
80
trove/network/nova.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# Copyright 2014 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 trove.common import exception
|
||||||
|
from trove.common import remote
|
||||||
|
from trove.network import base
|
||||||
|
from trove.openstack.common import log as logging
|
||||||
|
from novaclient import exceptions as nova_exceptions
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class NovaNetwork(base.NetworkDriver):
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
try:
|
||||||
|
self.client = remote.create_nova_client(
|
||||||
|
context)
|
||||||
|
except nova_exceptions.ClientException as e:
|
||||||
|
raise exception.TroveError(str(e))
|
||||||
|
|
||||||
|
def get_sec_group_by_id(self, group_id):
|
||||||
|
try:
|
||||||
|
return self.client.security_groups.get(group_id)
|
||||||
|
except nova_exceptions.ClientException as e:
|
||||||
|
LOG.exception('Failed to get remote security group')
|
||||||
|
raise exception.TroveError(str(e))
|
||||||
|
|
||||||
|
def create_security_group(self, name, description):
|
||||||
|
try:
|
||||||
|
sec_group = self.client.security_groups.create(
|
||||||
|
name=name, description=description)
|
||||||
|
return sec_group
|
||||||
|
except nova_exceptions.ClientException as e:
|
||||||
|
LOG.exception('Failed to create remote security group')
|
||||||
|
raise exception.SecurityGroupCreationError(str(e))
|
||||||
|
|
||||||
|
def delete_security_group(self, sec_group_id):
|
||||||
|
try:
|
||||||
|
self.client.security_groups.delete(sec_group_id)
|
||||||
|
except nova_exceptions.ClientException as e:
|
||||||
|
LOG.exception('Failed to delete remote security group')
|
||||||
|
raise exception.SecurityGroupDeletionError(str(e))
|
||||||
|
|
||||||
|
def add_security_group_rule(self, sec_group_id, protocol,
|
||||||
|
from_port, to_port, cidr):
|
||||||
|
try:
|
||||||
|
sec_group_rule = self.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
|
||||||
|
except nova_exceptions.ClientException as e:
|
||||||
|
LOG.exception('Failed to add rule to remote security group')
|
||||||
|
raise exception.SecurityGroupRuleCreationError(str(e))
|
||||||
|
|
||||||
|
def delete_security_group_rule(self, sec_group_rule_id):
|
||||||
|
try:
|
||||||
|
self.client.security_group_rules.delete(sec_group_rule_id)
|
||||||
|
|
||||||
|
except nova_exceptions.ClientException as e:
|
||||||
|
LOG.exception('Failed to delete rule to remote security group')
|
||||||
|
raise exception.SecurityGroupRuleDeletionError(str(e))
|
@ -20,6 +20,7 @@ import unittest
|
|||||||
|
|
||||||
|
|
||||||
GROUP = "dbaas.guest"
|
GROUP = "dbaas.guest"
|
||||||
|
GROUP_NEUTRON = "dbaas.neutron"
|
||||||
GROUP_START = "dbaas.guest.initialize"
|
GROUP_START = "dbaas.guest.initialize"
|
||||||
GROUP_START_SIMPLE = "dbaas.guest.initialize.simple"
|
GROUP_START_SIMPLE = "dbaas.guest.initialize.simple"
|
||||||
GROUP_TEST = "dbaas.guest.test"
|
GROUP_TEST = "dbaas.guest.test"
|
||||||
@ -46,6 +47,7 @@ from proboscis import after_class
|
|||||||
from proboscis import test
|
from proboscis import test
|
||||||
from proboscis import SkipTest
|
from proboscis import SkipTest
|
||||||
from proboscis.asserts import assert_equal
|
from proboscis.asserts import assert_equal
|
||||||
|
from proboscis.asserts import assert_false
|
||||||
from proboscis.asserts import assert_not_equal
|
from proboscis.asserts import assert_not_equal
|
||||||
from proboscis.asserts import assert_raises
|
from proboscis.asserts import assert_raises
|
||||||
from proboscis.asserts import assert_is_not_none
|
from proboscis.asserts import assert_is_not_none
|
||||||
@ -55,6 +57,7 @@ from proboscis.asserts import fail
|
|||||||
from trove import tests
|
from trove import tests
|
||||||
from trove.tests.config import CONFIG
|
from trove.tests.config import CONFIG
|
||||||
from trove.tests.util import create_dbaas_client
|
from trove.tests.util import create_dbaas_client
|
||||||
|
from trove.tests.util import create_nova_client
|
||||||
from trove.tests.util.usage import create_usage_verifier
|
from trove.tests.util.usage import create_usage_verifier
|
||||||
from trove.tests.util import dns_checker
|
from trove.tests.util import dns_checker
|
||||||
from trove.tests.util import iso_time
|
from trove.tests.util import iso_time
|
||||||
@ -677,6 +680,89 @@ class CreateInstance(object):
|
|||||||
check.volume()
|
check.volume()
|
||||||
|
|
||||||
|
|
||||||
|
@test(depends_on_classes=[InstanceSetup], groups=[GROUP_NEUTRON])
|
||||||
|
class CreateInstanceWithNeutron(unittest.TestCase):
|
||||||
|
@time_out(TIMEOUT_INSTANCE_CREATE)
|
||||||
|
def setUp(self):
|
||||||
|
if not CONFIG.values.get('neutron_enabled'):
|
||||||
|
raise SkipTest("neutron is not enabled, skipping")
|
||||||
|
|
||||||
|
user = test_config.users.find_user(
|
||||||
|
Requirements(is_admin=False, services=["nova", "trove"]))
|
||||||
|
self.nova_client = create_nova_client(user)
|
||||||
|
self.dbaas_client = create_dbaas_client(user)
|
||||||
|
|
||||||
|
self.result = None
|
||||||
|
self.instance_name = ("TEST_INSTANCE_CREATION_WITH_NICS"
|
||||||
|
+ str(datetime.now()))
|
||||||
|
databases = []
|
||||||
|
self.default_cidr = CONFIG.values.get('shared_network_subnet', None)
|
||||||
|
if VOLUME_SUPPORT:
|
||||||
|
volume = {'size': 1}
|
||||||
|
else:
|
||||||
|
volume = None
|
||||||
|
|
||||||
|
self.result = self.dbaas_client.instances.create(
|
||||||
|
self.instance_name,
|
||||||
|
instance_info.dbaas_flavor_href,
|
||||||
|
volume, databases)
|
||||||
|
self.instance_id = self.result.id
|
||||||
|
|
||||||
|
def verify_instance_is_active():
|
||||||
|
result = self.dbaas_client.instances.get(self.instance_id)
|
||||||
|
if result.status == "ACTIVE":
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
assert_equal("BUILD", result.status)
|
||||||
|
return False
|
||||||
|
|
||||||
|
poll_until(verify_instance_is_active)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if self.result.id is not None:
|
||||||
|
self.dbaas_client.instances.delete(self.result.id)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
self.dbaas_client.instances.get(self.result.id)
|
||||||
|
except exceptions.NotFound:
|
||||||
|
return True
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def check_ip_within_network(self, ip, network):
|
||||||
|
octet_list = str(ip).split(".")
|
||||||
|
|
||||||
|
octets, mask = str(network).split("/")
|
||||||
|
octet_list_ = octets.split(".")
|
||||||
|
|
||||||
|
for i in range(int(mask) / 8):
|
||||||
|
if octet_list[i] != octet_list_[i]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def test_ip_within_cidr(self):
|
||||||
|
nova_instance = None
|
||||||
|
for server in self.nova_client.servers.list():
|
||||||
|
if str(server.name) == self.instance_name:
|
||||||
|
nova_instance = server
|
||||||
|
break
|
||||||
|
|
||||||
|
if nova_instance is None:
|
||||||
|
fail("instance created with neutron enabled is not found in nova")
|
||||||
|
|
||||||
|
for address in nova_instance.addresses['private']:
|
||||||
|
ip = address['addr']
|
||||||
|
assert_true(self.check_ip_within_network(ip, self.default_cidr))
|
||||||
|
|
||||||
|
# black list filtered ip not visible via troveclient
|
||||||
|
trove_instance = self.dbaas_client.instances.get(self.result.id)
|
||||||
|
|
||||||
|
for ip in trove_instance.ip:
|
||||||
|
if str(ip).startswith('10.'):
|
||||||
|
assert_true(self.check_ip_within_network(ip, "10.0.0.0/24"))
|
||||||
|
assert_false(self.check_ip_within_network(ip, "10.0.1.0/24"))
|
||||||
|
|
||||||
|
|
||||||
@test(depends_on_classes=[CreateInstance],
|
@test(depends_on_classes=[CreateInstance],
|
||||||
groups=[GROUP,
|
groups=[GROUP,
|
||||||
GROUP_START,
|
GROUP_START,
|
||||||
|
@ -289,9 +289,11 @@ class FakeServers(object):
|
|||||||
raise nova_exceptions.ClientException("The requested availability "
|
raise nova_exceptions.ClientException("The requested availability "
|
||||||
"zone is not available.")
|
"zone is not available.")
|
||||||
|
|
||||||
if nics is not None and nics.port_id == 'UNKNOWN':
|
if nics:
|
||||||
raise nova_exceptions.ClientException("The requested availability "
|
if 'port-id' in nics[0] and nics[0]['port-id'] == "UNKNOWN":
|
||||||
"zone is not available.")
|
raise nova_exceptions.ClientException("The requested "
|
||||||
|
"port-id is not "
|
||||||
|
"available.")
|
||||||
|
|
||||||
server.schedule_status("ACTIVE", 1)
|
server.schedule_status("ACTIVE", 1)
|
||||||
LOG.info(_("FAKE_SERVERS_DB : %s") % str(FAKE_SERVERS_DB))
|
LOG.info(_("FAKE_SERVERS_DB : %s") % str(FAKE_SERVERS_DB))
|
||||||
|
@ -40,30 +40,35 @@ class SimpleInstanceTest(TestCase):
|
|||||||
"public": [{"addr": "15.123.123.123"}]}
|
"public": [{"addr": "15.123.123.123"}]}
|
||||||
self.orig_conf = CONF.network_label_regex
|
self.orig_conf = CONF.network_label_regex
|
||||||
self.orig_ip_regex = CONF.ip_regex
|
self.orig_ip_regex = CONF.ip_regex
|
||||||
|
self.orig_black_list_regex = CONF.black_list_regex
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(SimpleInstanceTest, self).tearDown()
|
super(SimpleInstanceTest, self).tearDown()
|
||||||
CONF.network_label_regex = self.orig_conf
|
CONF.network_label_regex = self.orig_conf
|
||||||
CONF.ip_start = None
|
CONF.ip_start = None
|
||||||
CONF.ip_regex = self.orig_ip_regex
|
|
||||||
|
|
||||||
def test_get_root_on_create(self):
|
def test_get_root_on_create(self):
|
||||||
root_on_create_val = Instance.get_root_on_create('redis')
|
root_on_create_val = Instance.get_root_on_create('redis')
|
||||||
self.assertFalse(root_on_create_val)
|
self.assertFalse(root_on_create_val)
|
||||||
|
|
||||||
def test_filter_ips(self):
|
def test_filter_ips_white_list(self):
|
||||||
CONF.network_label_regex = '.*'
|
CONF.network_label_regex = '.*'
|
||||||
CONF.ip_regex = '^(15.|123.)'
|
CONF.ip_regex = '^(15.|123.)'
|
||||||
|
CONF.black_list_regex = '^10.123.123.*'
|
||||||
ip = self.instance.get_visible_ip_addresses()
|
ip = self.instance.get_visible_ip_addresses()
|
||||||
ip = filter_ips(ip, CONF.ip_regex)
|
ip = filter_ips(ip, CONF.ip_regex, CONF.black_list_regex)
|
||||||
self.assertTrue(len(ip) == 2)
|
self.assertTrue(len(ip) == 2)
|
||||||
self.assertTrue('123.123.123.123' in ip)
|
self.assertTrue('123.123.123.123' in ip)
|
||||||
self.assertTrue('15.123.123.123' in ip)
|
self.assertTrue('15.123.123.123' in ip)
|
||||||
|
|
||||||
def test_one_network_label_exact(self):
|
def test_filter_ips_black_list(self):
|
||||||
CONF.network_label_regex = '^internal$'
|
CONF.network_label_regex = '.*'
|
||||||
|
CONF.ip_regex = '.*'
|
||||||
|
CONF.black_list_regex = '^10.123.123.*'
|
||||||
ip = self.instance.get_visible_ip_addresses()
|
ip = self.instance.get_visible_ip_addresses()
|
||||||
self.assertEqual(['10.123.123.123'], ip)
|
ip = filter_ips(ip, CONF.ip_regex, CONF.black_list_regex)
|
||||||
|
self.assertTrue(len(ip) == 2)
|
||||||
|
self.assertTrue('10.123.123.123' not in ip)
|
||||||
|
|
||||||
def test_one_network_label(self):
|
def test_one_network_label(self):
|
||||||
CONF.network_label_regex = 'public'
|
CONF.network_label_regex = 'public'
|
||||||
|
0
trove/tests/unittests/network/__init__.py
Normal file
0
trove/tests/unittests/network/__init__.py
Normal file
115
trove/tests/unittests/network/test_neutron_driver.py
Normal file
115
trove/tests/unittests/network/test_neutron_driver.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# Copyright 2014 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
import testtools
|
||||||
|
from mock import MagicMock
|
||||||
|
from mock import Mock
|
||||||
|
from neutronclient.common import exceptions as neutron_exceptions
|
||||||
|
from neutronclient.v2_0 import client as NeutronClient
|
||||||
|
from trove.common import exception
|
||||||
|
from trove.common import remote
|
||||||
|
from trove.common.models import NetworkRemoteModelBase
|
||||||
|
from trove.network import neutron
|
||||||
|
from trove.network.neutron import NeutronDriver as driver
|
||||||
|
from trove.extensions.security_group.models import RemoteSecurityGroup
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronDriverTest(testtools.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(NeutronDriverTest, self).setUp()
|
||||||
|
self.context = Mock()
|
||||||
|
self.orig_neutron_driver = NetworkRemoteModelBase.get_driver
|
||||||
|
self.orig_create_sg = driver.create_security_group
|
||||||
|
self.orig_add_sg_rule = driver.add_security_group_rule
|
||||||
|
self.orig_del_sg_rule = driver.delete_security_group_rule
|
||||||
|
self.orig_del_sg = driver.delete_security_group
|
||||||
|
NetworkRemoteModelBase.get_driver = Mock(return_value=driver)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(NeutronDriverTest, self).tearDown()
|
||||||
|
NetworkRemoteModelBase.get_driver = self.orig_neutron_driver
|
||||||
|
driver.create_security_group = self.orig_create_sg
|
||||||
|
driver.add_security_group_rule = self.orig_add_sg_rule
|
||||||
|
driver.delete_security_group_rule = self.orig_del_sg_rule
|
||||||
|
driver.delete_security_group = self.orig_del_sg
|
||||||
|
|
||||||
|
def test_create_security_group(self):
|
||||||
|
driver.create_security_group = Mock()
|
||||||
|
RemoteSecurityGroup.create(name=Mock(), description=Mock(),
|
||||||
|
context=self.context)
|
||||||
|
self.assertEqual(1, driver.create_security_group.call_count)
|
||||||
|
|
||||||
|
def test_add_security_group_rule(self):
|
||||||
|
driver.add_security_group_rule = Mock()
|
||||||
|
RemoteSecurityGroup.add_rule(sec_group_id=Mock(), protocol=Mock(),
|
||||||
|
from_port=Mock(), to_port=Mock(),
|
||||||
|
cidr=Mock(), context=self.context)
|
||||||
|
self.assertEqual(1, driver.add_security_group_rule.call_count)
|
||||||
|
|
||||||
|
def test_delete_security_group_rule(self):
|
||||||
|
driver.delete_security_group_rule = Mock()
|
||||||
|
RemoteSecurityGroup.delete_rule(sec_group_rule_id=Mock(),
|
||||||
|
context=self.context)
|
||||||
|
self.assertEqual(1, driver.delete_security_group_rule.call_count)
|
||||||
|
|
||||||
|
def test_delete_security_group(self):
|
||||||
|
driver.delete_security_group = Mock()
|
||||||
|
RemoteSecurityGroup.delete(sec_group_id=Mock(),
|
||||||
|
context=self.context)
|
||||||
|
self.assertEqual(1, driver.delete_security_group.call_count)
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronDriverExceptionTest(testtools.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(NeutronDriverExceptionTest, self).setUp()
|
||||||
|
self.context = Mock()
|
||||||
|
self.orig_neutron_driver = NetworkRemoteModelBase.get_driver
|
||||||
|
self.orig_NeutronClient = NeutronClient.Client
|
||||||
|
self.orig_get_endpoint = remote.get_endpoint
|
||||||
|
remote.get_endpoint = MagicMock(return_value="neutron_url")
|
||||||
|
mock_driver = neutron.NeutronDriver(self.context)
|
||||||
|
NetworkRemoteModelBase.get_driver = MagicMock(
|
||||||
|
return_value=mock_driver)
|
||||||
|
|
||||||
|
NeutronClient.Client = Mock(
|
||||||
|
side_effect=neutron_exceptions.NeutronClientException())
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(NeutronDriverExceptionTest, self).tearDown()
|
||||||
|
NetworkRemoteModelBase.get_driver = self.orig_neutron_driver
|
||||||
|
NeutronClient.Client = self.orig_NeutronClient
|
||||||
|
remote.get_endpoint = self.orig_get_endpoint
|
||||||
|
|
||||||
|
def test_create_sg_with_exception(self):
|
||||||
|
self.assertRaises(exception.SecurityGroupCreationError,
|
||||||
|
RemoteSecurityGroup.create,
|
||||||
|
"sg_name", "sg_desc", self.context)
|
||||||
|
|
||||||
|
def test_add_sg_rule_with_exception(self):
|
||||||
|
self.assertRaises(exception.SecurityGroupRuleCreationError,
|
||||||
|
RemoteSecurityGroup.add_rule,
|
||||||
|
"12234", "tcp", "22", "22",
|
||||||
|
"0.0.0.0/8", self.context)
|
||||||
|
|
||||||
|
def test_delete_sg_rule_with_exception(self):
|
||||||
|
self.assertRaises(exception.SecurityGroupRuleDeletionError,
|
||||||
|
RemoteSecurityGroup.delete_rule,
|
||||||
|
"12234", self.context)
|
||||||
|
|
||||||
|
def test_delete_sg_with_exception(self):
|
||||||
|
self.assertRaises(exception.SecurityGroupDeletionError,
|
||||||
|
RemoteSecurityGroup.delete,
|
||||||
|
"123445", self.context)
|
Loading…
Reference in New Issue
Block a user