Initial provider driver library checkin
This patch is the inital move of the provider driver modules from Octavia to octavia-lib. Change-Id: I7c1b5d7ae59ce8971d21db225174095f8b5919ce
This commit is contained in:
parent
5f833c77d2
commit
20bff06b0a
|
@ -0,0 +1,97 @@
|
|||
# The format of this file isn't really documented; just use --generate-rcfile
|
||||
[MASTER]
|
||||
# Add <file or directory> to the black list. It should be a base name, not a
|
||||
# path. You may set this option multiple times.
|
||||
ignore=.git,tests
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
# NOTE: The options which do not need to be suppressed can be removed.
|
||||
disable=
|
||||
# "F" Fatal errors that prevent further processing
|
||||
# "I" Informational noise
|
||||
locally-disabled,
|
||||
# "E" Error for important programming issues (likely bugs)
|
||||
import-error,
|
||||
not-callable,
|
||||
no-member,
|
||||
# "W" Warnings for stylistic problems or minor programming issues
|
||||
abstract-method,
|
||||
anomalous-backslash-in-string,
|
||||
arguments-differ,
|
||||
attribute-defined-outside-init,
|
||||
bad-builtin,
|
||||
broad-except,
|
||||
fixme,
|
||||
global-statement,
|
||||
no-init,
|
||||
pointless-string-statement,
|
||||
protected-access,
|
||||
redefined-builtin,
|
||||
redefined-outer-name,
|
||||
signature-differs,
|
||||
unidiomatic-typecheck,
|
||||
unused-argument,
|
||||
unused-variable,
|
||||
useless-super-delegation,
|
||||
# "C" Coding convention violations
|
||||
bad-continuation,
|
||||
invalid-name,
|
||||
line-too-long,
|
||||
missing-docstring,
|
||||
# "R" Refactor recommendations
|
||||
duplicate-code,
|
||||
interface-not-implemented,
|
||||
no-self-use,
|
||||
too-few-public-methods,
|
||||
too-many-ancestors,
|
||||
too-many-arguments,
|
||||
too-many-branches,
|
||||
too-many-instance-attributes,
|
||||
too-many-lines,
|
||||
too-many-locals,
|
||||
too-many-public-methods,
|
||||
too-many-return-statements,
|
||||
too-many-statements,
|
||||
multiple-statements,
|
||||
duplicate-except,
|
||||
keyword-arg-before-vararg
|
||||
|
||||
[BASIC]
|
||||
# Variable names can be 1 to 31 characters long, with lowercase and underscores
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
|
||||
# Argument names can be 2 to 31 characters long, with lowercase and underscores
|
||||
argument-rgx=[a-z_][a-z0-9_]{1,30}$
|
||||
|
||||
# Method names should be at least 3 characters long
|
||||
# and be lowercased with underscores
|
||||
method-rgx=([a-z_][a-z0-9_]{2,}|setUp|tearDown)$
|
||||
|
||||
# Module names matching
|
||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||
|
||||
# Don't require docstrings on tests.
|
||||
no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
|
||||
|
||||
[FORMAT]
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=79
|
||||
|
||||
[VARIABLES]
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
[CLASSES]
|
||||
|
||||
[IMPORTS]
|
||||
# Deprecated modules which should not be used, separated by a comma
|
||||
deprecated-modules=
|
||||
|
||||
[TYPECHECK]
|
||||
# List of module names for which member attributes should not be checked
|
||||
ignored-modules=six.moves,_MovedItems
|
||||
|
||||
[REPORTS]
|
||||
# Tells whether to display a full report or only the messages
|
||||
reports=no
|
|
@ -1,3 +1,3 @@
|
|||
[DEFAULT]
|
||||
test_path=./octavia_lib/tests
|
||||
test_path=${OS_TEST_PATH:-./octavia_lib/tests/unit}
|
||||
top_dir=./
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
bandit==1.4.0
|
||||
coverage==4.0
|
||||
doc8==0.6.0
|
||||
hacking==0.12.0
|
||||
oslo.i18n==3.15.3
|
||||
oslo.log==3.36.0
|
||||
oslo.utils==3.33.0
|
||||
oslotest==3.2.0
|
||||
pbr==2.0.0
|
||||
pylint==1.9.2
|
||||
python-subunit==1.0.0
|
||||
six==1.10.0
|
||||
stestr==2.0.0
|
||||
testtools==2.2.0
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
# Copyright (c) 2014 Rackspace
|
||||
# Copyright (c) 2016 Blue Box, an IBM Company
|
||||
# Copyright 2018 Rackspace, US Inc.
|
||||
# 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 six
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseDataModel(object):
|
||||
def to_dict(self, calling_classes=None, recurse=False,
|
||||
render_unsets=False, **kwargs):
|
||||
"""Converts a data model to a dictionary."""
|
||||
calling_classes = calling_classes or []
|
||||
ret = {}
|
||||
for attr in self.__dict__:
|
||||
if attr.startswith('_') or not kwargs.get(attr, True):
|
||||
continue
|
||||
value = self.__dict__[attr]
|
||||
|
||||
if recurse:
|
||||
if isinstance(getattr(self, attr), list):
|
||||
ret[attr] = []
|
||||
for item in value:
|
||||
if isinstance(item, BaseDataModel):
|
||||
if type(self) not in calling_classes:
|
||||
ret[attr].append(
|
||||
item.to_dict(calling_classes=(
|
||||
calling_classes + [type(self)]),
|
||||
recurse=True,
|
||||
render_unsets=render_unsets))
|
||||
else:
|
||||
ret[attr].append(None)
|
||||
else:
|
||||
ret[attr].append(item)
|
||||
elif isinstance(getattr(self, attr), BaseDataModel):
|
||||
if type(self) not in calling_classes:
|
||||
ret[attr] = value.to_dict(
|
||||
render_unsets=render_unsets,
|
||||
calling_classes=calling_classes + [type(self)])
|
||||
else:
|
||||
ret[attr] = None
|
||||
elif six.PY2 and isinstance(value, six.text_type):
|
||||
ret[attr.encode('utf8')] = value.encode('utf8')
|
||||
elif isinstance(value, UnsetType):
|
||||
if render_unsets:
|
||||
ret[attr] = None
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
ret[attr] = value
|
||||
else:
|
||||
if (isinstance(getattr(self, attr), (BaseDataModel, list)) or
|
||||
isinstance(value, UnsetType)):
|
||||
if render_unsets:
|
||||
ret[attr] = None
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
ret[attr] = value
|
||||
|
||||
return ret
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, self.__class__):
|
||||
return self.to_dict() == other.to_dict()
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict):
|
||||
return cls(**dict)
|
||||
|
||||
|
||||
class UnsetType(object):
|
||||
def __bool__(self):
|
||||
return False
|
||||
__nonzero__ = __bool__
|
||||
|
||||
def __repr__(self):
|
||||
return 'Unset'
|
||||
|
||||
|
||||
Unset = UnsetType()
|
||||
|
||||
|
||||
class LoadBalancer(BaseDataModel):
|
||||
def __init__(self, admin_state_up=Unset, description=Unset, flavor=Unset,
|
||||
listeners=Unset, loadbalancer_id=Unset, name=Unset,
|
||||
pools=Unset, project_id=Unset, vip_address=Unset,
|
||||
vip_network_id=Unset, vip_port_id=Unset, vip_subnet_id=Unset,
|
||||
vip_qos_policy_id=Unset):
|
||||
|
||||
self.admin_state_up = admin_state_up
|
||||
self.description = description
|
||||
self.flavor = flavor
|
||||
self.listeners = listeners
|
||||
self.loadbalancer_id = loadbalancer_id
|
||||
self.name = name
|
||||
self.pools = pools
|
||||
self.project_id = project_id
|
||||
self.vip_address = vip_address
|
||||
self.vip_network_id = vip_network_id
|
||||
self.vip_port_id = vip_port_id
|
||||
self.vip_subnet_id = vip_subnet_id
|
||||
self.vip_qos_policy_id = vip_qos_policy_id
|
||||
|
||||
|
||||
class Listener(BaseDataModel):
|
||||
def __init__(self, admin_state_up=Unset, connection_limit=Unset,
|
||||
default_pool=Unset, default_pool_id=Unset,
|
||||
default_tls_container_ref=Unset,
|
||||
default_tls_container_data=Unset, description=Unset,
|
||||
insert_headers=Unset, l7policies=Unset, listener_id=Unset,
|
||||
loadbalancer_id=Unset, name=Unset, protocol=Unset,
|
||||
protocol_port=Unset, sni_container_refs=Unset,
|
||||
sni_container_data=Unset, timeout_client_data=Unset,
|
||||
timeout_member_connect=Unset, timeout_member_data=Unset,
|
||||
timeout_tcp_inspect=Unset):
|
||||
|
||||
self.admin_state_up = admin_state_up
|
||||
self.connection_limit = connection_limit
|
||||
self.default_pool = default_pool
|
||||
self.default_pool_id = default_pool_id
|
||||
self.default_tls_container_data = default_tls_container_data
|
||||
self.default_tls_container_ref = default_tls_container_ref
|
||||
self.description = description
|
||||
self.insert_headers = insert_headers
|
||||
self.l7policies = l7policies
|
||||
self.listener_id = listener_id
|
||||
self.loadbalancer_id = loadbalancer_id
|
||||
self.name = name
|
||||
self.protocol = protocol
|
||||
self.protocol_port = protocol_port
|
||||
self.sni_container_data = sni_container_data
|
||||
self.sni_container_refs = sni_container_refs
|
||||
self.timeout_client_data = timeout_client_data
|
||||
self.timeout_member_connect = timeout_member_connect
|
||||
self.timeout_member_data = timeout_member_data
|
||||
self.timeout_tcp_inspect = timeout_tcp_inspect
|
||||
|
||||
|
||||
class Pool(BaseDataModel):
|
||||
def __init__(self, admin_state_up=Unset, description=Unset,
|
||||
healthmonitor=Unset, lb_algorithm=Unset,
|
||||
loadbalancer_id=Unset, members=Unset, name=Unset,
|
||||
pool_id=Unset, listener_id=Unset, protocol=Unset,
|
||||
session_persistence=Unset):
|
||||
|
||||
self.admin_state_up = admin_state_up
|
||||
self.description = description
|
||||
self.healthmonitor = healthmonitor
|
||||
self.lb_algorithm = lb_algorithm
|
||||
self.loadbalancer_id = loadbalancer_id
|
||||
self.members = members
|
||||
self.name = name
|
||||
self.pool_id = pool_id
|
||||
self.listener_id = listener_id
|
||||
self.protocol = protocol
|
||||
self.session_persistence = session_persistence
|
||||
|
||||
|
||||
class Member(BaseDataModel):
|
||||
def __init__(self, address=Unset, admin_state_up=Unset, member_id=Unset,
|
||||
monitor_address=Unset, monitor_port=Unset, name=Unset,
|
||||
pool_id=Unset, protocol_port=Unset, subnet_id=Unset,
|
||||
weight=Unset, backup=Unset):
|
||||
|
||||
self.address = address
|
||||
self.admin_state_up = admin_state_up
|
||||
self.member_id = member_id
|
||||
self.monitor_address = monitor_address
|
||||
self.monitor_port = monitor_port
|
||||
self.name = name
|
||||
self.pool_id = pool_id
|
||||
self.protocol_port = protocol_port
|
||||
self.subnet_id = subnet_id
|
||||
self.weight = weight
|
||||
self.backup = backup
|
||||
|
||||
|
||||
class HealthMonitor(BaseDataModel):
|
||||
def __init__(self, admin_state_up=Unset, delay=Unset, expected_codes=Unset,
|
||||
healthmonitor_id=Unset, http_method=Unset, max_retries=Unset,
|
||||
max_retries_down=Unset, name=Unset, pool_id=Unset,
|
||||
timeout=Unset, type=Unset, url_path=Unset):
|
||||
|
||||
self.admin_state_up = admin_state_up
|
||||
self.delay = delay
|
||||
self.expected_codes = expected_codes
|
||||
self.healthmonitor_id = healthmonitor_id
|
||||
self.http_method = http_method
|
||||
self.max_retries = max_retries
|
||||
self.max_retries_down = max_retries_down
|
||||
self.name = name
|
||||
self.pool_id = pool_id
|
||||
self.timeout = timeout
|
||||
self.type = type
|
||||
self.url_path = url_path
|
||||
|
||||
|
||||
class L7Policy(BaseDataModel):
|
||||
def __init__(self, action=Unset, admin_state_up=Unset, description=Unset,
|
||||
l7policy_id=Unset, listener_id=Unset, name=Unset,
|
||||
position=Unset, redirect_pool_id=Unset, redirect_url=Unset,
|
||||
rules=Unset, redirect_prefix=Unset):
|
||||
|
||||
self.action = action
|
||||
self.admin_state_up = admin_state_up
|
||||
self.description = description
|
||||
self.l7policy_id = l7policy_id
|
||||
self.listener_id = listener_id
|
||||
self.name = name
|
||||
self.position = position
|
||||
self.redirect_pool_id = redirect_pool_id
|
||||
self.redirect_url = redirect_url
|
||||
self.rules = rules
|
||||
self.redirect_prefix = redirect_prefix
|
||||
|
||||
|
||||
class L7Rule(BaseDataModel):
|
||||
def __init__(self, admin_state_up=Unset, compare_type=Unset, invert=Unset,
|
||||
key=Unset, l7policy_id=Unset, l7rule_id=Unset, type=Unset,
|
||||
value=Unset):
|
||||
|
||||
self.admin_state_up = admin_state_up
|
||||
self.compare_type = compare_type
|
||||
self.invert = invert
|
||||
self.key = key
|
||||
self.l7policy_id = l7policy_id
|
||||
self.l7rule_id = l7rule_id
|
||||
self.type = type
|
||||
self.value = value
|
||||
|
||||
|
||||
class VIP(BaseDataModel):
|
||||
def __init__(self, vip_address=Unset, vip_network_id=Unset,
|
||||
vip_port_id=Unset, vip_subnet_id=Unset,
|
||||
vip_qos_policy_id=Unset):
|
||||
|
||||
self.vip_address = vip_address
|
||||
self.vip_network_id = vip_network_id
|
||||
self.vip_port_id = vip_port_id
|
||||
self.vip_subnet_id = vip_subnet_id
|
||||
self.vip_qos_policy_id = vip_qos_policy_id
|
|
@ -0,0 +1,155 @@
|
|||
# Copyright 2018 Rackspace, US Inc.
|
||||
#
|
||||
# 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 octavia.common import constants as consts
|
||||
from octavia.common import utils
|
||||
from octavia.db import api as db_apis
|
||||
from octavia.db import repositories as repo
|
||||
from octavia_lib.api.drivers import exceptions as driver_exceptions
|
||||
|
||||
|
||||
class DriverLibrary(object):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.loadbalancer_repo = repo.LoadBalancerRepository()
|
||||
self.listener_repo = repo.ListenerRepository()
|
||||
self.pool_repo = repo.PoolRepository()
|
||||
self.health_mon_repo = repo.HealthMonitorRepository()
|
||||
self.member_repo = repo.MemberRepository()
|
||||
self.l7policy_repo = repo.L7PolicyRepository()
|
||||
self.l7rule_repo = repo.L7RuleRepository()
|
||||
self.listener_stats_repo = repo.ListenerStatisticsRepository()
|
||||
|
||||
self.db_session = db_apis.get_session()
|
||||
super(DriverLibrary, self).__init__(**kwargs)
|
||||
|
||||
def _check_for_lb_vip_deallocate(self, repo, lb_id):
|
||||
lb = repo.get(self.db_session, id=lb_id)
|
||||
if lb.vip.octavia_owned:
|
||||
vip = lb.vip
|
||||
# We need a backreference
|
||||
vip.load_balancer = lb
|
||||
# Only lookup the network driver if we have a VIP to deallocate
|
||||
network_driver = utils.get_network_driver()
|
||||
network_driver.deallocate_vip(vip)
|
||||
|
||||
def _process_status_update(self, repo, object_name, record,
|
||||
delete_record=False):
|
||||
# Zero it out so that if the ID is missing from a record we do not
|
||||
# report the last LB as the failed record in the exception
|
||||
record_id = None
|
||||
try:
|
||||
record_id = record['id']
|
||||
record_kwargs = {}
|
||||
prov_status = record.get(consts.PROVISIONING_STATUS, None)
|
||||
if prov_status:
|
||||
if (prov_status == consts.DELETED and
|
||||
object_name == consts.LOADBALANCERS):
|
||||
self._check_for_lb_vip_deallocate(repo, record_id)
|
||||
elif prov_status == consts.DELETED and delete_record:
|
||||
repo.delete(self.db_session, id=record_id)
|
||||
return
|
||||
record_kwargs[consts.PROVISIONING_STATUS] = prov_status
|
||||
op_status = record.get(consts.OPERATING_STATUS, None)
|
||||
if op_status:
|
||||
record_kwargs[consts.OPERATING_STATUS] = op_status
|
||||
if prov_status or op_status:
|
||||
repo.update(self.db_session, record_id, **record_kwargs)
|
||||
except Exception as e:
|
||||
# We need to raise a failure here to notify the driver it is
|
||||
# sending bad status data.
|
||||
raise driver_exceptions.UpdateStatusError(
|
||||
fault_string=str(e), status_object_id=record_id,
|
||||
status_object=object_name)
|
||||
|
||||
def update_loadbalancer_status(self, status):
|
||||
"""Update load balancer status.
|
||||
|
||||
:param status: dictionary defining the provisioning status and
|
||||
operating status for load balancer objects, including pools,
|
||||
members, listeners, L7 policies, and L7 rules.
|
||||
iod (string): ID for the object.
|
||||
provisioning_status (string): Provisioning status for the object.
|
||||
operating_status (string): Operating status for the object.
|
||||
:type status: dict
|
||||
:raises: UpdateStatusError
|
||||
:returns: None
|
||||
"""
|
||||
members = status.pop(consts.MEMBERS, [])
|
||||
for member in members:
|
||||
self._process_status_update(self.member_repo, consts.MEMBERS,
|
||||
member, delete_record=True)
|
||||
|
||||
health_mons = status.pop(consts.HEALTHMONITORS, [])
|
||||
for health_mon in health_mons:
|
||||
self._process_status_update(
|
||||
self.health_mon_repo, consts.HEALTHMONITORS, health_mon,
|
||||
delete_record=True)
|
||||
|
||||
pools = status.pop(consts.POOLS, [])
|
||||
for pool in pools:
|
||||
self._process_status_update(self.pool_repo, consts.POOLS,
|
||||
pool, delete_record=True)
|
||||
|
||||
l7rules = status.pop(consts.L7RULES, [])
|
||||
for l7rule in l7rules:
|
||||
self._process_status_update(self.l7rule_repo, consts.L7RULES,
|
||||
l7rule, delete_record=True)
|
||||
|
||||
l7policies = status.pop(consts.L7POLICIES, [])
|
||||
for l7policy in l7policies:
|
||||
self._process_status_update(self.l7policy_repo, consts.L7POLICIES,
|
||||
l7policy, delete_record=True)
|
||||
|
||||
listeners = status.pop(consts.LISTENERS, [])
|
||||
for listener in listeners:
|
||||
self._process_status_update(self.listener_repo, consts.LISTENERS,
|
||||
listener, delete_record=True)
|
||||
|
||||
lbs = status.pop(consts.LOADBALANCERS, [])
|
||||
for lb in lbs:
|
||||
self._process_status_update(self.loadbalancer_repo,
|
||||
consts.LOADBALANCERS, lb)
|
||||
|
||||
def update_listener_statistics(self, statistics):
|
||||
"""Update listener statistics.
|
||||
|
||||
:param statistics: Statistics for listeners:
|
||||
id (string): ID for listener.
|
||||
active_connections (int): Number of currently active connections.
|
||||
bytes_in (int): Total bytes received.
|
||||
bytes_out (int): Total bytes sent.
|
||||
request_errors (int): Total requests not fulfilled.
|
||||
total_connections (int): The total connections handled.
|
||||
:type statistics: dict
|
||||
:raises: UpdateStatisticsError
|
||||
:returns: None
|
||||
"""
|
||||
listener_stats = statistics.get('listeners', [])
|
||||
for stat in listener_stats:
|
||||
try:
|
||||
listener_id = stat.pop('id')
|
||||
except Exception as e:
|
||||
raise driver_exceptions.UpdateStatisticsError(
|
||||
fault_string=str(e), stats_object='listeners')
|
||||
# Provider drivers other than the amphora driver do not have
|
||||
# an amphora ID, use the listener ID again here to meet the
|
||||
# constraint requirement.
|
||||
try:
|
||||
self.listener_stats_repo.replace(self.db_session, listener_id,
|
||||
listener_id, **stat)
|
||||
except Exception as e:
|
||||
raise driver_exceptions.UpdateStatisticsError(
|
||||
fault_string=str(e), stats_object='listeners',
|
||||
stats_object_id=listener_id)
|
|
@ -0,0 +1,148 @@
|
|||
# Copyright 2018 Rackspace, US Inc.
|
||||
#
|
||||
# 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 octavia_lib.i18n import _
|
||||
|
||||
|
||||
class DriverError(Exception):
|
||||
"""Catch all exception that drivers can raise.
|
||||
|
||||
This exception includes two strings: The user fault string and the
|
||||
optional operator fault string. The user fault string,
|
||||
"user_fault_string", will be provided to the API requester. The operator
|
||||
fault string, "operator_fault_string", will be logged in the Octavia API
|
||||
log file for the operator to use when debugging.
|
||||
|
||||
:param user_fault_string: String provided to the API requester.
|
||||
:type user_fault_string: string
|
||||
:param operator_fault_string: Optional string logged by the Octavia API
|
||||
for the operator to use when debugging.
|
||||
:type operator_fault_string: string
|
||||
"""
|
||||
user_fault_string = _("An unknown driver error occurred.")
|
||||
operator_fault_string = _("An unknown driver error occurred.")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user_fault_string = kwargs.pop('user_fault_string',
|
||||
self.user_fault_string)
|
||||
self.operator_fault_string = kwargs.pop('operator_fault_string',
|
||||
self.operator_fault_string)
|
||||
super(DriverError, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class NotImplementedError(Exception):
|
||||
"""Exception raised when a driver does not implement an API function.
|
||||
|
||||
:param user_fault_string: String provided to the API requester.
|
||||
:type user_fault_string: string
|
||||
:param operator_fault_string: Optional string logged by the Octavia API
|
||||
for the operator to use when debugging.
|
||||
:type operator_fault_string: string
|
||||
"""
|
||||
user_fault_string = _("This feature is not implemented by the provider.")
|
||||
operator_fault_string = _("This feature is not implemented by this "
|
||||
"provider.")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user_fault_string = kwargs.pop('user_fault_string',
|
||||
self.user_fault_string)
|
||||
self.operator_fault_string = kwargs.pop('operator_fault_string',
|
||||
self.operator_fault_string)
|
||||
super(NotImplementedError, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class UnsupportedOptionError(Exception):
|
||||
"""Exception raised when a driver does not support an option.
|
||||
|
||||
Provider drivers will validate that they can complete the request -- that
|
||||
all options are supported by the driver. If the request fails validation,
|
||||
drivers will raise an UnsupportedOptionError exception. For example, if a
|
||||
driver does not support a flavor passed as an option to load balancer
|
||||
create(), the driver will raise an UnsupportedOptionError and include a
|
||||
message parameter providing an explanation of the failure.
|
||||
|
||||
:param user_fault_string: String provided to the API requester.
|
||||
:type user_fault_string: string
|
||||
:param operator_fault_string: Optional string logged by the Octavia API
|
||||
for the operator to use when debugging.
|
||||
:type operator_fault_string: string
|
||||
"""
|
||||
user_fault_string = _("A specified option is not supported by this "
|
||||
"provider.")
|
||||
operator_fault_string = _("A specified option is not supported by this "
|
||||
"provider.")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user_fault_string = kwargs.pop('user_fault_string',
|
||||
self.user_fault_string)
|
||||
self.operator_fault_string = kwargs.pop('operator_fault_string',
|
||||
self.operator_fault_string)
|
||||
super(UnsupportedOptionError, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class UpdateStatusError(Exception):
|
||||
"""Exception raised when a status update fails.
|
||||
|
||||
Each exception will include a message field that describes the
|
||||
error and references to the failed record if available.
|
||||
:param fault_string: String describing the fault.
|
||||
:type fault_string: string
|
||||
:param status_object: The object the fault occurred on.
|
||||
:type status_object: string
|
||||
:param status_object_id: The ID of the object that failed status update.
|
||||
:type status_object_id: string
|
||||
:param status_record: The status update record that caused the fault.
|
||||
:type status_record: string
|
||||
"""
|
||||
fault_string = _("The status update had an unknown error.")
|
||||
status_object = None
|
||||
status_object_id = None
|
||||
status_record = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.fault_string = kwargs.pop('fault_string', self.fault_string)
|
||||
self.status_object = kwargs.pop('status_object', None)
|
||||
self.status_object_id = kwargs.pop('status_object_id', None)
|
||||
self.status_record = kwargs.pop('status_record', None)
|
||||
|
||||
super(UpdateStatusError, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class UpdateStatisticsError(Exception):
|
||||
"""Exception raised when a statistics update fails.
|
||||
|
||||
Each exception will include a message field that describes the
|
||||
error and references to the failed record if available.
|
||||
:param fault_string: String describing the fault.
|
||||
:type fault_string: string
|
||||
:param status_object: The object the fault occurred on.
|
||||
:type status_object: string
|
||||
:param status_object_id: The ID of the object that failed stats update.
|
||||
:type status_object_id: string
|
||||
:param status_record: The stats update record that caused the fault.
|
||||
:type status_record: string
|
||||
"""
|
||||
fault_string = _("The statistics update had an unknown error.")
|
||||
stats_object = None
|
||||
stats_object_id = None
|
||||
stats_record = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.fault_string = kwargs.pop('fault_string',
|
||||
self.fault_string)
|
||||
self.stats_object = kwargs.pop('stats_object', None)
|
||||
self.stats_object_id = kwargs.pop('stats_object_id', None)
|
||||
self.stats_record = kwargs.pop('stats_record', None)
|
||||
|
||||
super(UpdateStatisticsError, self).__init__(*args, **kwargs)
|
|
@ -0,0 +1,481 @@
|
|||
# Copyright 2018 Rackspace, US Inc.
|
||||
#
|
||||
# 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 octavia_lib.api.drivers import exceptions
|
||||
|
||||
# This class describes the abstraction of a provider driver interface.
|
||||
# Load balancing provider drivers will implement this interface.
|
||||
|
||||
|
||||
class ProviderDriver(object):
|
||||
# name is for internal Octavia use and should not be used by drivers
|
||||
name = None
|
||||
|
||||
# Load Balancer
|
||||
def create_vip_port(self, loadbalancer_id, project_id, vip_dictionary):
|
||||
"""Creates a port for a load balancer VIP.
|
||||
|
||||
If the driver supports creating VIP ports, the driver will create a
|
||||
VIP port and return the vip_dictionary populated with the vip_port_id.
|
||||
If the driver does not support port creation, the driver will raise
|
||||
a NotImplementedError.
|
||||
|
||||
:param loadbalancer_id: ID of loadbalancer.
|
||||
:type loadbalancer_id: string
|
||||
:param project_id: The project ID to create the VIP under.
|
||||
:type project_id: string
|
||||
:param: vip_dictionary: The VIP dictionary.
|
||||
:type vip_dictionary: dict
|
||||
:returns: VIP dictionary with vip_port_id.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: The driver does not support creating
|
||||
VIP ports.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support creating VIP '
|
||||
'ports.',
|
||||
operator_fault_string='This provider does not support creating '
|
||||
'VIP ports. Octavia will create it.')
|
||||
|
||||
def loadbalancer_create(self, loadbalancer):
|
||||
"""Creates a new load balancer.
|
||||
|
||||
:param loadbalancer: The load balancer object.
|
||||
:type loadbalancer: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: The driver does not support create.
|
||||
:raises UnsupportedOptionError: The driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support creating '
|
||||
'load balancers.',
|
||||
operator_fault_string='This provider does not support creating '
|
||||
'load balancers. What?')
|
||||
|
||||
def loadbalancer_delete(self, loadbalancer, cascade=False):
|
||||
"""Deletes a load balancer.
|
||||
|
||||
:param loadbalancer: The load balancer to delete.
|
||||
:type loadbalancer: object
|
||||
:param cascade: If True, deletes all child objects (listeners,
|
||||
pools, etc.) in addition to the load balancer.
|
||||
:type cascade: bool
|
||||
:return: Nothing if the delete request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support deleting '
|
||||
'load balancers.',
|
||||
operator_fault_string='This provider does not support deleting '
|
||||
'load balancers.')
|
||||
|
||||
def loadbalancer_failover(self, loadbalancer_id):
|
||||
"""Performs a fail over of a load balancer.
|
||||
|
||||
:param loadbalancer_id: ID of the load balancer to failover.
|
||||
:type loadbalancer_id: string
|
||||
:return: Nothing if the failover request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises: NotImplementedError if driver does not support request.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support failing over '
|
||||
'load balancers.',
|
||||
operator_fault_string='This provider does not support failing '
|
||||
'over load balancers.')
|
||||
|
||||
def loadbalancer_update(self, old_loadbalancer, new_loadbalncer):
|
||||
"""Updates a load balancer.
|
||||
|
||||
:param old_loadbalancer: The baseline load balancer object.
|
||||
:type old_loadbalancer: object
|
||||
:param new_loadbalancer: The updated load balancer object.
|
||||
:type new_loadbalancer: object
|
||||
:return: Nothing if the update request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: The driver does not support request.
|
||||
:raises UnsupportedOptionError: The driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support updating '
|
||||
'load balancers.',
|
||||
operator_fault_string='This provider does not support updating '
|
||||
'load balancers.')
|
||||
|
||||
# Listener
|
||||
def listener_create(self, listener):
|
||||
"""Creates a new listener.
|
||||
|
||||
:param listener: The listener object.
|
||||
:type listener: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support creating '
|
||||
'listeners.',
|
||||
operator_fault_string='This provider does not support creating '
|
||||
'listeners.')
|
||||
|
||||
def listener_delete(self, listener):
|
||||
"""Deletes a listener.
|
||||
|
||||
:param listener: The listener to delete.
|
||||
:type listener: object
|
||||
:return: Nothing if the delete request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support deleting '
|
||||
'listeners.',
|
||||
operator_fault_string='This provider does not support deleting '
|
||||
'listeners.')
|
||||
|
||||
def listener_update(self, old_listener, new_listener):
|
||||
"""Updates a listener.
|
||||
|
||||
:param old_listener: The baseline listener object.
|
||||
:type old_listener: object
|
||||
:param new_listener: The updated listener object.
|
||||
:type new_listener: object
|
||||
:return: Nothing if the update request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support updating '
|
||||
'listeners.',
|
||||
operator_fault_string='This provider does not support updating '
|
||||
'listeners.')
|
||||
|
||||
# Pool
|
||||
def pool_create(self, pool):
|
||||
"""Creates a new pool.
|
||||
|
||||
:param pool: The pool object.
|
||||
:type pool: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support creating '
|
||||
'pools.',
|
||||
operator_fault_string='This provider does not support creating '
|
||||
'pools.')
|
||||
|
||||
def pool_delete(self, pool):
|
||||
"""Deletes a pool and its members.
|
||||
|
||||
:param pool: The pool to delete.
|
||||
:type pool: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support deleting '
|
||||
'pools.',
|
||||
operator_fault_string='This provider does not support deleting '
|
||||
'pools.')
|
||||
|
||||
def pool_update(self, old_pool, new_pool):
|
||||
"""Updates a pool.
|
||||
|
||||
:param pool: The baseline pool object.
|
||||
:type pool: object
|
||||
:param pool: The updated pool object.
|
||||
:type pool: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support updating '
|
||||
'pools.',
|
||||
operator_fault_string='This provider does not support updating '
|
||||
'pools.')
|
||||
|
||||
# Member
|
||||
def member_create(self, member):
|
||||
"""Creates a new member for a pool.
|
||||
|
||||
:param member: The member object.
|
||||
:type member: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support creating '
|
||||
'members.',
|
||||
operator_fault_string='This provider does not support creating '
|
||||
'members.')
|
||||
|
||||
def member_delete(self, member):
|
||||
"""Deletes a pool member.
|
||||
|
||||
:param member: The member to delete.
|
||||
:type member: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support deleting '
|
||||
'members.',
|
||||
operator_fault_string='This provider does not support deleting '
|
||||
'members.')
|
||||
|
||||
def member_update(self, old_member, new_member):
|
||||
"""Updates a pool member.
|
||||
|
||||
:param old_member: The baseline member object.
|
||||
:type old_member: object
|
||||
:param new_member: The updated member object.
|
||||
:type new_member: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support updating '
|
||||
'members.',
|
||||
operator_fault_string='This provider does not support updating '
|
||||
'members.')
|
||||
|
||||
def member_batch_update(self, members):
|
||||
"""Creates, updates, or deletes a set of pool members.
|
||||
|
||||
:param members: List of member objects.
|
||||
:type members: list
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support batch '
|
||||
'updating members.',
|
||||
operator_fault_string='This provider does not support batch '
|
||||
'updating members.')
|
||||
|
||||
# Health Monitor
|
||||
def health_monitor_create(self, healthmonitor):
|
||||
"""Creates a new health monitor.
|
||||
|
||||
:param healthmonitor: The health monitor object.
|
||||
:type healthmonitor: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support creating '
|
||||
'health monitors.',
|
||||
operator_fault_string='This provider does not support creating '
|
||||
'health monitors.')
|
||||
|
||||
def health_monitor_delete(self, healthmonitor):
|
||||
"""Deletes a healthmonitor_id.
|
||||
|
||||
:param healthmonitor: The monitor to delete.
|
||||
:type healthmonitor: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support deleting '
|
||||
'health monitors.',
|
||||
operator_fault_string='This provider does not support deleting '
|
||||
'health monitors.')
|
||||
|
||||
def health_monitor_update(self, old_healthmonitor, new_healthmonitor):
|
||||
"""Updates a health monitor.
|
||||
|
||||
:param old_healthmonitor: The baseline health monitor object.
|
||||
:type old_healthmonitor: object
|
||||
:param new_healthmonitor: The updated health monitor object.
|
||||
:type new_healthmonitor: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support updating '
|
||||
'health monitors.',
|
||||
operator_fault_string='This provider does not support updating '
|
||||
'health monitors.')
|
||||
|
||||
# L7 Policy
|
||||
def l7policy_create(self, l7policy):
|
||||
"""Creates a new L7 policy.
|
||||
|
||||
:param l7policy: The L7 policy object.
|
||||
:type l7policy: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support creating '
|
||||
'l7policies.',
|
||||
operator_fault_string='This provider does not support creating '
|
||||
'l7policies.')
|
||||
|
||||
def l7policy_delete(self, l7policy):
|
||||
"""Deletes an L7 policy.
|
||||
|
||||
:param l7policy: The L7 policy to delete.
|
||||
:type l7policy: object
|
||||
:return: Nothing if the delete request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support deleting '
|
||||
'l7policies.',
|
||||
operator_fault_string='This provider does not support deleting '
|
||||
'l7policies.')
|
||||
|
||||
def l7policy_update(self, old_l7policy, new_l7policy):
|
||||
"""Updates an L7 policy.
|
||||
|
||||
:param old_l7policy: The baseline L7 policy object.
|
||||
:type old_l7policy: object
|
||||
:param new_l7policy: The updated L7 policy object.
|
||||
:type new_l7policy: object
|
||||
:return: Nothing if the update request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support updating '
|
||||
'l7policies.',
|
||||
operator_fault_string='This provider does not support updating '
|
||||
'l7policies.')
|
||||
|
||||
# L7 Rule
|
||||
def l7rule_create(self, l7rule):
|
||||
"""Creates a new L7 rule.
|
||||
|
||||
:param l7rule: The L7 rule object.
|
||||
:type l7rule: object
|
||||
:return: Nothing if the create request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support creating '
|
||||
'l7rules.',
|
||||
operator_fault_string='This provider does not support creating '
|
||||
'l7rules.')
|
||||
|
||||
def l7rule_delete(self, l7rule):
|
||||
"""Deletes an L7 rule.
|
||||
|
||||
:param l7rule: The L7 rule to delete.
|
||||
:type l7rule: object
|
||||
:return: Nothing if the delete request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support deleting '
|
||||
'l7rules.',
|
||||
operator_fault_string='This provider does not support deleting '
|
||||
'l7rules.')
|
||||
|
||||
def l7rule_update(self, old_l7rule, new_l7rule):
|
||||
"""Updates an L7 rule.
|
||||
|
||||
:param old_l7rule: The baseline L7 rule object.
|
||||
:type old_l7rule: object
|
||||
:param new_l7rule: The updated L7 rule object.
|
||||
:type new_l7rule: object
|
||||
:return: Nothing if the update request was accepted.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: if driver does not support request.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support updating '
|
||||
'l7rules.',
|
||||
operator_fault_string='This provider does not support updating '
|
||||
'l7rules.')
|
||||
|
||||
# Flavor
|
||||
def get_supported_flavor_metadata(self):
|
||||
"""Returns a dict of flavor metadata keys supported by this driver.
|
||||
|
||||
The returned dictionary will include key/value pairs, 'name' and
|
||||
'description.'
|
||||
|
||||
:returns: The flavor metadata dictionary
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: The driver does not support flavors.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support getting the '
|
||||
'supported flavor metadata.',
|
||||
operator_fault_string='This provider does not support getting '
|
||||
'the supported flavor metadata.')
|
||||
|
||||
def validate_flavor(self, flavor_metadata):
|
||||
"""Validates if driver can support the flavor.
|
||||
|
||||
:param flavor_metadata: Dictionary with flavor metadata.
|
||||
:type flavor_metadata: dict
|
||||
:return: Nothing if the flavor is valid and supported.
|
||||
:raises DriverError: An unexpected error occurred in the driver.
|
||||
:raises NotImplementedError: The driver does not support flavors.
|
||||
:raises UnsupportedOptionError: if driver does not
|
||||
support one of the configuration options.
|
||||
"""
|
||||
raise exceptions.NotImplementedError(
|
||||
user_fault_string='This provider does not support validating '
|
||||
'flavors.',
|
||||
operator_fault_string='This provider does not support validating '
|
||||
'the supported flavor metadata.')
|
|
@ -0,0 +1,20 @@
|
|||
# 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 oslo_i18n as i18n
|
||||
|
||||
_translators = i18n.TranslatorFactory(domain='octavia-lib')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
|
@ -1,28 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
test_octavia_lib
|
||||
----------------------------------
|
||||
|
||||
Tests for `octavia_lib` module.
|
||||
"""
|
||||
|
||||
from octavia_lib.tests import base
|
||||
|
||||
|
||||
class TestOctavia_lib(base.TestCase):
|
||||
|
||||
def test_something(self):
|
||||
pass
|
|
@ -0,0 +1,412 @@
|
|||
# Copyright 2018 Rackspace, US Inc.
|
||||
#
|
||||
# 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 copy import deepcopy
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from octavia_lib.api.drivers import data_models
|
||||
from octavia_lib.tests.unit import base
|
||||
|
||||
|
||||
class TestProviderDataModels(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestProviderDataModels, self).setUp()
|
||||
|
||||
self.loadbalancer_id = uuidutils.generate_uuid()
|
||||
self.project_id = uuidutils.generate_uuid()
|
||||
self.vip_address = '192.0.2.83'
|
||||
self.vip_network_id = uuidutils.generate_uuid()
|
||||
self.vip_port_id = uuidutils.generate_uuid()
|
||||
self.vip_subnet_id = uuidutils.generate_uuid()
|
||||
self.listener_id = uuidutils.generate_uuid()
|
||||
self.vip_qos_policy_id = uuidutils.generate_uuid()
|
||||
self.default_tls_container_ref = uuidutils.generate_uuid()
|
||||
self.sni_container_ref_1 = uuidutils.generate_uuid()
|
||||
self.sni_container_ref_2 = uuidutils.generate_uuid()
|
||||
self.pool_id = uuidutils.generate_uuid()
|
||||
self.session_persistence = {"cookie_name": "sugar",
|
||||
"type": "APP_COOKIE"}
|
||||
self.member_id = uuidutils.generate_uuid()
|
||||
self.mem_subnet_id = uuidutils.generate_uuid()
|
||||
self.healthmonitor_id = uuidutils.generate_uuid()
|
||||
self.l7policy_id = uuidutils.generate_uuid()
|
||||
self.l7rule_id = uuidutils.generate_uuid()
|
||||
|
||||
self.ref_l7rule = data_models.L7Rule(
|
||||
admin_state_up=True,
|
||||
compare_type='STARTS_WITH',
|
||||
invert=True,
|
||||
key='cookie',
|
||||
l7policy_id=self.l7policy_id,
|
||||
l7rule_id=self.l7rule_id,
|
||||
type='COOKIE',
|
||||
value='chocolate')
|
||||
|
||||
self.ref_l7policy = data_models.L7Policy(
|
||||
action='REJECT',
|
||||
admin_state_up=False,
|
||||
description='A L7 Policy',
|
||||
l7policy_id=self.l7policy_id,
|
||||
listener_id=self.listener_id,
|
||||
name='l7policy',
|
||||
position=1,
|
||||
redirect_pool_id=self.pool_id,
|
||||
redirect_url='/test',
|
||||
rules=[self.ref_l7rule],
|
||||
redirect_prefix='http://example.com')
|
||||
|
||||
self.ref_listener = data_models.Listener(
|
||||
admin_state_up=True,
|
||||
connection_limit=5000,
|
||||
default_pool=None,
|
||||
default_pool_id=None,
|
||||
default_tls_container_data='default_cert_data',
|
||||
default_tls_container_ref=self.default_tls_container_ref,
|
||||
description=data_models.Unset,
|
||||
insert_headers={'X-Forwarded-For': 'true'},
|
||||
l7policies=[self.ref_l7policy],
|
||||
listener_id=self.listener_id,
|
||||
loadbalancer_id=self.loadbalancer_id,
|
||||
name='super_listener',
|
||||
protocol='avian',
|
||||
protocol_port=42,
|
||||
sni_container_data=['sni_cert_data_1', 'sni_cert_data_2'],
|
||||
sni_container_refs=[self.sni_container_ref_1,
|
||||
self.sni_container_ref_2],
|
||||
timeout_client_data=3,
|
||||
timeout_member_connect=4,
|
||||
timeout_member_data=5,
|
||||
timeout_tcp_inspect=6)
|
||||
|
||||
self.ref_lb = data_models.LoadBalancer(
|
||||
admin_state_up=False,
|
||||
description='One great load balancer',
|
||||
flavor={'cake': 'chocolate'},
|
||||
listeners=[self.ref_listener],
|
||||
loadbalancer_id=self.loadbalancer_id,
|
||||
name='favorite_lb',
|
||||
project_id=self.project_id,
|
||||
vip_address=self.vip_address,
|
||||
vip_network_id=self.vip_network_id,
|
||||
vip_port_id=self.vip_port_id,
|
||||
vip_subnet_id=self.vip_subnet_id,
|
||||
vip_qos_policy_id=self.vip_qos_policy_id)
|
||||
|
||||
self.ref_vip = data_models.VIP(
|
||||
vip_address=self.vip_address,
|
||||
vip_network_id=self.vip_network_id,
|
||||
vip_port_id=self.vip_port_id,
|
||||
vip_subnet_id=self.vip_subnet_id,
|
||||
vip_qos_policy_id=self.vip_qos_policy_id)
|
||||
|
||||
self.ref_member = data_models.Member(
|
||||
address='192.0.2.10',
|
||||
admin_state_up=True,
|
||||
member_id=self.member_id,
|
||||
monitor_address='192.0.2.11',
|
||||
monitor_port=8888,
|
||||
name='member',
|
||||
pool_id=self.pool_id,
|
||||
protocol_port=80,
|
||||
subnet_id=self.mem_subnet_id,
|
||||
weight=1,
|
||||
backup=False)
|
||||
|
||||
self.ref_healthmonitor = data_models.HealthMonitor(
|
||||
admin_state_up=False,
|
||||
delay=1,
|
||||
expected_codes='200,202',
|
||||
healthmonitor_id=self.healthmonitor_id,
|
||||
http_method='GET',
|
||||
max_retries=2,
|
||||
max_retries_down=3,
|
||||
name='member',
|
||||
pool_id=self.pool_id,
|
||||
timeout=4,
|
||||
type='HTTP',
|
||||
url_path='/test')
|
||||
|
||||
self.ref_pool = data_models.Pool(
|
||||
admin_state_up=True,
|
||||
description='A pool',
|
||||
healthmonitor=None,
|
||||
lb_algorithm='fast',
|
||||
loadbalancer_id=self.loadbalancer_id,
|
||||
members=[self.ref_member],
|
||||
name='pool',
|
||||
pool_id=self.pool_id,
|
||||
listener_id=self.listener_id,
|
||||
protocol='avian',
|
||||
session_persistence=self.session_persistence)
|
||||
|
||||
self.ref_l7rule_dict = {'admin_state_up': True,
|
||||
'compare_type': 'STARTS_WITH',
|
||||
'invert': True,
|
||||
'key': 'cookie',
|
||||
'l7policy_id': self.l7policy_id,
|
||||
'l7rule_id': self.l7rule_id,
|
||||
'type': 'COOKIE',
|
||||
'value': 'chocolate'}
|
||||
|
||||
self.ref_l7policy_dict = {'action': 'REJECT',
|
||||
'admin_state_up': False,
|
||||
'description': 'A L7 Policy',
|
||||
'l7policy_id': self.l7policy_id,
|
||||
'listener_id': self.listener_id,
|
||||
'name': 'l7policy',
|
||||
'position': 1,
|
||||
'redirect_pool_id': self.pool_id,
|
||||
'redirect_url': '/test',
|
||||
'rules': [self.ref_l7rule_dict],
|
||||
'redirect_prefix': 'http://example.com'}
|
||||
|
||||
self.ref_lb_dict = {'project_id': self.project_id,
|
||||
'flavor': {'cake': 'chocolate'},
|
||||
'vip_network_id': self.vip_network_id,
|
||||
'admin_state_up': False,
|
||||
'loadbalancer_id': self.loadbalancer_id,
|
||||
'vip_port_id': self.vip_port_id,
|
||||
'vip_address': self.vip_address,
|
||||
'description': 'One great load balancer',
|
||||
'vip_subnet_id': self.vip_subnet_id,
|
||||
'name': 'favorite_lb',
|
||||
'vip_qos_policy_id': self.vip_qos_policy_id}
|
||||
|
||||
self.ref_listener_dict = {
|
||||
'admin_state_up': True,
|
||||
'connection_limit': 5000,
|
||||
'default_pool': None,
|
||||
'default_pool_id': None,
|
||||
'default_tls_container_data': 'default_cert_data',
|
||||
'default_tls_container_ref': self.default_tls_container_ref,
|
||||
'description': None,
|
||||
'insert_headers': {'X-Forwarded-For': 'true'},
|
||||
'listener_id': self.listener_id,
|
||||
'l7policies': [self.ref_l7policy_dict],
|
||||
'loadbalancer_id': self.loadbalancer_id,
|
||||
'name': 'super_listener',
|
||||
'protocol': 'avian',
|
||||
'protocol_port': 42,
|
||||
'sni_container_data': ['sni_cert_data_1', 'sni_cert_data_2'],
|
||||
'sni_container_refs': [self.sni_container_ref_1,
|
||||
self.sni_container_ref_2],
|
||||
'timeout_client_data': 3,
|
||||
'timeout_member_connect': 4,
|
||||
'timeout_member_data': 5,
|
||||
'timeout_tcp_inspect': 6}
|
||||
|
||||
self.ref_lb_dict_with_listener = {
|
||||
'admin_state_up': False,
|
||||
'description': 'One great load balancer',
|
||||
'flavor': {'cake': 'chocolate'},
|
||||
'listeners': [self.ref_listener_dict],
|
||||
'loadbalancer_id': self.loadbalancer_id,
|
||||
'name': 'favorite_lb',
|
||||
'project_id': self.project_id,
|
||||
'vip_address': self.vip_address,
|
||||
'vip_network_id': self.vip_network_id,
|
||||
'vip_port_id': self.vip_port_id,
|
||||
'vip_subnet_id': self.vip_subnet_id,
|
||||
'vip_qos_policy_id': self.vip_qos_policy_id}
|
||||
|
||||
self.ref_vip_dict = {
|
||||
'vip_address': self.vip_address,
|
||||
'vip_network_id': self.vip_network_id,
|
||||
'vip_port_id': self.vip_port_id,
|
||||
'vip_subnet_id': self.vip_subnet_id,
|
||||
'vip_qos_policy_id': self.vip_qos_policy_id}
|
||||
|
||||
self.ref_member_dict = {
|
||||
'address': '192.0.2.10',
|
||||
'admin_state_up': True,
|
||||
'member_id': self.member_id,
|
||||
'monitor_address': '192.0.2.11',
|
||||
'monitor_port': 8888,
|
||||
'name': 'member',
|
||||
'pool_id': self.pool_id,
|
||||
'protocol_port': 80,
|
||||
'subnet_id': self.mem_subnet_id,
|
||||
'weight': 1,
|
||||
'backup': False}
|
||||
|
||||
self.ref_healthmonitor_dict = {
|
||||
'admin_state_up': False,
|
||||
'delay': 1,
|
||||
'expected_codes': '200,202',
|
||||
'healthmonitor_id': self.healthmonitor_id,
|
||||
'http_method': 'GET',
|
||||
'max_retries': 2,
|
||||
'max_retries_down': 3,
|
||||
'name': 'member',
|
||||
'pool_id': self.pool_id,
|
||||
'timeout': 4,
|
||||
'type': 'HTTP',
|
||||
'url_path': '/test'}
|
||||
|
||||
self.ref_pool_dict = {
|
||||
'admin_state_up': True,
|
||||
'description': 'A pool',
|
||||
'healthmonitor': self.ref_healthmonitor_dict,
|
||||
'lb_algorithm': 'fast',
|
||||
'loadbalancer_id': self.loadbalancer_id,
|
||||
'members': [self.ref_member_dict],
|
||||
'name': 'pool',
|
||||
'pool_id': self.pool_id,
|
||||
'listener_id': self.listener_id,
|
||||
'protocol': 'avian',
|
||||
'session_persistence': self.session_persistence}
|
||||
|
||||
def test_equality(self):
|
||||
second_ref_lb = deepcopy(self.ref_lb)
|
||||
|
||||
self.assertTrue(self.ref_lb == second_ref_lb)
|
||||
|
||||
second_ref_lb.admin_state_up = True
|
||||
|
||||
self.assertFalse(self.ref_lb == second_ref_lb)
|
||||
|
||||
self.assertFalse(self.ref_lb == self.loadbalancer_id)
|
||||
|
||||
def test_inequality(self):
|
||||
second_ref_lb = deepcopy(self.ref_lb)
|
||||
|
||||
self.assertFalse(self.ref_lb != second_ref_lb)
|
||||
|
||||
second_ref_lb.admin_state_up = True
|
||||
|
||||
self.assertTrue(self.ref_lb != second_ref_lb)
|
||||
|
||||
self.assertTrue(self.ref_lb != self.loadbalancer_id)
|
||||
|
||||
def test_to_dict(self):
|
||||
ref_lb_converted_to_dict = self.ref_lb.to_dict()
|
||||
ref_listener_converted_to_dict = self.ref_listener.to_dict()
|
||||
ref_pool_converted_to_dict = self.ref_pool.to_dict()
|
||||
ref_member_converted_to_dict = self.ref_member.to_dict()
|
||||
ref_healthmon_converted_to_dict = self.ref_healthmonitor.to_dict()
|
||||
ref_l7policy_converted_to_dict = self.ref_l7policy.to_dict()
|
||||
ref_l7rule_converted_to_dict = self.ref_l7rule.to_dict()
|
||||
ref_vip_converted_to_dict = self.ref_vip.to_dict()
|
||||
|
||||
# This test does not recurse, so remove items for the reference
|
||||
# that should not be rendered
|
||||
ref_list_dict = deepcopy(self.ref_listener_dict)
|
||||
ref_list_dict.pop('l7policies', None)
|
||||
ref_list_dict.pop('sni_container_data', None)
|
||||
ref_list_dict.pop('sni_container_refs', None)
|
||||
ref_pool_dict = deepcopy(self.ref_pool_dict)
|
||||
ref_pool_dict['healthmonitor'] = None
|
||||
ref_pool_dict.pop('members', None)
|
||||
ref_l7policy_dict = deepcopy(self.ref_l7policy_dict)
|
||||
ref_l7policy_dict.pop('rules', None)
|
||||
|
||||
# This test does not render unsets, so remove those from the reference
|
||||
ref_list_dict.pop('description', None)
|
||||
|
||||
self.assertEqual(self.ref_lb_dict, ref_lb_converted_to_dict)
|
||||
self.assertEqual(ref_list_dict, ref_listener_converted_to_dict)
|
||||
self.assertEqual(ref_pool_dict, ref_pool_converted_to_dict)
|
||||
self.assertEqual(self.ref_member_dict, ref_member_converted_to_dict)
|
||||
self.assertEqual(self.ref_healthmonitor_dict,
|
||||
ref_healthmon_converted_to_dict)
|
||||
self.assertEqual(ref_l7policy_dict, ref_l7policy_converted_to_dict)
|
||||
self.assertEqual(self.ref_l7rule_dict, ref_l7rule_converted_to_dict)
|
||||
self.assertEqual(self.ref_vip_dict, ref_vip_converted_to_dict)
|
||||
|
||||
def test_to_dict_private_attrs(self):
|
||||
private_dict = {'_test': 'foo'}
|
||||
ref_lb_converted_to_dict = self.ref_lb.to_dict(**private_dict)
|
||||
|
||||
self.assertEqual(self.ref_lb_dict, ref_lb_converted_to_dict)
|
||||
|
||||
def test_to_dict_partial(self):
|
||||
ref_lb = data_models.LoadBalancer(loadbalancer_id=self.loadbalancer_id)
|
||||
ref_lb_dict = {'loadbalancer_id': self.loadbalancer_id}
|
||||
ref_lb_converted_to_dict = ref_lb.to_dict()
|
||||
|
||||
self.assertEqual(ref_lb_dict, ref_lb_converted_to_dict)
|
||||
|
||||
def test_to_dict_render_unsets(self):
|
||||
|
||||
ref_lb_converted_to_dict = self.ref_lb.to_dict(render_unsets=True)
|
||||
|
||||
new_ref_lib_dict = deepcopy(self.ref_lb_dict)
|
||||
new_ref_lib_dict['pools'] = None
|
||||
new_ref_lib_dict['listeners'] = None
|
||||
|
||||
self.assertEqual(new_ref_lib_dict, ref_lb_converted_to_dict)
|
||||
|
||||
def test_to_dict_recursive(self):
|
||||
# Render with unsets is not set, so remove the Unset description
|
||||
ref_lb_dict_with_listener = deepcopy(self.ref_lb_dict_with_listener)
|
||||
ref_lb_dict_with_listener['listeners'][0].pop('description', None)
|
||||
|
||||
ref_lb_converted_to_dict = self.ref_lb.to_dict(recurse=True)
|
||||
|
||||
self.assertEqual(ref_lb_dict_with_listener,
|
||||
ref_lb_converted_to_dict)
|
||||
|
||||
def test_to_dict_recursive_partial(self):
|
||||
ref_lb = data_models.LoadBalancer(
|
||||
loadbalancer_id=self.loadbalancer_id,
|
||||
listeners=[self.ref_listener])
|
||||
|
||||
ref_lb_dict_with_listener = {
|
||||
'loadbalancer_id': self.loadbalancer_id,
|
||||
'listeners': [self.ref_listener_dict]}
|
||||
|
||||
# Render with unsets is not set, so remove the Unset description
|
||||
ref_lb_dict_with_listener = deepcopy(ref_lb_dict_with_listener)
|
||||
ref_lb_dict_with_listener['listeners'][0].pop('description', None)
|
||||
|
||||
ref_lb_converted_to_dict = ref_lb.to_dict(recurse=True)
|
||||
|
||||
self.assertEqual(ref_lb_dict_with_listener, ref_lb_converted_to_dict)
|
||||
|
||||
def test_to_dict_recursive_render_unset(self):
|
||||
ref_lb = data_models.LoadBalancer(
|
||||
admin_state_up=False,
|
||||
description='One great load balancer',
|
||||
flavor={'cake': 'chocolate'},
|
||||
listeners=[self.ref_listener],
|
||||
loadbalancer_id=self.loadbalancer_id,
|
||||
project_id=self.project_id,
|
||||
vip_address=self.vip_address,
|
||||
vip_network_id=self.vip_network_id,
|
||||
vip_port_id=self.vip_port_id,
|
||||
vip_subnet_id=self.vip_subnet_id,
|
||||
vip_qos_policy_id=self.vip_qos_policy_id)
|
||||
|
||||
ref_lb_dict_with_listener = deepcopy(self.ref_lb_dict_with_listener)
|
||||
ref_lb_dict_with_listener['pools'] = None
|
||||
ref_lb_dict_with_listener['name'] = None
|
||||
|
||||
ref_lb_converted_to_dict = ref_lb.to_dict(recurse=True,
|
||||
render_unsets=True)
|
||||
|
||||
self.assertEqual(ref_lb_dict_with_listener,
|
||||
ref_lb_converted_to_dict)
|
||||
|
||||
def test_from_dict(self):
|
||||
lb_object = data_models.LoadBalancer.from_dict(self.ref_lb_dict)
|
||||
|
||||
self.assertEqual(self.ref_lb, lb_object)
|
||||
|
||||
def test_unset_bool(self):
|
||||
self.assertFalse(data_models.Unset)
|
||||
|
||||
def test_unset_repr(self):
|
||||
self.assertEqual('Unset', repr(data_models.Unset))
|
|
@ -0,0 +1,250 @@
|
|||
# Copyright 2018 Rackspace, US Inc.
|
||||
#
|
||||
# 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 mock
|
||||
from mock import call
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia_lib.api.drivers import driver_lib
|
||||
from octavia_lib.api.drivers import exceptions as driver_exceptions
|
||||
from octavia_lib.tests.unit import base
|
||||
|
||||
|
||||
class TestDriverLib(base.TestCase):
|
||||
@mock.patch('octavia.db.repositories.L7RuleRepository')
|
||||
@mock.patch('octavia.db.repositories.L7PolicyRepository')
|
||||
@mock.patch('octavia.db.repositories.HealthMonitorRepository')
|
||||
@mock.patch('octavia.db.repositories.MemberRepository')
|
||||
@mock.patch('octavia.db.repositories.PoolRepository')
|
||||
@mock.patch('octavia.db.repositories.ListenerRepository')
|
||||
@mock.patch('octavia.db.repositories.LoadBalancerRepository')
|
||||
@mock.patch('octavia.db.api.get_session')
|
||||
def setUp(self, mock_get_session, mock_lb_repo, mock_list_repo,
|
||||
mock_pool_repo, mock_member_repo, mock_health_repo,
|
||||
mock_l7p_repo, mock_l7r_repo):
|
||||
super(TestDriverLib, self).setUp()
|
||||
self.mock_session = "FAKE_DB_SESSION"
|
||||
mock_get_session.return_value = self.mock_session
|
||||
lb_mock = mock.MagicMock()
|
||||
mock_lb_repo.return_value = lb_mock
|
||||
self.mock_lb_repo = lb_mock
|
||||
list_mock = mock.MagicMock()
|
||||
mock_list_repo.return_value = list_mock
|
||||
self.mock_list_repo = list_mock
|
||||
pool_mock = mock.MagicMock()
|
||||
mock_pool_repo.return_value = pool_mock
|
||||
self.mock_pool_repo = pool_mock
|
||||
member_mock = mock.MagicMock()
|
||||
mock_member_repo.return_value = member_mock
|
||||
self.mock_member_repo = member_mock
|
||||
health_mock = mock.MagicMock()
|
||||
mock_health_repo.return_value = health_mock
|
||||
self.mock_health_repo = health_mock
|
||||
l7p_mock = mock.MagicMock()
|
||||
mock_l7p_repo.return_value = l7p_mock
|
||||
self.mock_l7p_repo = l7p_mock
|
||||
l7r_mock = mock.MagicMock()
|
||||
mock_l7r_repo.return_value = l7r_mock
|
||||
self.mock_l7r_repo = l7r_mock
|
||||
self.driver_lib = driver_lib.DriverLibrary()
|
||||
listener_stats_list = [{"id": 1, "active_connections": 10,
|
||||
"bytes_in": 20,
|
||||
"bytes_out": 30,
|
||||
"request_errors": 40,
|
||||
"total_connections": 50},
|
||||
{"id": 2, "active_connections": 60,
|
||||
"bytes_in": 70,
|
||||
"bytes_out": 80,
|
||||
"request_errors": 90,
|
||||
"total_connections": 100}]
|
||||
self.listener_stats_dict = {"listeners": listener_stats_list}
|
||||
|
||||
@mock.patch('octavia.common.utils.get_network_driver')
|
||||
def test_check_for_lb_vip_deallocate(self, mock_get_driver):
|
||||
mock_repo = mock.MagicMock()
|
||||
mock_lb = mock.MagicMock()
|
||||
|
||||
# Test VIP not owned by Octavia
|
||||
mock_lb.vip.octavia_owned = False
|
||||
mock_repo.get.return_value = mock_lb
|
||||
self.driver_lib._check_for_lb_vip_deallocate(mock_repo, 4)
|
||||
mock_get_driver.assert_not_called()
|
||||
|
||||
# Test VIP is owned by Octavia
|
||||
mock_lb.vip.octavia_owned = True
|
||||
mock_repo.get.return_value = mock_lb
|
||||
mock_net_driver = mock.MagicMock()
|
||||
mock_get_driver.return_value = mock_net_driver
|
||||
self.driver_lib._check_for_lb_vip_deallocate(mock_repo, 4)
|
||||
mock_net_driver.deallocate_vip.assert_called_once_with(mock_lb.vip)
|
||||
|
||||
@mock.patch('octavia_lib.api.drivers.driver_lib.DriverLibrary.'
|
||||
'_check_for_lb_vip_deallocate')
|
||||
def test_process_status_update(self, mock_deallocate):
|
||||
mock_repo = mock.MagicMock()
|
||||
list_dict = {"id": 2, constants.PROVISIONING_STATUS: constants.ACTIVE,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
list_prov_dict = {"id": 2,
|
||||
constants.PROVISIONING_STATUS: constants.ACTIVE}
|
||||
list_oper_dict = {"id": 2,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
list_deleted_dict = {
|
||||
"id": 2, constants.PROVISIONING_STATUS: constants.DELETED,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
|
||||
# Test with full record
|
||||
self.driver_lib._process_status_update(mock_repo, 'FakeName',
|
||||
list_dict)
|
||||
mock_repo.update.assert_called_once_with(
|
||||
self.mock_session, 2, provisioning_status=constants.ACTIVE,
|
||||
operating_status=constants.ONLINE)
|
||||
mock_repo.delete.assert_not_called()
|
||||
|
||||
# Test with only provisioning status record
|
||||
mock_repo.reset_mock()
|
||||
self.driver_lib._process_status_update(mock_repo, 'FakeName',
|
||||
list_prov_dict)
|
||||
mock_repo.update.assert_called_once_with(
|
||||
self.mock_session, 2, provisioning_status=constants.ACTIVE)
|
||||
mock_repo.delete.assert_not_called()
|
||||
|
||||
# Test with only operating status record
|
||||
mock_repo.reset_mock()
|
||||
self.driver_lib._process_status_update(mock_repo, 'FakeName',
|
||||
list_oper_dict)
|
||||
mock_repo.update.assert_called_once_with(
|
||||
self.mock_session, 2, operating_status=constants.ONLINE)
|
||||
mock_repo.delete.assert_not_called()
|
||||
|
||||
# Test with deleted but delete_record False
|
||||
mock_repo.reset_mock()
|
||||
self.driver_lib._process_status_update(mock_repo, 'FakeName',
|
||||
list_deleted_dict)
|
||||
mock_repo.update.assert_called_once_with(
|
||||
self.mock_session, 2, provisioning_status=constants.DELETED,
|
||||
operating_status=constants.ONLINE)
|
||||
mock_repo.delete.assert_not_called()
|
||||
|
||||
# Test with an empty update
|
||||
mock_repo.reset_mock()
|
||||
self.driver_lib._process_status_update(mock_repo, 'FakeName',
|
||||
{"id": 2})
|
||||
mock_repo.update.assert_not_called()
|
||||
mock_repo.delete.assert_not_called()
|
||||
|
||||
# Test with deleted and delete_record True
|
||||
mock_repo.reset_mock()
|
||||
self.driver_lib._process_status_update(
|
||||
mock_repo, 'FakeName', list_deleted_dict, delete_record=True)
|
||||
mock_repo.delete.assert_called_once_with(self.mock_session, id=2)
|
||||
mock_repo.update.assert_not_called()
|
||||
|
||||
# Test with LB Delete
|
||||
mock_repo.reset_mock()
|
||||
self.driver_lib._process_status_update(
|
||||
mock_repo, constants.LOADBALANCERS, list_deleted_dict)
|
||||
mock_deallocate.assert_called_once_with(mock_repo, 2)
|
||||
|
||||
# Test with an exception
|
||||
mock_repo.reset_mock()
|
||||
mock_repo.update.side_effect = Exception('boom')
|
||||
self.assertRaises(driver_exceptions.UpdateStatusError,
|
||||
self.driver_lib._process_status_update,
|
||||
mock_repo, 'FakeName', list_dict)
|
||||
|
||||
# Test with no ID record
|
||||
mock_repo.reset_mock()
|
||||
self.assertRaises(driver_exceptions.UpdateStatusError,
|
||||
self.driver_lib._process_status_update,
|
||||
mock_repo, 'FakeName', {"fake": "data"})
|
||||
|
||||
@mock.patch('octavia_lib.api.drivers.driver_lib.DriverLibrary.'
|
||||
'_process_status_update')
|
||||
def test_update_loadbalancer_status(self, mock_status_update):
|
||||
lb_dict = {"id": 1, constants.PROVISIONING_STATUS: constants.ACTIVE,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
list_dict = {"id": 2, constants.PROVISIONING_STATUS: constants.ACTIVE,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
pool_dict = {"id": 3, constants.PROVISIONING_STATUS: constants.ACTIVE,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
member_dict = {"id": 4,
|
||||
constants.PROVISIONING_STATUS: constants.ACTIVE,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
hm_dict = {"id": 5, constants.PROVISIONING_STATUS: constants.ACTIVE,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
l7p_dict = {"id": 6, constants.PROVISIONING_STATUS: constants.ACTIVE,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
l7r_dict = {"id": 7, constants.PROVISIONING_STATUS: constants.ACTIVE,
|
||||
constants.OPERATING_STATUS: constants.ONLINE}
|
||||
status_dict = {constants.LOADBALANCERS: [lb_dict],
|
||||
constants.LISTENERS: [list_dict],
|
||||
constants.POOLS: [pool_dict],
|
||||
constants.MEMBERS: [member_dict],
|
||||
constants.HEALTHMONITORS: [hm_dict],
|
||||
constants.L7POLICIES: [l7p_dict],
|
||||
constants.L7RULES: [l7r_dict]}
|
||||
|
||||
self.driver_lib.update_loadbalancer_status(status_dict)
|
||||
|
||||
calls = [call(self.mock_member_repo, constants.MEMBERS, member_dict,
|
||||
delete_record=True),
|
||||
call(self.mock_health_repo, constants.HEALTHMONITORS,
|
||||
hm_dict, delete_record=True),
|
||||
call(self.mock_pool_repo, constants.POOLS, pool_dict,
|
||||
delete_record=True),
|
||||
call(self.mock_l7r_repo, constants.L7RULES, l7r_dict,
|
||||
delete_record=True),
|
||||
call(self.mock_l7p_repo, constants.L7POLICIES, l7p_dict,
|
||||
delete_record=True),
|
||||
call(self.mock_list_repo, constants.LISTENERS, list_dict,
|
||||
delete_record=True),
|
||||
call(self.mock_lb_repo, constants.LOADBALANCERS,
|
||||
lb_dict)]
|
||||
mock_status_update.assert_has_calls(calls)
|
||||
|
||||
mock_status_update.reset_mock()
|
||||
self.driver_lib.update_loadbalancer_status({})
|
||||
mock_status_update.assert_not_called()
|
||||
|
||||
@mock.patch('octavia.db.repositories.ListenerStatisticsRepository.replace')
|
||||
def test_update_listener_statistics(self, mock_replace):
|
||||
self.driver_lib.update_listener_statistics(self.listener_stats_dict)
|
||||
calls = [call(self.mock_session, 1, 1, active_connections=10,
|
||||
bytes_in=20, bytes_out=30, request_errors=40,
|
||||
total_connections=50),
|
||||
call(self.mock_session, 2, 2, active_connections=60,
|
||||
bytes_in=70, bytes_out=80, request_errors=90,
|
||||
total_connections=100)]
|
||||
mock_replace.assert_has_calls(calls)
|
||||
|
||||
mock_replace.reset_mock()
|
||||
self.driver_lib.update_listener_statistics({})
|
||||
mock_replace.assert_not_called()
|
||||
|
||||
# Test missing ID
|
||||
bad_id_dict = {"listeners": [{"notID": "one"}]}
|
||||
self.assertRaises(driver_exceptions.UpdateStatisticsError,
|
||||
self.driver_lib.update_listener_statistics,
|
||||
bad_id_dict)
|
||||
|
||||
# Coverage doesn't like this test as part of the above test
|
||||
# So, broke it out in it's own test
|
||||
@mock.patch('octavia.db.repositories.ListenerStatisticsRepository.replace')
|
||||
def test_update_listener_statistics_exception(self, mock_replace):
|
||||
|
||||
# Test stats exception
|
||||
mock_replace.side_effect = Exception('boom')
|
||||
self.assertRaises(driver_exceptions.UpdateStatisticsError,
|
||||
self.driver_lib.update_listener_statistics,
|
||||
self.listener_stats_dict)
|
|
@ -0,0 +1,88 @@
|
|||
# Copyright 2018 Rackspace, US Inc.
|
||||
#
|
||||
# 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 octavia_lib.api.drivers import exceptions
|
||||
from octavia_lib.tests.unit import base
|
||||
|
||||
|
||||
class TestProviderExceptions(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestProviderExceptions, self).setUp()
|
||||
self.user_fault_string = 'Bad driver'
|
||||
self.operator_fault_string = 'Fix bad driver.'
|
||||
self.fault_object = 'MCP'
|
||||
self.fault_object_id = '-1'
|
||||
self.fault_record = 'skip'
|
||||
|
||||
def test_DriverError(self):
|
||||
driver_error = exceptions.DriverError(
|
||||
user_fault_string=self.user_fault_string,
|
||||
operator_fault_string=self.operator_fault_string)
|
||||
|
||||
self.assertEqual(self.user_fault_string,
|
||||
driver_error.user_fault_string)
|
||||
self.assertEqual(self.operator_fault_string,
|
||||
driver_error.operator_fault_string)
|
||||
self.assertIsInstance(driver_error, Exception)
|
||||
|
||||
def test_NotImplementedError(self):
|
||||
not_implemented_error = exceptions.NotImplementedError(
|
||||
user_fault_string=self.user_fault_string,
|
||||
operator_fault_string=self.operator_fault_string)
|
||||
|
||||
self.assertEqual(self.user_fault_string,
|
||||
not_implemented_error.user_fault_string)
|
||||
self.assertEqual(self.operator_fault_string,
|
||||
not_implemented_error.operator_fault_string)
|
||||
self.assertIsInstance(not_implemented_error, Exception)
|
||||
|
||||
def test_UnsupportedOptionError(self):
|
||||
unsupported_option_error = exceptions.UnsupportedOptionError(
|
||||
user_fault_string=self.user_fault_string,
|
||||
operator_fault_string=self.operator_fault_string)
|
||||
|
||||
self.assertEqual(self.user_fault_string,
|
||||
unsupported_option_error.user_fault_string)
|
||||
self.assertEqual(self.operator_fault_string,
|
||||
unsupported_option_error.operator_fault_string)
|
||||
self.assertIsInstance(unsupported_option_error, Exception)
|
||||
|
||||
def test_UpdateStatusError(self):
|
||||
update_status_error = exceptions.UpdateStatusError(
|
||||
fault_string=self.user_fault_string,
|
||||
status_object=self.fault_object,
|
||||
status_object_id=self.fault_object_id,
|
||||
status_record=self.fault_record)
|
||||
|
||||
self.assertEqual(self.user_fault_string,
|
||||
update_status_error.fault_string)
|
||||
self.assertEqual(self.fault_object, update_status_error.status_object)
|
||||
self.assertEqual(self.fault_object_id,
|
||||
update_status_error.status_object_id)
|
||||
self.assertEqual(self.fault_record, update_status_error.status_record)
|
||||
|
||||
def test_UpdateStatisticsError(self):
|
||||
update_stats_error = exceptions.UpdateStatisticsError(
|
||||
fault_string=self.user_fault_string,
|
||||
stats_object=self.fault_object,
|
||||
stats_object_id=self.fault_object_id,
|
||||
stats_record=self.fault_record)
|
||||
|
||||
self.assertEqual(self.user_fault_string,
|
||||
update_stats_error.fault_string)
|
||||
self.assertEqual(self.fault_object, update_stats_error.stats_object)
|
||||
self.assertEqual(self.fault_object_id,
|
||||
update_stats_error.stats_object_id)
|
||||
self.assertEqual(self.fault_record, update_stats_error.stats_record)
|
|
@ -0,0 +1,157 @@
|
|||
# Copyright 2018 Rackspace, US Inc.
|
||||
#
|
||||
# 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 octavia_lib.api.drivers import exceptions
|
||||
from octavia_lib.api.drivers import provider_base as driver_base
|
||||
from octavia_lib.tests.unit import base
|
||||
|
||||
|
||||
class TestProviderBase(base.TestCase):
|
||||
"""Test base methods.
|
||||
|
||||
Tests that methods not implemented by the drivers raise
|
||||
NotImplementedError.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestProviderBase, self).setUp()
|
||||
self.driver = driver_base.ProviderDriver()
|
||||
|
||||
def test_create_vip_port(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.create_vip_port,
|
||||
False, False, False)
|
||||
|
||||
def test_loadbalancer_create(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.loadbalancer_create,
|
||||
False)
|
||||
|
||||
def test_loadbalancer_delete(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.loadbalancer_delete,
|
||||
False)
|
||||
|
||||
def test_loadbalancer_failover(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.loadbalancer_failover,
|
||||
False)
|
||||
|
||||
def test_loadbalancer_update(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.loadbalancer_update,
|
||||
False, False)
|
||||
|
||||
def test_listener_create(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.listener_create,
|
||||
False)
|
||||
|
||||
def test_listener_delete(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.listener_delete,
|
||||
False)
|
||||
|
||||
def test_listener_update(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.listener_update,
|
||||
False, False)
|
||||
|
||||
def test_pool_create(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.pool_create,
|
||||
False)
|
||||
|
||||
def test_pool_delete(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.pool_delete,
|
||||
False)
|
||||
|
||||
def test_pool_update(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.pool_update,
|
||||
False, False)
|
||||
|
||||
def test_member_create(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.member_create,
|
||||
False)
|
||||
|
||||
def test_member_delete(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.member_delete,
|
||||
False)
|
||||
|
||||
def test_member_update(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.member_update,
|
||||
False, False)
|
||||
|
||||
def test_member_batch_update(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.member_batch_update,
|
||||
False)
|
||||
|
||||
def test_health_monitor_create(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.health_monitor_create,
|
||||
False)
|
||||
|
||||
def test_health_monitor_delete(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.health_monitor_delete,
|
||||
False)
|
||||
|
||||
def test_health_monitor_update(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.health_monitor_update,
|
||||
False, False)
|
||||
|
||||
def test_l7policy_create(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.l7policy_create,
|
||||
False)
|
||||
|
||||
def test_l7policy_delete(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.l7policy_delete,
|
||||
False)
|
||||
|
||||
def test_l7policy_update(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.l7policy_update,
|
||||
False, False)
|
||||
|
||||
def test_l7rule_create(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.l7rule_create,
|
||||
False)
|
||||
|
||||
def test_l7rule_delete(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.l7rule_delete,
|
||||
False)
|
||||
|
||||
def test_l7rule_update(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.l7rule_update,
|
||||
False, False)
|
||||
|
||||
def test_get_supported_flavor_metadata(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.get_supported_flavor_metadata)
|
||||
|
||||
def test_validate_flavor(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.validate_flavor,
|
||||
False)
|
|
@ -15,9 +15,14 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from oslotest import base
|
||||
|
||||
|
||||
class TestCase(base.BaseTestCase):
|
||||
|
||||
"""Test case base class for all unit tests."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
self.addCleanup(mock.patch.stopall)
|
|
@ -2,4 +2,7 @@
|
|||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
oslo.i18n>=3.15.3 # Apache-2.0
|
||||
oslo.log>=3.36.0 # Apache-2.0
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
six>=1.10.0 # MIT
|
||||
|
|
|
@ -4,8 +4,15 @@
|
|||
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
|
||||
bandit>=1.1.0 # Apache-2.0
|
||||
coverage>=4.0,!=4.4 # Apache-2.0
|
||||
doc8>=0.6.0 # Apache-2.0
|
||||
pylint==1.9.2 # GPLv2
|
||||
python-subunit>=1.0.0 # Apache-2.0/BSD
|
||||
oslo.utils>=3.33.0 # Apache-2.0
|
||||
oslotest>=3.2.0 # Apache-2.0
|
||||
stestr>=2.0.0 # Apache-2.0
|
||||
testtools>=2.2.0 # MIT
|
||||
|
||||
# Temporary workaround until we get the updates endpoint setup
|
||||
octavia
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
#!/bin/sh
|
||||
# This script is copied from neutron and adapted for octavia-lib.
|
||||
set -eu
|
||||
|
||||
usage () {
|
||||
echo "Usage: $0 [OPTION]..."
|
||||
echo "Run octavia-lib's coding check(s)"
|
||||
echo ""
|
||||
echo " -Y, --pylint [<basecommit>] Run pylint check on the entire octavia-lib module or just files changed in basecommit (e.g. HEAD~1)"
|
||||
echo " -h, --help Print this usage message"
|
||||
echo
|
||||
exit 0
|
||||
}
|
||||
|
||||
join_args() {
|
||||
if [ -z "$scriptargs" ]; then
|
||||
scriptargs="$opt"
|
||||
else
|
||||
scriptargs="$scriptargs $opt"
|
||||
fi
|
||||
}
|
||||
|
||||
process_options () {
|
||||
i=1
|
||||
while [ $i -le $# ]; do
|
||||
eval opt=\$$i
|
||||
case $opt in
|
||||
-h|--help) usage;;
|
||||
-Y|--pylint) pylint=1;;
|
||||
*) join_args;;
|
||||
esac
|
||||
i=$((i+1))
|
||||
done
|
||||
}
|
||||
|
||||
run_pylint () {
|
||||
local target="${scriptargs:-all}"
|
||||
|
||||
if [ "$target" = "all" ]; then
|
||||
files="octavia_lib"
|
||||
else
|
||||
case "$target" in
|
||||
*HEAD~[0-9]*) files=$(git diff --diff-filter=AM --name-only $target -- "*.py");;
|
||||
*) echo "$target is an unrecognized basecommit"; exit 1;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo "Running pylint..."
|
||||
echo "You can speed this up by running it on 'HEAD~[0-9]' (e.g. HEAD~1, this change only)..."
|
||||
if [ -n "${files}" ]; then
|
||||
pylint --max-nested-blocks 7 --extension-pkg-whitelist netifaces --rcfile=.pylintrc --output-format=colorized ${files}
|
||||
else
|
||||
echo "No python changes in this commit, pylint check not required."
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
scriptargs=
|
||||
pylint=1
|
||||
|
||||
process_options $@
|
||||
|
||||
if [ $pylint -eq 1 ]; then
|
||||
run_pylint
|
||||
exit 0
|
||||
fi
|
9
tox.ini
9
tox.ini
|
@ -18,6 +18,11 @@ commands = stestr run {posargs}
|
|||
[testenv:pep8]
|
||||
basepython = python3
|
||||
commands = flake8 {posargs}
|
||||
doc8 --ignore-path doc/source/contributor/modules \
|
||||
doc/source octavia_lib HACKING.rst README.rst
|
||||
# Run security linter
|
||||
bandit -r octavia_lib -ll -ii -x octavia_lib/tests
|
||||
{toxinidir}/tools/coding-checks.sh --pylint '{posargs}'
|
||||
|
||||
[testenv:venv]
|
||||
basepython = python3
|
||||
|
@ -33,6 +38,7 @@ commands =
|
|||
coverage combine
|
||||
coverage html -d cover
|
||||
coverage xml -o cover/coverage.xml
|
||||
coverage report --fail-under=95 --skip-covered
|
||||
|
||||
[testenv:docs]
|
||||
basepython = python3
|
||||
|
@ -65,5 +71,4 @@ deps =
|
|||
-r{toxinidir}/requirements.txt
|
||||
whitelist_externals = sh
|
||||
commands =
|
||||
sh -c 'OS_TEST_PATH={toxinidir}/octavia/tests/unit stestr run {posargs}'
|
||||
sh -c 'OS_TEST_PATH={toxinidir}/octavia/tests/functional stestr run {posargs}'
|
||||
sh -c 'OS_TEST_PATH={toxinidir}/octavia_lib/tests/unit stestr run {posargs}'
|
||||
|
|
Loading…
Reference in New Issue