Implements: blueprint anti-affinity server group

https://blueprints.launchpad.net/octavia/+spec/anti-affinity
Added a new column in lb table for server group id;
Added a new task in compute tasks for creating server group;
Added a new task in dtabase tasks to update server
group id info for lb;
Add server group id in create method in nova driver to support
anti-affinity when creating compute instance

Change-Id: If0d3a9ba1012651937a2bda9bc95ab4f4c8852d5
This commit is contained in:
minwang 2016-02-18 10:33:25 -08:00
parent dd542b1080
commit 07a608f681
25 changed files with 518 additions and 47 deletions

View File

@ -231,6 +231,10 @@
# the OpenStack services. # the OpenStack services.
# endpoint_type = publicURL # endpoint_type = publicURL
# Flag to enable nova anti-affinity capabilities to place amphorae on
# different hosts
# enable_anti_affinity = False
[neutron] [neutron]
# The name of the neutron service in the keystone catalog # The name of the neutron service in the keystone catalog
# service_name = # service_name =

View File

@ -359,6 +359,9 @@ nova_opts = [
'communication with the OpenStack services.')), 'communication with the OpenStack services.')),
cfg.StrOpt('endpoint_type', default='publicURL', cfg.StrOpt('endpoint_type', default='publicURL',
help=_('Endpoint interface in identity service to use')), help=_('Endpoint interface in identity service to use')),
cfg.BoolOpt('enable_anti_affinity', default=False,
help=_('Flag to indicate if nova anti-affinity feature is '
'turned on.'))
] ]
neutron_opts = [ neutron_opts = [

View File

@ -139,6 +139,8 @@ LISTENER = 'listener'
LISTENERS = 'listeners' LISTENERS = 'listeners'
LOADBALANCER = 'loadbalancer' LOADBALANCER = 'loadbalancer'
LOADBALANCER_ID = 'loadbalancer_id' LOADBALANCER_ID = 'loadbalancer_id'
SERVER_GROUP_ID = 'server_group_id'
ANTI_AFFINITY = 'anti-affinity'
MEMBER = 'member' MEMBER = 'member'
MEMBER_ID = 'member_id' MEMBER_ID = 'member_id'
COMPUTE_ID = 'compute_id' COMPUTE_ID = 'compute_id'
@ -166,6 +168,9 @@ CREATE_AMPHORA_FLOW = 'octavia-create-amphora-flow'
CREATE_AMPHORA_FOR_LB_FLOW = 'octavia-create-amp-for-lb-flow' CREATE_AMPHORA_FOR_LB_FLOW = 'octavia-create-amp-for-lb-flow'
CREATE_HEALTH_MONITOR_FLOW = 'octavia-create-health-monitor-flow' CREATE_HEALTH_MONITOR_FLOW = 'octavia-create-health-monitor-flow'
CREATE_LISTENER_FLOW = 'octavia-create-listener_flow' CREATE_LISTENER_FLOW = 'octavia-create-listener_flow'
PRE_CREATE_LOADBALANCER_FLOW = 'octavia-pre-create-loadbalancer-flow'
CREATE_SERVER_GROUP_FLOW = 'octavia-create-server-group-flow'
UPDATE_LB_SERVERGROUPID_FLOW = 'octavia-update-lb-server-group-id-flow'
CREATE_LOADBALANCER_FLOW = 'octavia-create-loadbalancer-flow' CREATE_LOADBALANCER_FLOW = 'octavia-create-loadbalancer-flow'
CREATE_MEMBER_FLOW = 'octavia-create-member-flow' CREATE_MEMBER_FLOW = 'octavia-create-member-flow'
CREATE_POOL_FLOW = 'octavia-create-pool-flow' CREATE_POOL_FLOW = 'octavia-create-pool-flow'
@ -227,7 +232,6 @@ CREATE_VRRP_SECURITY_RULES = 'octavia-create-vrrp-security-rules'
GENERATE_SERVER_PEM_TASK = 'GenerateServerPEMTask' GENERATE_SERVER_PEM_TASK = 'GenerateServerPEMTask'
# Task Names # Task Names
RELOAD_LB_AFTER_AMP_ASSOC = 'reload-lb-after-amp-assoc' RELOAD_LB_AFTER_AMP_ASSOC = 'reload-lb-after-amp-assoc'
RELOAD_LB_AFTER_PLUG_VIP = 'reload-lb-after-plug-vip' RELOAD_LB_AFTER_PLUG_VIP = 'reload-lb-after-plug-vip'

View File

@ -310,7 +310,8 @@ class LoadBalancer(BaseDataModel):
def __init__(self, id=None, project_id=None, name=None, description=None, def __init__(self, id=None, project_id=None, name=None, description=None,
provisioning_status=None, operating_status=None, enabled=None, provisioning_status=None, operating_status=None, enabled=None,
topology=None, vip=None, listeners=None, amphorae=None, topology=None, vip=None, listeners=None, amphorae=None,
pools=None, vrrp_group=None): pools=None, vrrp_group=None, server_group_id=None):
self.id = id self.id = id
self.project_id = project_id self.project_id = project_id
self.name = name self.name = name
@ -324,6 +325,7 @@ class LoadBalancer(BaseDataModel):
self.listeners = listeners or [] self.listeners = listeners or []
self.amphorae = amphorae or [] self.amphorae = amphorae or []
self.pools = pools or [] self.pools = pools or []
self.server_group_id = server_group_id
class VRRPGroup(BaseDataModel): class VRRPGroup(BaseDataModel):

View File

@ -217,3 +217,11 @@ class InvalidRegex(OctaviaException):
class InvalidL7Rule(OctaviaException): class InvalidL7Rule(OctaviaException):
message = _LE('Invalid L7 Rule: $(msg)s') message = _LE('Invalid L7 Rule: $(msg)s')
class ServerGroupObjectCreateException(OctaviaException):
message = _LE('Failed to create server group object.')
class ServerGroupObjectDeleteException(OctaviaException):
message = _LE('Failed to delete server group object.')

View File

@ -23,7 +23,7 @@ class ComputeBase(object):
@abc.abstractmethod @abc.abstractmethod
def build(self, name="amphora_name", amphora_flavor=None, image_id=None, def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
key_name=None, sec_groups=None, network_ids=None, key_name=None, sec_groups=None, network_ids=None,
config_drive_files=None, user_data=None): config_drive_files=None, user_data=None, server_group_id=None):
"""Build a new amphora. """Build a new amphora.
:param name: Optional name for Amphora :param name: Optional name for Amphora
@ -40,6 +40,8 @@ class ComputeBase(object):
:param user_data: Optional user data to pass to be exposed by the :param user_data: Optional user data to pass to be exposed by the
metadata server this can be a file type object as well or metadata server this can be a file type object as well or
a string a string
:param server_group_id: Optional server group id(uuid) which is used
for anti_affinity feature
:raises ComputeBuildException: if compute failed to build amphora :raises ComputeBuildException: if compute failed to build amphora
:returns: UUID of amphora :returns: UUID of amphora
@ -71,3 +73,21 @@ class ComputeBase(object):
:returns: the amphora object :returns: the amphora object
""" """
pass pass
@abc.abstractmethod
def create_server_group(self, name, policy):
"""Create a server group object
:param name: the name of the server group
:param policy: the policy of the server group
:returns: the server group object
"""
pass
@abc.abstractmethod
def delete_server_group(self, server_group_id):
"""Delete a server group object
:param server_group_id: the uuid of a server group
"""
pass

View File

@ -29,13 +29,15 @@ class NoopManager(object):
def build(self, name="amphora_name", amphora_flavor=None, image_id=None, def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
key_name=None, sec_groups=None, network_ids=None, key_name=None, sec_groups=None, network_ids=None,
config_drive_files=None, user_data=None, port_ids=None): config_drive_files=None, user_data=None, port_ids=None,
server_group_id=None):
LOG.debug("Compute %s no-op, build name %s, amphora_flavor %s, " LOG.debug("Compute %s no-op, build name %s, amphora_flavor %s, "
"image_id %s, key_name %s, sec_groups %s, network_ids %s," "image_id %s, key_name %s, sec_groups %s, network_ids %s,"
"config_drive_files %s, user_data %s, port_ids %s", "config_drive_files %s, user_data %s, port_ids %s,"
"server_group_id %s",
self.__class__.__name__, name, amphora_flavor, image_id, self.__class__.__name__, name, amphora_flavor, image_id,
key_name, sec_groups, network_ids, config_drive_files, key_name, sec_groups, network_ids, config_drive_files,
user_data, port_ids) user_data, port_ids, server_group_id)
self.computeconfig[(name, amphora_flavor, image_id, key_name, self.computeconfig[(name, amphora_flavor, image_id, key_name,
user_data)] = ( user_data)] = (
name, amphora_flavor, name, amphora_flavor,
@ -66,6 +68,16 @@ class NoopManager(object):
lb_network_ip='192.0.2.1' lb_network_ip='192.0.2.1'
) )
def create_server_group(self, name, policy):
LOG.debug("Create Server Group %s no-op, name %s, policy %s ",
self.__class__.__name__, name, policy)
self.computeconfig[(name, policy)] = (name, policy, 'create')
def delete_server_group(self, server_group_id):
LOG.debug("Delete Server Group %s no-op, id %s ",
self.__class__.__name__, server_group_id)
self.computeconfig[server_group_id] = (server_group_id, 'delete')
class NoopComputeDriver(driver_base.ComputeBase): class NoopComputeDriver(driver_base.ComputeBase):
def __init__(self): def __init__(self):
@ -74,11 +86,13 @@ class NoopComputeDriver(driver_base.ComputeBase):
def build(self, name="amphora_name", amphora_flavor=None, image_id=None, def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
key_name=None, sec_groups=None, network_ids=None, key_name=None, sec_groups=None, network_ids=None,
config_drive_files=None, user_data=None, port_ids=None): config_drive_files=None, user_data=None, port_ids=None,
server_group_id=None):
compute_id = self.driver.build(name, amphora_flavor, image_id, compute_id = self.driver.build(name, amphora_flavor, image_id,
key_name, sec_groups, network_ids, key_name, sec_groups, network_ids,
config_drive_files, user_data, port_ids) config_drive_files, user_data, port_ids,
server_group_id)
return compute_id return compute_id
def delete(self, compute_id): def delete(self, compute_id):
@ -89,3 +103,9 @@ class NoopComputeDriver(driver_base.ComputeBase):
def get_amphora(self, compute_id): def get_amphora(self, compute_id):
return self.driver.get_amphora(compute_id) return self.driver.get_amphora(compute_id)
def create_server_group(self, name, policy):
return self.driver.create_server_group(name, policy)
def delete_server_group(self, server_group_id):
self.driver.delete_server_group(server_group_id)

View File

@ -42,10 +42,12 @@ class VirtualMachineManager(compute_base.ComputeBase):
region=CONF.nova.region_name, region=CONF.nova.region_name,
endpoint_type=CONF.nova.endpoint_type) endpoint_type=CONF.nova.endpoint_type)
self.manager = self._nova_client.servers self.manager = self._nova_client.servers
self.server_groups = self._nova_client.server_groups
def build(self, name="amphora_name", amphora_flavor=None, image_id=None, def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
key_name=None, sec_groups=None, network_ids=None, key_name=None, sec_groups=None, network_ids=None,
port_ids=None, config_drive_files=None, user_data=None): port_ids=None, config_drive_files=None, user_data=None,
server_group_id=None):
'''Create a new virtual machine. '''Create a new virtual machine.
:param name: optional name for amphora :param name: optional name for amphora
@ -63,6 +65,8 @@ class VirtualMachineManager(compute_base.ComputeBase):
:param user_data: Optional user data to pass to be exposed by the :param user_data: Optional user data to pass to be exposed by the
metadata server this can be a file type object as well or metadata server this can be a file type object as well or
a string a string
:param server_group_id: Optional server group id(uuid) which is used
for anti_affinity feature
:raises ComputeBuildException: if nova failed to build virtual machine :raises ComputeBuildException: if nova failed to build virtual machine
:returns: UUID of amphora :returns: UUID of amphora
@ -77,13 +81,17 @@ class VirtualMachineManager(compute_base.ComputeBase):
if port_ids: if port_ids:
nics.extend([{"port-id": port_id} for port_id in port_ids]) nics.extend([{"port-id": port_id} for port_id in port_ids])
server_group = None if server_group_id is None else {
"group": server_group_id}
amphora = self.manager.create( amphora = self.manager.create(
name=name, image=image_id, flavor=amphora_flavor, name=name, image=image_id, flavor=amphora_flavor,
key_name=key_name, security_groups=sec_groups, key_name=key_name, security_groups=sec_groups,
nics=nics, nics=nics,
files=config_drive_files, files=config_drive_files,
userdata=user_data, userdata=user_data,
config_drive=True config_drive=True,
scheduler_hints=server_group
) )
return amphora.id return amphora.id
@ -175,3 +183,36 @@ class VirtualMachineManager(compute_base.ComputeBase):
lb_network_ip=lb_network_ip lb_network_ip=lb_network_ip
) )
return response return response
def create_server_group(self, name, policy):
"""Create a server group object
:param name: the name of the server group
:param policy: the policy of the server group
:raises: Generic exception if the server group is not created
:returns: the server group object
"""
kwargs = {'name': name,
'policies': [policy]}
try:
server_group_obj = self.server_groups.create(**kwargs)
return server_group_obj
except Exception:
LOG.exception(_LE("Error create server group instance."))
raise exceptions.ServerGroupObjectCreateException()
def delete_server_group(self, server_group_id):
"""Delete a server group object
:raises: Generic exception if the server group is not deleted
:param server_group_id: the uuid of a server group
"""
try:
self.server_groups.delete(server_group_id)
except nova_exceptions.NotFound:
LOG.warn(_LW("Server group instance with id: %s not found. "
"Assuming already deleted."), server_group_id)
except Exception:
LOG.exception(_LE("Error delete server group instance."))
raise exceptions.ServerGroupObjectDeleteException()

View File

@ -310,9 +310,12 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
lb = self._lb_repo.get(db_apis.get_session(), lb = self._lb_repo.get(db_apis.get_session(),
id=load_balancer_id) id=load_balancer_id)
store = {constants.LOADBALANCER: lb,
constants.SERVER_GROUP_ID: lb.server_group_id}
delete_lb_tf = self._taskflow_load(self._lb_flows. delete_lb_tf = self._taskflow_load(self._lb_flows.
get_delete_load_balancer_flow(), get_delete_load_balancer_flow(),
store={constants.LOADBALANCER: lb}) store=store)
with tf_logging.DynamicLoggingListener(delete_lb_tf, with tf_logging.DynamicLoggingListener(delete_lb_tf,
log=LOG): log=LOG):

View File

@ -26,6 +26,7 @@ from octavia.controller.worker.tasks import network_tasks
CONF = cfg.CONF CONF = cfg.CONF
CONF.import_group('controller_worker', 'octavia.common.config') CONF.import_group('controller_worker', 'octavia.common.config')
CONF.import_group('nova', 'octavia.common.config')
class AmphoraFlows(object): class AmphoraFlows(object):
@ -111,11 +112,12 @@ class AmphoraFlows(object):
sf_name = prefix + '-' + constants.CREATE_AMP_FOR_LB_SUBFLOW sf_name = prefix + '-' + constants.CREATE_AMP_FOR_LB_SUBFLOW
create_amp_for_lb_subflow = linear_flow.Flow(sf_name) create_amp_for_lb_subflow = linear_flow.Flow(sf_name)
create_amp_for_lb_subflow.add(database_tasks.CreateAmphoraInDB( create_amp_for_lb_subflow.add(database_tasks.CreateAmphoraInDB(
name=sf_name + '-' + constants.CREATE_AMPHORA_INDB, name=sf_name + '-' + constants.CREATE_AMPHORA_INDB,
provides=constants.AMPHORA_ID)) provides=constants.AMPHORA_ID))
anti_affinity = CONF.nova.enable_anti_affinity
if self.REST_AMPHORA_DRIVER: if self.REST_AMPHORA_DRIVER:
create_amp_for_lb_subflow.add(cert_task.GenerateServerPEMTask( create_amp_for_lb_subflow.add(cert_task.GenerateServerPEMTask(
name=sf_name + '-' + constants.GENERATE_SERVER_PEM, name=sf_name + '-' + constants.GENERATE_SERVER_PEM,
@ -126,15 +128,31 @@ class AmphoraFlows(object):
name=sf_name + '-' + constants.UPDATE_CERT_EXPIRATION, name=sf_name + '-' + constants.UPDATE_CERT_EXPIRATION,
requires=(constants.AMPHORA_ID, constants.SERVER_PEM))) requires=(constants.AMPHORA_ID, constants.SERVER_PEM)))
create_amp_for_lb_subflow.add(compute_tasks.CertComputeCreate( if role in (constants.ROLE_BACKUP, constants.ROLE_MASTER
name=sf_name + '-' + constants.CERT_COMPUTE_CREATE, ) and anti_affinity:
requires=(constants.AMPHORA_ID, constants.SERVER_PEM), create_amp_for_lb_subflow.add(compute_tasks.CertComputeCreate(
provides=constants.COMPUTE_ID)) name=sf_name + '-' + constants.CERT_COMPUTE_CREATE,
requires=(constants.AMPHORA_ID, constants.SERVER_PEM,
constants.SERVER_GROUP_ID),
provides=constants.COMPUTE_ID))
else:
create_amp_for_lb_subflow.add(compute_tasks.CertComputeCreate(
name=sf_name + '-' + constants.CERT_COMPUTE_CREATE,
requires=(constants.AMPHORA_ID, constants.SERVER_PEM),
provides=constants.COMPUTE_ID))
else: else:
create_amp_for_lb_subflow.add(compute_tasks.ComputeCreate(
name=sf_name + '-' + constants.COMPUTE_CREATE, if role in (constants.ROLE_BACKUP, constants.ROLE_MASTER
requires=constants.AMPHORA_ID, ) and anti_affinity:
provides=constants.COMPUTE_ID)) create_amp_for_lb_subflow.add(compute_tasks.ComputeCreate(
name=sf_name + '-' + constants.COMPUTE_CREATE,
requires=(constants.AMPHORA_ID, constants.SERVER_GROUP_ID),
provides=constants.COMPUTE_ID))
else:
create_amp_for_lb_subflow.add(compute_tasks.ComputeCreate(
name=sf_name + '-' + constants.COMPUTE_CREATE,
requires=constants.AMPHORA_ID,
provides=constants.COMPUTE_ID))
create_amp_for_lb_subflow.add(database_tasks.UpdateAmphoraComputeId( create_amp_for_lb_subflow.add(database_tasks.UpdateAmphoraComputeId(
name=sf_name + '-' + constants.UPDATE_AMPHORA_COMPUTEID, name=sf_name + '-' + constants.UPDATE_AMPHORA_COMPUTEID,

View File

@ -31,6 +31,7 @@ from octavia.i18n import _LE
CONF = cfg.CONF CONF = cfg.CONF
CONF.import_group('controller_worker', 'octavia.common.config') CONF.import_group('controller_worker', 'octavia.common.config')
CONF.import_group('nova', 'octavia.common.config')
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -44,18 +45,46 @@ class LoadBalancerFlows(object):
two spare amphorae. two spare amphorae.
:raises InvalidTopology: Invalid topology specified :raises InvalidTopology: Invalid topology specified
:return: The graph flow for creating an active_standby loadbalancer. :return: The graph flow for creating a loadbalancer.
""" """
# create a linear flow as a wrapper
lf_name = constants.PRE_CREATE_LOADBALANCER_FLOW
create_lb_flow_wrapper = linear_flow.Flow(lf_name)
f_name = constants.CREATE_LOADBALANCER_FLOW f_name = constants.CREATE_LOADBALANCER_FLOW
lb_create_flow = unordered_flow.Flow(f_name) lb_create_flow = unordered_flow.Flow(f_name)
if topology == constants.TOPOLOGY_ACTIVE_STANDBY: if topology == constants.TOPOLOGY_ACTIVE_STANDBY:
# When we boot up amphora for an active/standby topology,
# we should leverage the Nova anti-affinity capabilities
# to place the amphora on different hosts, also we need to check
# if anti-affinity-flag is enabled or not:
anti_affinity = CONF.nova.enable_anti_affinity
if anti_affinity:
# we need to create a server group first
create_lb_flow_wrapper.add(
compute_tasks.NovaServerGroupCreate(
name=lf_name + '-' +
constants.CREATE_SERVER_GROUP_FLOW,
requires=(constants.LOADBALANCER_ID),
provides=constants.SERVER_GROUP_ID))
# update server group id in lb table
create_lb_flow_wrapper.add(
database_tasks.UpdateLBServerGroupInDB(
name=lf_name + '-' +
constants.UPDATE_LB_SERVERGROUPID_FLOW,
requires=(constants.LOADBALANCER_ID,
constants.SERVER_GROUP_ID)))
master_amp_sf = self.amp_flows.get_amphora_for_lb_subflow( master_amp_sf = self.amp_flows.get_amphora_for_lb_subflow(
prefix=constants.ROLE_MASTER, role=constants.ROLE_MASTER) prefix=constants.ROLE_MASTER, role=constants.ROLE_MASTER)
backup_amp_sf = self.amp_flows.get_amphora_for_lb_subflow( backup_amp_sf = self.amp_flows.get_amphora_for_lb_subflow(
prefix=constants.ROLE_BACKUP, role=constants.ROLE_BACKUP) prefix=constants.ROLE_BACKUP, role=constants.ROLE_BACKUP)
lb_create_flow.add(master_amp_sf, backup_amp_sf) lb_create_flow.add(master_amp_sf, backup_amp_sf)
elif topology == constants.TOPOLOGY_SINGLE: elif topology == constants.TOPOLOGY_SINGLE:
amphora_sf = self.amp_flows.get_amphora_for_lb_subflow( amphora_sf = self.amp_flows.get_amphora_for_lb_subflow(
prefix=constants.ROLE_STANDALONE, prefix=constants.ROLE_STANDALONE,
@ -66,7 +95,8 @@ class LoadBalancerFlows(object):
"balancer."), topology) "balancer."), topology)
raise exceptions.InvalidTopology(topology=topology) raise exceptions.InvalidTopology(topology=topology)
return lb_create_flow create_lb_flow_wrapper.add(lb_create_flow)
return create_lb_flow_wrapper
def get_post_lb_amp_association_flow(self, prefix, topology): def get_post_lb_amp_association_flow(self, prefix, topology):
"""Reload the loadbalancer and create networking subflows for """Reload the loadbalancer and create networking subflows for
@ -109,6 +139,8 @@ class LoadBalancerFlows(object):
:returns: The flow for deleting a load balancer :returns: The flow for deleting a load balancer
""" """
delete_LB_flow = linear_flow.Flow(constants.DELETE_LOADBALANCER_FLOW) delete_LB_flow = linear_flow.Flow(constants.DELETE_LOADBALANCER_FLOW)
delete_LB_flow.add(compute_tasks.NovaServerGroupDelete(
requires=constants.SERVER_GROUP_ID))
delete_LB_flow.add(database_tasks.MarkLBAmphoraeHealthBusy( delete_LB_flow.add(database_tasks.MarkLBAmphoraeHealthBusy(
requires=constants.LOADBALANCER)) requires=constants.LOADBALANCER))
delete_LB_flow.add(controller_tasks.DeleteListenersOnLB( delete_LB_flow.add(controller_tasks.DeleteListenersOnLB(

View File

@ -48,7 +48,8 @@ class BaseComputeTask(task.Task):
class ComputeCreate(BaseComputeTask): class ComputeCreate(BaseComputeTask):
"""Create the compute instance for a new amphora.""" """Create the compute instance for a new amphora."""
def execute(self, amphora_id, ports=None, config_drive_files=None): def execute(self, amphora_id, ports=None, config_drive_files=None,
server_group_id=None):
"""Create an amphora """Create an amphora
:returns: an amphora :returns: an amphora
@ -82,7 +83,8 @@ class ComputeCreate(BaseComputeTask):
network_ids=[CONF.controller_worker.amp_network], network_ids=[CONF.controller_worker.amp_network],
port_ids=[port.id for port in ports], port_ids=[port.id for port in ports],
config_drive_files=config_drive_files, config_drive_files=config_drive_files,
user_data=user_data) user_data=user_data,
server_group_id=server_group_id)
LOG.debug("Server created with id: %s for amphora id: %s", LOG.debug("Server created with id: %s for amphora id: %s",
compute_id, amphora_id) compute_id, amphora_id)
@ -111,7 +113,8 @@ class ComputeCreate(BaseComputeTask):
class CertComputeCreate(ComputeCreate): class CertComputeCreate(ComputeCreate):
def execute(self, amphora_id, server_pem, ports=None): def execute(self, amphora_id, server_pem, ports=None,
server_group_id=None):
"""Create an amphora """Create an amphora
:returns: an amphora :returns: an amphora
@ -125,7 +128,8 @@ class CertComputeCreate(ComputeCreate):
'/etc/octavia/certs/server.pem': server_pem, '/etc/octavia/certs/server.pem': server_pem,
'/etc/octavia/certs/client_ca.pem': ca} '/etc/octavia/certs/client_ca.pem': ca}
return super(CertComputeCreate, self).execute( return super(CertComputeCreate, self).execute(
amphora_id, ports=ports, config_drive_files=config_drive_files) amphora_id, ports=ports, config_drive_files=config_drive_files,
server_group_id=server_group_id)
class DeleteAmphoraeOnLoadBalancer(BaseComputeTask): class DeleteAmphoraeOnLoadBalancer(BaseComputeTask):
@ -177,3 +181,39 @@ class ComputeWait(BaseComputeTask):
time.sleep(CONF.controller_worker.amp_active_wait_sec) time.sleep(CONF.controller_worker.amp_active_wait_sec)
raise exceptions.ComputeWaitTimeoutException() raise exceptions.ComputeWaitTimeoutException()
class NovaServerGroupCreate(BaseComputeTask):
def execute(self, loadbalancer_id):
"""Create a server group by nova client api
:param loadbalancer_id: will be used for server group's name
:param policy: will used for server group's policy
:raises: Generic exception if the server group is not created
:returns: server group's id
"""
name = 'octavia-lb-' + loadbalancer_id
server_group = self.compute.create_server_group(
name, constants.ANTI_AFFINITY)
LOG.debug("Server Group created with id: %s for load balancer id: "
"%s", server_group.id, loadbalancer_id)
return server_group.id
def revert(self, result, *args, **kwargs):
"""This method will revert the creation of the
:param result: here it refers to server group id
"""
server_group_id = result
LOG.warn(_LW("Reverting server group create with id:%s"),
server_group_id)
self.compute.delete_server_group(server_group_id)
class NovaServerGroupDelete(BaseComputeTask):
def execute(self, server_group_id):
if server_group_id is not None:
self.compute.delete_server_group(server_group_id)
else:
return

View File

@ -690,6 +690,25 @@ class MarkLBActiveInDB(BaseDatabaseTask):
provisioning_status=constants.ERROR) provisioning_status=constants.ERROR)
class UpdateLBServerGroupInDB(BaseDatabaseTask):
"""Update the server group id info for load balancer in DB."""
def execute(self, loadbalancer_id, server_group_id):
LOG.debug("Server Group updated with id: %s for load balancer id: %s:",
server_group_id, loadbalancer_id)
self.loadbalancer_repo.update(db_apis.get_session(),
id=loadbalancer_id,
server_group_id=server_group_id)
def revert(self, loadbalancer_id, server_group_id, *args, **kwargs):
LOG.warn(_LW('Reverting Server Group updated with id: %(s1)s for '
'load balancer id: %(s2)s '),
{'s1': server_group_id, 's2': loadbalancer_id})
self.loadbalancer_repo.update(db_apis.get_session(),
id=loadbalancer_id,
server_group_id=None)
class MarkLBDeletedInDB(BaseDatabaseTask): class MarkLBDeletedInDB(BaseDatabaseTask):
"""Mark the load balancer deleted in the DB. """Mark the load balancer deleted in the DB.

View File

@ -0,0 +1,38 @@
# Copyright 2016 Hewlett-Packard Development Company, L.P.
#
# 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.
#
"""add_server_group_id_to_loadbalancer
Revision ID: 186509101b9b
Revises: 29ff921a6eb
Create Date: 2016-01-25 15:12:52.489652
"""
# revision identifiers, used by Alembic.
revision = '186509101b9b'
down_revision = '458c9ee2a011'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column(u'load_balancer', sa.Column(u'server_group_id',
sa.String(36), nullable=True))
def downgrade():
pass

View File

@ -277,6 +277,7 @@ class LoadBalancer(base_models.BASE, base_models.IdMixin,
amphorae = orm.relationship("Amphora", uselist=True, amphorae = orm.relationship("Amphora", uselist=True,
backref=orm.backref("load_balancer", backref=orm.backref("load_balancer",
uselist=False)) uselist=False))
server_group_id = sa.Column(sa.String(36), nullable=True)
class VRRPGroup(base_models.BASE): class VRRPGroup(base_models.BASE):

View File

@ -103,7 +103,8 @@ class ModelTestMixin(object):
'id': self.FAKE_UUID_1, 'id': self.FAKE_UUID_1,
'provisioning_status': constants.ACTIVE, 'provisioning_status': constants.ACTIVE,
'operating_status': constants.ONLINE, 'operating_status': constants.ONLINE,
'enabled': True} 'enabled': True,
'server_group_id': self.FAKE_UUID_1}
kwargs.update(overrides) kwargs.update(overrides)
return self._insert(session, models.LoadBalancer, kwargs) return self._insert(session, models.LoadBalancer, kwargs)

View File

@ -120,6 +120,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
'operating_status': constants.OFFLINE, 'operating_status': constants.OFFLINE,
'topology': constants.TOPOLOGY_ACTIVE_STANDBY, 'topology': constants.TOPOLOGY_ACTIVE_STANDBY,
'vrrp_group': None, 'vrrp_group': None,
'server_group_id': uuidutils.generate_uuid(),
'project_id': uuidutils.generate_uuid()} 'project_id': uuidutils.generate_uuid()}
vip = {'ip_address': '10.0.0.1', vip = {'ip_address': '10.0.0.1',
'port_id': uuidutils.generate_uuid(), 'port_id': uuidutils.generate_uuid(),
@ -547,7 +548,8 @@ class ListenerRepositoryTest(BaseRepositoryTest):
self.session, id=self.FAKE_UUID_1, project_id=self.FAKE_UUID_2, self.session, id=self.FAKE_UUID_1, project_id=self.FAKE_UUID_2,
name="lb_name", description="lb_description", name="lb_name", description="lb_description",
provisioning_status=constants.ACTIVE, provisioning_status=constants.ACTIVE,
operating_status=constants.ONLINE, enabled=True) operating_status=constants.ONLINE, enabled=True,
server_group_id=self.FAKE_UUID_1)
def create_listener(self, listener_id, port, default_pool_id=None): def create_listener(self, listener_id, port, default_pool_id=None):
listener = self.listener_repo.create( listener = self.listener_repo.create(

View File

@ -36,15 +36,20 @@ class TestNoopComputeDriver(base.TestCase):
self.confdrivefiles = "config_driver_files" self.confdrivefiles = "config_driver_files"
self.user_data = "user_data" self.user_data = "user_data"
self.amphora_id = self.FAKE_UUID_1 self.amphora_id = self.FAKE_UUID_1
self.loadbalancer_id = self.FAKE_UUID_1
self.server_group_policy = 'anti-affinity'
self.server_group_id = self.FAKE_UUID_1
def build(self): def build(self):
self.driver.build(self.name, self.amphora_flavor, self.image_id, self.driver.build(self.name, self.amphora_flavor, self.image_id,
self.key_name, self.sec_groups, self.network_ids, self.key_name, self.sec_groups, self.network_ids,
self.confdrivefiles, self.user_data) self.confdrivefiles, self.user_data,
self.server_group_id)
self.assertEqual((self.name, self.amphora_flavor, self.image_id, self.assertEqual((self.name, self.amphora_flavor, self.image_id,
self.key_name, self.sec_groups, self.network_ids, self.key_name, self.sec_groups, self.network_ids,
self.config_drive_files, self.user_data, 'build'), self.config_drive_files, self.user_data,
self.server_group_id, 'build'),
self.driver.driver.computeconfig[(self.name, self.driver.driver.computeconfig[(self.name,
self.amphora_flavor, self.amphora_flavor,
self.image_id, self.image_id,
@ -52,7 +57,9 @@ class TestNoopComputeDriver(base.TestCase):
self.sec_groups, self.sec_groups,
self.network_ids, self.network_ids,
self.confdrivefiles, self.confdrivefiles,
self.user_data)]) self.user_data,
self.server_group_id
)])
def test_delete(self): def test_delete(self):
self.driver.delete(self.amphora_id) self.driver.delete(self.amphora_id)
@ -65,3 +72,10 @@ class TestNoopComputeDriver(base.TestCase):
def get_amphora(self): def get_amphora(self):
self.driver.get_amphora(self.amphora_id) self.driver.get_amphora(self.amphora_id)
def test_create_server_group(self):
self.driver.create_server_group(self.loadbalancer_id,
self.server_group_policy)
def test_delete_server_group(self):
self.driver.delete_server_group(self.server_group_id)

View File

@ -13,6 +13,7 @@
# under the License. # under the License.
import mock import mock
from novaclient import exceptions as nova_exceptions
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import uuidutils from oslo_utils import uuidutils
@ -50,19 +51,34 @@ class TestNovaClient(base.TestCase):
self.interface_list.fixed_ips = [mock.MagicMock()] self.interface_list.fixed_ips = [mock.MagicMock()]
self.interface_list.fixed_ips[0] = {'ip_address': '10.0.0.1'} self.interface_list.fixed_ips[0] = {'ip_address': '10.0.0.1'}
self.loadbalancer_id = uuidutils.generate_uuid()
self.server_group_policy = constants.ANTI_AFFINITY
self.server_group_id = uuidutils.generate_uuid()
self.manager = nova_common.VirtualMachineManager() self.manager = nova_common.VirtualMachineManager()
self.manager.manager = mock.MagicMock() self.manager.manager = mock.MagicMock()
self.manager.server_groups = mock.MagicMock()
self.manager._nova_client = mock.MagicMock() self.manager._nova_client = mock.MagicMock()
self.nova_response.interface_list.side_effect = [[self.interface_list]] self.nova_response.interface_list.side_effect = [[self.interface_list]]
self.manager.manager.get.return_value = self.nova_response self.manager.manager.get.return_value = self.nova_response
self.manager.manager.create.return_value = self.nova_response self.manager.manager.create.return_value = self.nova_response
self.manager.server_groups.create.return_value = mock.Mock()
self.nova_response.addresses = {self.net_name: [{'addr': '10.0.0.1'}]} self.nova_response.addresses = {self.net_name: [{'addr': '10.0.0.1'}]}
self.nova_network = mock.Mock() self.nova_network = mock.Mock()
self.nova_network.label = self.net_name self.nova_network.label = self.net_name
self.server_group_name = 'octavia-lb-' + self.loadbalancer_id
self.server_group_kwargs = {'name': self.server_group_name,
'policies': [self.server_group_policy]}
self.server_group_mock = mock.Mock()
self.server_group_mock.name = self.server_group_name
self.server_group_mock.policy = self.server_group_policy
self.server_group_mock.id = self.server_group_id
super(TestNovaClient, self).setUp() super(TestNovaClient, self).setUp()
def test_build(self): def test_build(self):
@ -85,7 +101,8 @@ class TestNovaClient(base.TestCase):
security_groups=1, security_groups=1,
files='Files Blah', files='Files Blah',
userdata='Blah', userdata='Blah',
config_drive=True) config_drive=True,
scheduler_hints=None)
def test_bad_build(self): def test_bad_build(self):
self.manager.manager.create.side_effect = Exception self.manager.manager.create.side_effect = Exception
@ -144,3 +161,43 @@ class TestNovaClient(base.TestCase):
self.assertEqual(self.amphora, amphora) self.assertEqual(self.amphora, amphora)
self.assertTrue(self.nova_response.interface_list.called) self.assertTrue(self.nova_response.interface_list.called)
self.manager._nova_client.networks.get.called_with(self.net_name) self.manager._nova_client.networks.get.called_with(self.net_name)
def test_create_server_group(self):
self.manager.server_groups.create.return_value = self.server_group_mock
sg = self.manager.create_server_group(self.server_group_name,
self.server_group_policy)
self.assertEqual(sg.id, self.server_group_id)
self.assertEqual(sg.name, self.server_group_name)
self.assertEqual(sg.policy, self.server_group_policy)
self.manager.server_groups.create.called_with(
**self.server_group_kwargs)
def test_bad_create_server_group(self):
self.manager.server_groups.create.side_effect = Exception
self.assertRaises(exceptions.ServerGroupObjectCreateException,
self.manager.create_server_group,
self.server_group_name, self.server_group_policy)
self.manager.server_groups.create.called_with(
**self.server_group_kwargs)
def test_delete_server_group(self):
self.manager.delete_server_group(self.server_group_id)
self.manager.server_groups.delete.called_with(self.server_group_id)
def test_bad_delete_server_group(self):
self.manager.server_groups.delete.side_effect = [
nova_exceptions.NotFound('test_exception'), Exception]
# NotFound should not raise an exception
self.manager.delete_server_group(self.server_group_id)
self.manager.server_groups.delete.called_with(self.server_group_id)
# Catch the exception for server group object delete exception
self.assertRaises(exceptions.ServerGroupObjectDeleteException,
self.manager.delete_server_group,
self.server_group_id)
self.manager.server_groups.delete.called_with(self.server_group_id)

View File

@ -37,6 +37,7 @@ class TestEndpoint(base.TestCase):
self.context = {} self.context = {}
self.resource_updates = {} self.resource_updates = {}
self.resource_id = 1234 self.resource_id = 1234
self.server_group_id = 3456
def test_create_load_balancer(self): def test_create_load_balancer(self):
self.ep.create_load_balancer(self.context, self.resource_id) self.ep.create_load_balancer(self.context, self.resource_id)

View File

@ -30,6 +30,8 @@ class TestAmphoraFlows(base.TestCase):
def setUp(self): def setUp(self):
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver', cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver',
group='controller_worker') group='controller_worker')
cfg.CONF.set_override('enable_anti_affinity', False,
group='nova')
self.AmpFlow = amphora_flows.AmphoraFlows() self.AmpFlow = amphora_flows.AmphoraFlows()
conf = oslo_fixture.Config(cfg.CONF) conf = oslo_fixture.Config(cfg.CONF)
conf.config(group="keystone_authtoken", auth_version=AUTH_VERSION) conf.config(group="keystone_authtoken", auth_version=AUTH_VERSION)
@ -89,6 +91,7 @@ class TestAmphoraFlows(base.TestCase):
def test_get_cert_create_amphora_for_lb_flow(self): def test_get_cert_create_amphora_for_lb_flow(self):
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver', cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver',
group='controller_worker') group='controller_worker')
self.AmpFlow = amphora_flows.AmphoraFlows() self.AmpFlow = amphora_flows.AmphoraFlows()
amp_flow = self.AmpFlow._get_create_amp_for_lb_subflow( amp_flow = self.AmpFlow._get_create_amp_for_lb_subflow(
@ -110,6 +113,7 @@ class TestAmphoraFlows(base.TestCase):
def test_get_cert_master_create_amphora_for_lb_flow(self): def test_get_cert_master_create_amphora_for_lb_flow(self):
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver', cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver',
group='controller_worker') group='controller_worker')
self.AmpFlow = amphora_flows.AmphoraFlows() self.AmpFlow = amphora_flows.AmphoraFlows()
amp_flow = self.AmpFlow._get_create_amp_for_lb_subflow( amp_flow = self.AmpFlow._get_create_amp_for_lb_subflow(
@ -128,6 +132,28 @@ class TestAmphoraFlows(base.TestCase):
self.assertEqual(5, len(amp_flow.provides)) self.assertEqual(5, len(amp_flow.provides))
self.assertEqual(1, len(amp_flow.requires)) self.assertEqual(1, len(amp_flow.requires))
def test_get_cert_master_rest_anti_affinity_create_amphora_for_lb_flow(
self):
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver',
group='controller_worker')
cfg.CONF.set_override('enable_anti_affinity', True,
group='nova')
self.AmpFlow = amphora_flows.AmphoraFlows()
amp_flow = self.AmpFlow._get_create_amp_for_lb_subflow(
'SOMEPREFIX', constants.ROLE_MASTER)
self.assertIsInstance(amp_flow, flow.Flow)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.SERVER_GROUP_ID, amp_flow.requires)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.SERVER_PEM, amp_flow.provides)
self.assertEqual(5, len(amp_flow.provides))
self.assertEqual(2, len(amp_flow.requires))
def test_get_cert_backup_create_amphora_for_lb_flow(self): def test_get_cert_backup_create_amphora_for_lb_flow(self):
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver', cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver',
group='controller_worker') group='controller_worker')
@ -170,6 +196,27 @@ class TestAmphoraFlows(base.TestCase):
self.assertEqual(5, len(amp_flow.provides)) self.assertEqual(5, len(amp_flow.provides))
self.assertEqual(1, len(amp_flow.requires)) self.assertEqual(1, len(amp_flow.requires))
def test_get_cert_backup_rest_anti_affinity_create_amphora_for_lb_flow(
self):
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver',
group='controller_worker')
cfg.CONF.set_override('enable_anti_affinity', True,
group='nova')
self.AmpFlow = amphora_flows.AmphoraFlows()
amp_flow = self.AmpFlow._get_create_amp_for_lb_subflow(
'SOMEPREFIX', constants.ROLE_BACKUP)
self.assertIsInstance(amp_flow, flow.Flow)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.SERVER_GROUP_ID, amp_flow.requires)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.SERVER_PEM, amp_flow.provides)
self.assertEqual(5, len(amp_flow.provides))
self.assertEqual(2, len(amp_flow.requires))
def test_get_delete_amphora_flow(self): def test_get_delete_amphora_flow(self):
amp_flow = self.AmpFlow.get_delete_amphora_flow() amp_flow = self.AmpFlow.get_delete_amphora_flow()

View File

@ -12,9 +12,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# #
from oslo_config import cfg
from oslo_config import fixture as oslo_fixture
from taskflow.patterns import linear_flow as flow from taskflow.patterns import linear_flow as flow
from taskflow.patterns import unordered_flow
from octavia.common import constants from octavia.common import constants
from octavia.common import exceptions from octavia.common import exceptions
@ -26,13 +26,14 @@ class TestLoadBalancerFlows(base.TestCase):
def setUp(self): def setUp(self):
self.LBFlow = load_balancer_flows.LoadBalancerFlows() self.LBFlow = load_balancer_flows.LoadBalancerFlows()
conf = oslo_fixture.Config(cfg.CONF)
conf.config(group="nova", enable_anti_affinity=False)
super(TestLoadBalancerFlows, self).setUp() super(TestLoadBalancerFlows, self).setUp()
def test_get_create_load_balancer_flow(self): def test_get_create_load_balancer_flow(self):
amp_flow = self.LBFlow.get_create_load_balancer_flow( amp_flow = self.LBFlow.get_create_load_balancer_flow(
constants.TOPOLOGY_SINGLE) constants.TOPOLOGY_SINGLE)
self.assertIsInstance(amp_flow, unordered_flow.Flow) self.assertIsInstance(amp_flow, flow.Flow)
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires) self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.AMPHORA, amp_flow.provides) self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides) self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
@ -42,13 +43,27 @@ class TestLoadBalancerFlows(base.TestCase):
def test_get_create_active_standby_load_balancer_flow(self): def test_get_create_active_standby_load_balancer_flow(self):
amp_flow = self.LBFlow.get_create_load_balancer_flow( amp_flow = self.LBFlow.get_create_load_balancer_flow(
constants.TOPOLOGY_ACTIVE_STANDBY) constants.TOPOLOGY_ACTIVE_STANDBY)
self.assertIsInstance(amp_flow, unordered_flow.Flow) self.assertIsInstance(amp_flow, flow.Flow)
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires) self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.AMPHORA, amp_flow.provides) self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides) self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides) self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides) self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
def test_get_create_anti_affinity_active_standby_load_balancer_flow(self):
cfg.CONF.set_override('enable_anti_affinity', True,
group='nova')
self._LBFlow = load_balancer_flows.LoadBalancerFlows()
amp_flow = self._LBFlow.get_create_load_balancer_flow(
constants.TOPOLOGY_ACTIVE_STANDBY)
self.assertIsInstance(amp_flow, flow.Flow)
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.SERVER_GROUP_ID, amp_flow.provides)
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
def test_get_create_bogus_topology_load_balancer_flow(self): def test_get_create_bogus_topology_load_balancer_flow(self):
self.assertRaises(exceptions.InvalidTopology, self.assertRaises(exceptions.InvalidTopology,
self.LBFlow.get_create_load_balancer_flow, self.LBFlow.get_create_load_balancer_flow,
@ -61,9 +76,10 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIsInstance(lb_flow, flow.Flow) self.assertIsInstance(lb_flow, flow.Flow)
self.assertIn(constants.LOADBALANCER, lb_flow.requires) self.assertIn(constants.LOADBALANCER, lb_flow.requires)
self.assertIn(constants.SERVER_GROUP_ID, lb_flow.requires)
self.assertEqual(0, len(lb_flow.provides)) self.assertEqual(0, len(lb_flow.provides))
self.assertEqual(1, len(lb_flow.requires)) self.assertEqual(2, len(lb_flow.requires))
def test_get_new_LB_networking_subflow(self): def test_get_new_LB_networking_subflow(self):

View File

@ -19,6 +19,7 @@ from oslo_config import fixture as oslo_fixture
from oslo_utils import uuidutils from oslo_utils import uuidutils
import six.moves.builtins as builtins import six.moves.builtins as builtins
from octavia.common import constants from octavia.common import constants
from octavia.common import exceptions from octavia.common import exceptions
from octavia.controller.worker.tasks import compute_tasks from octavia.controller.worker.tasks import compute_tasks
@ -35,6 +36,7 @@ COMPUTE_ID = uuidutils.generate_uuid()
LB_NET_IP = '192.0.2.1' LB_NET_IP = '192.0.2.1'
PORT_ID = uuidutils.generate_uuid() PORT_ID = uuidutils.generate_uuid()
AUTH_VERSION = '2' AUTH_VERSION = '2'
SERVER_GRPOUP_ID = uuidutils.generate_uuid()
class TestException(Exception): class TestException(Exception):
@ -85,7 +87,8 @@ class TestComputeTasks(base.TestCase):
mock_driver.build.return_value = COMPUTE_ID mock_driver.build.return_value = COMPUTE_ID
# Test execute() # Test execute()
compute_id = createcompute.execute(_amphora_mock.id, ports=[_port]) compute_id = createcompute.execute(_amphora_mock.id, ports=[_port],
server_group_id=SERVER_GRPOUP_ID)
# Validate that the build method was called properly # Validate that the build method was called properly
mock_driver.build.assert_called_once_with( mock_driver.build.assert_called_once_with(
@ -98,7 +101,8 @@ class TestComputeTasks(base.TestCase):
port_ids=[PORT_ID], port_ids=[PORT_ID],
config_drive_files={'/etc/octavia/' config_drive_files={'/etc/octavia/'
'amphora-agent.conf': 'test_conf'}, 'amphora-agent.conf': 'test_conf'},
user_data=None) user_data=None,
server_group_id=SERVER_GRPOUP_ID)
# Make sure it returns the expected compute_id # Make sure it returns the expected compute_id
assert(compute_id == COMPUTE_ID) assert(compute_id == COMPUTE_ID)
@ -155,7 +159,8 @@ class TestComputeTasks(base.TestCase):
network_ids=[AMP_NET], network_ids=[AMP_NET],
port_ids=[PORT_ID], port_ids=[PORT_ID],
config_drive_files=None, config_drive_files=None,
user_data='test_ud_conf') user_data='test_ud_conf',
server_group_id=None)
# Make sure it returns the expected compute_id # Make sure it returns the expected compute_id
assert(compute_id == COMPUTE_ID) assert(compute_id == COMPUTE_ID)
@ -199,7 +204,8 @@ class TestComputeTasks(base.TestCase):
conf.config(group="controller_worker", amp_ssh_access_allowed=False) conf.config(group="controller_worker", amp_ssh_access_allowed=False)
# Test execute() # Test execute()
compute_id = createcompute.execute(_amphora_mock.id, ports=[_port]) compute_id = createcompute.execute(_amphora_mock.id, ports=[_port],
server_group_id=SERVER_GRPOUP_ID)
# Validate that the build method was called properly # Validate that the build method was called properly
mock_driver.build.assert_called_once_with( mock_driver.build.assert_called_once_with(
@ -212,7 +218,8 @@ class TestComputeTasks(base.TestCase):
port_ids=[PORT_ID], port_ids=[PORT_ID],
config_drive_files={'/etc/octavia/' config_drive_files={'/etc/octavia/'
'amphora-agent.conf': 'test_conf'}, 'amphora-agent.conf': 'test_conf'},
user_data=None) user_data=None,
server_group_id=SERVER_GRPOUP_ID)
# Make sure it returns the expected compute_id # Make sure it returns the expected compute_id
self.assertEqual(COMPUTE_ID, compute_id) self.assertEqual(COMPUTE_ID, compute_id)
@ -252,8 +259,9 @@ class TestComputeTasks(base.TestCase):
m = mock.mock_open(read_data='test') m = mock.mock_open(read_data='test')
with mock.patch.object(builtins, 'open', m, create=True): with mock.patch.object(builtins, 'open', m, create=True):
# Test execute() # Test execute()
compute_id = createcompute.execute(_amphora_mock.id, compute_id = createcompute.execute(_amphora_mock.id, 'test_cert',
'test_cert') server_group_id=SERVER_GRPOUP_ID
)
# Validate that the build method was called properly # Validate that the build method was called properly
mock_driver.build.assert_called_once_with( mock_driver.build.assert_called_once_with(
@ -268,7 +276,8 @@ class TestComputeTasks(base.TestCase):
config_drive_files={ config_drive_files={
'/etc/octavia/certs/server.pem': 'test_cert', '/etc/octavia/certs/server.pem': 'test_cert',
'/etc/octavia/certs/client_ca.pem': 'test', '/etc/octavia/certs/client_ca.pem': 'test',
'/etc/octavia/amphora-agent.conf': 'test_conf'}) '/etc/octavia/amphora-agent.conf': 'test_conf'},
server_group_id=SERVER_GRPOUP_ID)
# Make sure it returns the expected compute_id # Make sure it returns the expected compute_id
assert(compute_id == COMPUTE_ID) assert(compute_id == COMPUTE_ID)
@ -355,3 +364,44 @@ class TestComputeTasks(base.TestCase):
delete_compute.execute(_amphora_mock) delete_compute.execute(_amphora_mock)
mock_driver.delete.assert_called_once_with(COMPUTE_ID) mock_driver.delete.assert_called_once_with(COMPUTE_ID)
@mock.patch('stevedore.driver.DriverManager.driver')
def test_nova_server_group_create(self, mock_driver):
nova_sever_group_obj = compute_tasks.NovaServerGroupCreate()
server_group_test_id = '6789'
fake_server_group = mock.MagicMock()
fake_server_group.id = server_group_test_id
fake_server_group.policy = 'anti-affinity'
mock_driver.create_server_group.return_value = fake_server_group
# Test execute()
sg_id = nova_sever_group_obj.execute('123')
# Validate that the build method was called properly
mock_driver.create_server_group.assert_called_once_with(
'octavia-lb-123', 'anti-affinity')
# Make sure it returns the expected server group_id
assert(sg_id == server_group_test_id)
# Test revert()
nova_sever_group_obj.revert(sg_id)
# Validate that the delete_server_group method was called properly
mock_driver.delete_server_group.assert_called_once_with(sg_id)
nova_sever_group_obj.revert(sg_id)
@mock.patch('stevedore.driver.DriverManager.driver')
def test_nova_server_group_delete_with_sever_group_id(self, mock_driver):
nova_sever_group_obj = compute_tasks.NovaServerGroupDelete()
sg_id = '6789'
nova_sever_group_obj.execute(sg_id)
mock_driver.delete_server_group.assert_called_once_with(sg_id)
@mock.patch('stevedore.driver.DriverManager.driver')
def test_nova_server_group_delete_with_None(self, mock_driver):
nova_sever_group_obj = compute_tasks.NovaServerGroupDelete()
sg_id = None
nova_sever_group_obj.execute(sg_id)
self.assertFalse(mock_driver.delete_server_group.called, sg_id)

View File

@ -29,6 +29,7 @@ import octavia.tests.unit.base as base
AMP_ID = uuidutils.generate_uuid() AMP_ID = uuidutils.generate_uuid()
COMPUTE_ID = uuidutils.generate_uuid() COMPUTE_ID = uuidutils.generate_uuid()
LB_ID = uuidutils.generate_uuid() LB_ID = uuidutils.generate_uuid()
SERVER_GROUP_ID = uuidutils.generate_uuid()
LB_NET_IP = '192.0.2.2' LB_NET_IP = '192.0.2.2'
LISTENER_ID = uuidutils.generate_uuid() LISTENER_ID = uuidutils.generate_uuid()
POOL_ID = uuidutils.generate_uuid() POOL_ID = uuidutils.generate_uuid()
@ -1348,3 +1349,27 @@ class TestDatabaseTasks(base.TestCase):
mark_busy.execute(_loadbalancer_mock) mark_busy.execute(_loadbalancer_mock)
mock_amp_health_repo_update.assert_called_once_with( mock_amp_health_repo_update.assert_called_once_with(
'TEST', amphora_id=AMP_ID, busy=True) 'TEST', amphora_id=AMP_ID, busy=True)
@mock.patch('octavia.db.repositories.LoadBalancerRepository.update')
def test_update_lb_server_group_in_db(self,
mock_listner_repo_update,
mock_generate_uuid,
mock_LOG,
mock_get_session,
mock_loadbalancer_repo_update,
mock_listener_repo_update,
mock_amphora_repo_update,
mock_amphora_repo_delete):
update_server_group_info = database_tasks.UpdateLBServerGroupInDB()
update_server_group_info.execute(LB_ID, SERVER_GROUP_ID)
repo.LoadBalancerRepository.update.assert_called_once_with(
'TEST',
id=LB_ID,
server_group_id=SERVER_GROUP_ID)
# Test the revert
mock_listener_repo_update.reset_mock()
update_server_group_info.revert(LB_ID, SERVER_GROUP_ID)

View File

@ -469,7 +469,12 @@ class TestControllerWorker(base.TestCase):
(base_taskflow.BaseTaskFlowEngine._taskflow_load. (base_taskflow.BaseTaskFlowEngine._taskflow_load.
assert_called_once_with(_flow_mock, assert_called_once_with(_flow_mock,
store={constants.LOADBALANCER: store={constants.LOADBALANCER:
_load_balancer_mock})) _load_balancer_mock,
constants.SERVER_GROUP_ID:
_load_balancer_mock.server_group_id
}
)
)
_flow_mock.run.assert_called_once_with() _flow_mock.run.assert_called_once_with()