Skip to content

Commit

Permalink
Merge branch 'master' of github.com:a10networks/a10-octavia into stab…
Browse files Browse the repository at this point in the history
…le/stein
  • Loading branch information
ytsai-a10 committed Feb 3, 2021
2 parents 47e3538 + c72113f commit 563b092
Show file tree
Hide file tree
Showing 57 changed files with 3,556 additions and 140 deletions.
99 changes: 97 additions & 2 deletions a10_octavia/api/drivers/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@
# License for the specific language governing permissions and limitations
# under the License.

from jsonschema import exceptions as js_exceptions
from jsonschema import validate
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging as messaging

from octavia.api.drivers import provider_base as driver_base
from octavia.common import constants
from octavia_lib.api.drivers import exceptions
from octavia_lib.api.drivers import provider_base as driver_base

from a10_octavia.api.drivers import flavor_schema


CONF = cfg.CONF
CONF.import_group('oslo_messaging', 'octavia.common.config')
Expand All @@ -39,7 +45,8 @@ def __init__(self):
# Load Balancer
def loadbalancer_create(self, loadbalancer):
LOG.info('A10 provider load balancer loadbalancer: %s.', loadbalancer.__dict__)
payload = {constants.LOAD_BALANCER_ID: loadbalancer.loadbalancer_id}
payload = {constants.LOAD_BALANCER_ID: loadbalancer.loadbalancer_id,
constants.FLAVOR: loadbalancer.flavor}
self.client.cast({}, 'create_load_balancer', **payload)

def loadbalancer_delete(self, loadbalancer, cascade=False):
Expand Down Expand Up @@ -209,3 +216,91 @@ def l7rule_update(self, old_l7rule, new_l7rule):
payload = {constants.L7RULE_ID: l7rule_id,
constants.L7RULE_UPDATES: l7rule_dict}
self.client.cast({}, 'update_l7rule', **payload)

# Flavor
def get_supported_flavor_metadata(self):
try:
dict = {}
for obj in flavor_schema.SUPPORTED_FLAVOR_SCHEMA['properties']:
obj_v = flavor_schema.SUPPORTED_FLAVOR_SCHEMA['properties'][obj]
if 'description' in obj_v:
dict[obj] = obj_v.get('description')
if 'properties' in obj_v:
props = obj_v['properties']
for k, v in props.items():
if 'description' in v:
dict[obj + '.' + k] = v.get('description')
return dict
except Exception as e:
raise exceptions.DriverError(
user_fault_string='Failed to get the supported flavor '
'metadata due to: {}'.format(str(e)),
operator_fault_string='Failed to get the supported flavor '
'metadata due to: {}'.format(str(e)))

def validate_flavor(self, flavor_dict):
try:
validate(flavor_dict, flavor_schema.SUPPORTED_FLAVOR_SCHEMA)

# validate flavor for slb objects
if 'virtual-server' in flavor_dict:
flavor = flavor_dict['virtual-server']
if 'name' in flavor:
raise Exception('axapi key \'name\' is not allowed')
if 'ip-address' in flavor:
raise Exception('axapi key \'ip-address\' is not supported yet')
if 'virtual-port' in flavor_dict:
flavor = flavor_dict['virtual-port']
if 'name' in flavor:
raise Exception('axapi key \'name\' is not allowed')
if 'port-number' in flavor:
raise Exception('axapi key \'port-number\' is not allowed')
if 'protocol' in flavor:
raise Exception('axapi key \'protocol\' is not allowed')
if 'service-group' in flavor_dict:
flavor = flavor_dict['service-group']
if 'name' in flavor:
raise Exception('axapi key \'name\' is not allowed')
if 'server' in flavor_dict:
flavor = flavor_dict['server']
if 'name' in flavor:
raise Exception('axapi key \'name\' is not allowed')
if 'health-monitor' in flavor_dict:
flavor = flavor_dict['health-monitor']
if 'name' in flavor:
raise Exception('axapi key \'name\' is not allowed')

# validate nat-pool and nat-pool-list keys
if 'nat-pool' in flavor_dict:
nat = flavor_dict['nat-pool']
if 'pool-name' not in nat:
raise Exception('pool-name is required for nat-pool flavor')
if 'start-address' not in nat:
raise Exception('start-address is required for nat-pool flavor')
if 'end-address' not in nat:
raise Exception('end-address is required for nat-pool flavor')
if 'netmask' not in nat:
raise Exception('netmask is required for nat-pool flavor')
if 'nat-pool-list' in flavor_dict:
for nat in flavor_dict['nat-pool-list']:
if 'pool-name' not in nat:
raise Exception('pool-name is required for nat-pool-list flavor')
if 'start-address' not in nat:
raise Exception('start-address is required for nat-pool-list flavor')
if 'end-address' not in nat:
raise Exception('end-address is required for nat-pool-list flavor')
if 'netmask' not in nat:
raise Exception('netmask is required for nat-pool-list flavor')
except js_exceptions.ValidationError as e:
error_object = ''
if e.relative_path:
error_object = '{} '.format(e.relative_path[0])
raise exceptions.UnsupportedOptionError(
user_fault_string='{0}{1}'.format(error_object, e.message),
operator_fault_string=str(e))
except Exception as e:
raise exceptions.DriverError(
user_fault_string='Failed to validate the flavor metadata '
'due to: {}'.format(str(e)),
operator_fault_string='Failed to validate the flavor metadata '
'due to: {}'.format(str(e)))
184 changes: 184 additions & 0 deletions a10_octavia/api/drivers/flavor_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Copyright 2020, A10 Networks
#
# 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.

SUPPORTED_FLAVOR_SCHEMA = {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Octavia Amphora Driver Flavor Metadata Schema",
"description": "This schema is used to validate new flavor profiles "
"submitted for use in an a10 driver flavor profile.",
"type": "object",
"additionalProperties": True,
"properties": {
"virtual-server": {
"type": "object",
"description": "Specify axapi that will apply to the slb virtual-server",
"properties": {
"name-expressions": {
"type": "array",
"description": "Specify name expression to match loadbalancers "
"and axapi that will apply to the slb virtual-server",
"items": {
"type": "object",
"properties": {
"regex": {
"type": "string",
},
"json": {
"type": "object",
},
}
}
},
}
},
"virtual-port": {
"type": "object",
"description": "Specify axapi that will apply to the vport",
"properties": {
"name-expressions": {
"type": "array",
"description": "Specify name expression to match listeners "
"and axapi that will apply to the vport",
"items": {
"type": "object",
"properties": {
"regex": {
"type": "string",
},
"json": {
"type": "object",
},
}
}
},
"template-tcp": {
"type": "string",
},
"template-http": {
"type": "string",
},
"template-virtual-port": {
"type": "string",
},
}
},
"service-group": {
"type": "object",
"description": "Specify axapi that will apply to the service-group",
"properties": {
"name-expressions": {
"type": "array",
"description": "Specify name expression to match pools "
"and axapi that will apply to the service-group",
"items": {
"type": "object",
"properties": {
"regex": {
"type": "string",
},
"json": {
"type": "object",
},
}
}
},
}
},
"server": {
"type": "object",
"description": "Specify axapi that will apply to the server",
"properties": {
"name-expressions": {
"type": "array",
"description": "Specify name expression to match members "
"and axapi that will apply to the server",
"items": {
"type": "object",
"properties": {
"regex": {
"type": "string",
},
"json": {
"type": "object",
},
}
}
},
"conn-limit": {
"type": "integer",
"minimum": 1,
"maximum": 64000000,
"default": 64000000,
},
"conn-resume": {
"type": "integer",
"minimum": 1,
"maximum": 1000000,
}
}
},
"health-monitor": {
"type": "object",
"description": "Specify axapi that will apply to the health monitor",
"properties": {
"name-expressions": {
"type": "array",
"description": "Specify name expression to match healthmonitor "
"and axapi that will apply to the health monitor",
"items": {
"type": "object",
"properties": {
"regex": {
"type": "string",
},
"json": {
"type": "object",
},
}
}
},
}
},
"nat-pool": {
"type": "object",
"description": "Specify axapi of default nat pool for loadbalancer",
"properties": {
"pool-name": {
"type": "string",
},
"start-address": {
"type": "string",
},
"end-address": {
"type": "string",
},
"netmask": {
"type": "string",
}
}
},
"nat-pool-list": {
"type": "array",
"description": "Specify axapi of nat pools for loadbalancer",
"items": {
"type": "object",
"properties": {
"pool-name": {
"type": "string",
}
}
}
},
}
}
3 changes: 2 additions & 1 deletion a10_octavia/cmd/a10_health_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ def hm_exit(*args, **kwargs):
signal.signal(signal.SIGINT, hm_exit)
LOG.warning("Pausing before starting health check")
exit_event.wait(CONF.a10_health_manager.heartbeat_timeout)
health_check.start()
# TODO(ytsai-a10) Update health manager code from octavia and enable it
# health_check.start()


def main():
Expand Down
34 changes: 31 additions & 3 deletions a10_octavia/cmd/a10_house_keeper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# under the License.


import datetime
from datetime import datetime
import signal
import sys
import threading
Expand All @@ -32,6 +32,7 @@

spare_amp_thread_event = threading.Event()
db_cleanup_thread_event = threading.Event()
write_memory_thread_event = threading.Event()


def spare_amphora_check():
Expand Down Expand Up @@ -73,6 +74,26 @@ def db_cleanup():
db_cleanup_thread_event.wait(interval)


def write_memory():
"""Performs write memory for all thunders"""
if CONF.a10_house_keeping.use_periodic_write_memory == 'enable':
interval = CONF.a10_house_keeping.write_mem_interval
write_memory_perform = house_keeping.WriteMemory()
LOG.info("Write Memory interval set to %s seconds", interval)
while not write_memory_thread_event.is_set():
LOG.info("Initiating the write memory operation for all thunders...")
timestamp = datetime.utcnow()
LOG.debug("Starting write memory thread ar %s", str(timestamp))
try:
write_memory_perform.perform_memory_writes()
except Exception as e:
LOG.exception('write memory caught the following exception and '
' is restarting: {}'.format(e))
write_memory_thread_event.wait(interval)
else:
LOG.warning("Write memory flag is disabled...")


def _mutate_config(*args, **kwargs):
LOG.info("Housekeeping recieved HUP signal, mutating config.")
CONF.mutate_config_files()
Expand All @@ -83,8 +104,8 @@ def main():

gmr.TextGuruMeditation.setup_autorun(version)

timestamp = str(datetime.datetime.utcnow())
LOG.info("Starting house keeping at %s", timestamp)
timestamp = datetime.utcnow()
LOG.info("Starting house keeping at %s", str(timestamp))

# Thread to perform spare amphora check
spare_amp_thread = threading.Thread(target=spare_amphora_check)
Expand All @@ -96,6 +117,11 @@ def main():
db_cleanup_thread.daemon = True
db_cleanup_thread.start()

# Thread to perform write memory
write_memory_thread = threading.Thread(target=write_memory)
write_memory_thread.daemon = True
write_memory_thread.start()

signal.signal(signal.SIGHUP, _mutate_config)

# Try-Exception block should be at the end to gracefully exit threads
Expand All @@ -106,6 +132,8 @@ def main():
LOG.info("Attempting to gracefully terminate House-Keeping")
spare_amp_thread_event.set()
db_cleanup_thread_event.set()
write_memory_thread_event.set()
spare_amp_thread.join()
db_cleanup_thread.join()
write_memory_thread.join()
LOG.info("House-Keeping process terminated")
Loading

0 comments on commit 563b092

Please sign in to comment.