Adding F5 based LBaaS murano app

* Adding F5 BIG IP LTM driver source code
 * Adding a script for fast building murano package

Change-Id: Ic6c05378eed1490545fce9bba3ead768003aac50
This commit is contained in:
Nikolay Mahotkin 2016-03-30 15:54:29 +03:00
parent cb3fa8e136
commit 83bdd28931
26 changed files with 767 additions and 0 deletions

1
murano-apps/F5-based-LBaaS/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
F5_based_LBaaS.zip

View File

@ -0,0 +1,36 @@
# Stop the script if an error occurs.
set -e
function cleanup {
cd $SCRIPTPATH
rm -rf tmp
}
# In case if script is running not where it is located.
cd $(dirname $0)
SCRIPTPATH=`pwd`
# Cleanup tmp dir on script exit.
trap 'cleanup' EXIT
mkdir tmp
pushd package
cp -v -r Classes Resources UI manifest.yaml logo.png ../tmp/
popd
archive_name=f5-lbaas-driver.tar.gz
f5_directory_name=f5_lbaas_driver-0.0.1
# Pack python tarball.
pushd tmp/Resources/scripts
tar -czvf $archive_name $f5_directory_name/*
base64 $archive_name > $archive_name.bs64
rm -rf $f5_directory_name
rm -rf $archive_name
popd
# Make murano package.
pushd tmp
zip -r ../F5_based_LBaaS.zip .
popd

View File

@ -0,0 +1,64 @@
# 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.
Namespaces:
=: io.murano.apps.lbaas
std: io.murano
sys: io.murano.system
Name: F5BigIPLTM
Extends: LBaaS
Properties:
host:
Contract: $.string().notNull()
username:
Contract: $.string().notNull()
password:
Contract: $.string().notNull()
Methods:
.init:
Body:
- $.implementation: f5ltm
installLBaaS:
Arguments:
- implementation:
Contract: $.string().notNull()
Body:
- $lbaas: $.super($.installLBaaS($implementation))
- $resources: new(sys:Resources)
- $template: $resources.yaml('DeployF5Driver.template')
- $.environment.reporter.report($this, 'Installing F5 driver for LBaaS...')
- $.instance.agent.call($template, $resources)
- $.environment.reporter.report($this, 'F5 driver is installed.')
- Return: $lbaas
getOptionalConfig:
Body:
- Return:
- section: lbaas_f5
key: host
value: $.host
- section: lbaas_f5
key: username
value: $.username
- section: lbaas_f5
key: password
value: $.password

View File

@ -0,0 +1,18 @@
FormatVersion: 2.0.0
Version: 1.0.0
Name: Deploy F5 Driver
Body: |
return F5DriverDeploy().stdout
Scripts:
F5DriverDeploy:
Type: Application
Version: 1.0.0
EntryPoint: deployF5Driver.sh
Files:
- <f5-lbaas-driver.tar.gz.bs64>
- f5-lbaas-append.conf.sample
Options:
captureStdout: true
captureStderr: true

View File

@ -0,0 +1,13 @@
# Fail script if an error occurs.
set -e
# TODO(nmakhotkin): It should be removed in the future after fixing the bug:
# TODO(nmakhotkin): https://bugs.launchpad.net/murano/+bug/1561522
# TODO(nmakhotkin): Here should be used pure tar.gz archive instead of base64-encoded.
base64 --decode f5-lbaas-driver.tar.gz.bs64 > f5-lbaas-driver.tar.gz
# Installing LBaaS API.
sudo pip install f5-lbaas-driver.tar.gz
# Adding a new config section to main config.
cat f5-lbaas-append.conf.sample >> /etc/lbaas/lbaas.conf

View File

@ -0,0 +1,4 @@
[lbaas_f5]
host = host
username = user
password = password

View File

@ -0,0 +1 @@
Nikolay Mahotkin <nmakhotkin@mirantis.com>

View File

@ -0,0 +1,8 @@
CHANGES
=======
* Fixing issues
* Restrict requirements.txt
* Added AUTHORS and ChangeLog
* Initial version of F5 BIG LTM driver for LBaaS API
* Initial commit

View File

@ -0,0 +1,13 @@
Metadata-Version: 1.0
Name: f5_lbaas_driver
Version: 0.0.1.dev4
Summary: LBaaS Project
Home-page: UNKNOWN
Author: Mirantis Inc.
Author-email: nmakhotkin@mirantis.com
License: Apache License, Version 2.0
Description: # f5-lbaas-driver
F5 BIG IP LTM implementation for lbaas-api
Platform: UNKNOWN

View File

@ -0,0 +1,2 @@
# f5-lbaas-driver
F5 BIG IP LTM implementation for lbaas-api

View File

@ -0,0 +1,13 @@
Metadata-Version: 1.0
Name: f5-lbaas-driver
Version: 0.0.1.dev4
Summary: LBaaS Project
Home-page: UNKNOWN
Author: Mirantis Inc.
Author-email: nmakhotkin@mirantis.com
License: Apache License, Version 2.0
Description: # f5-lbaas-driver
F5 BIG IP LTM implementation for lbaas-api
Platform: UNKNOWN

View File

@ -0,0 +1,16 @@
AUTHORS
ChangeLog
README.md
requirements.txt
setup.cfg
setup.py
f5_lbaas_driver/__init__.py
f5_lbaas_driver/f5ltm.py
f5_lbaas_driver.egg-info/PKG-INFO
f5_lbaas_driver.egg-info/SOURCES.txt
f5_lbaas_driver.egg-info/dependency_links.txt
f5_lbaas_driver.egg-info/entry_points.txt
f5_lbaas_driver.egg-info/not-zip-safe
f5_lbaas_driver.egg-info/pbr.json
f5_lbaas_driver.egg-info/requires.txt
f5_lbaas_driver.egg-info/top_level.txt

View File

@ -0,0 +1,3 @@
[lbaas.drivers]
f5ltm = f5_lbaas_driver.f5ltm:F5Driver

View File

@ -0,0 +1 @@
{"is_release": false, "git_version": "5e3cafd"}

View File

@ -0,0 +1,3 @@
f5-sdk>=0.1
requests!=2.9.0,>=2.8.1
oslo.config>=3.7.0

View File

@ -0,0 +1,376 @@
# Copyright 2016 - Mirantis, 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 json
from f5 import bigip
from oslo_config import cfg
from oslo_log import log as logging
import requests
from lbaas import config
from lbaas.drivers import base
bigip_opts = [
cfg.StrOpt('host', help='BIG IP host.'),
cfg.StrOpt('username', help='BIG IP username'),
cfg.StrOpt('password', help='BIG IP password')
]
CONF = cfg.CONF
CONF.register_opts(bigip_opts, group='lbaas_f5')
LOG = logging.getLogger(__name__)
config.read_config()
class F5Driver(base.LoadBalancerDriver):
def __init__(self):
self.bigip = bigip.BigIP(
CONF.lbaas_f5.host,
CONF.lbaas_f5.username,
CONF.lbaas_f5.password
).ltm
@staticmethod
def _install_cert(path, name):
requests.post(
url='https://%s/mgmt/tm/sys/crypto/cert' % CONF.lbaas_f5.host,
data=json.dumps(
{
"fromLocalFile": path,
"name": name,
"command": "install"
}
),
verify=False,
auth=(CONF.lbaas_f5.username, CONF.lbaas_f5.password),
headers={
'Accept': 'application/json',
'Content-Type': 'application/json'
}
)
@staticmethod
def _install_key(path, name):
requests.post(
url='https://%s/mgmt/tm/sys/crypto/key'
% CONF.lbaas_f5.host,
data=json.dumps(
{
"fromLocalFile": path,
"name": name,
"command": "install"
}
),
verify=False,
auth=(CONF.lbaas_f5.username, CONF.lbaas_f5.password),
headers={
'Accept': 'application/json',
'Content-Type': 'application/json'
}
)
def _create_or_update_ssl_profile(self, name, key_name, cert_name,
options, ciphers):
profile_resp = requests.get(
url='https://%s/mgmt/tm/ltm/profile/client-ssl/%s'
% (CONF.lbaas_f5.host, name),
auth=(CONF.lbaas_f5.username, CONF.lbaas_f5.password),
verify=False
)
if profile_resp.status_code == 404:
return self._create_ssl_profile(
name,
key_name,
cert_name,
options,
ciphers
)
else:
return self._update_ssl_profile(
name,
key_name,
cert_name,
options,
ciphers
)
@staticmethod
def _create_ssl_profile(name, key_name, cert_name,
options=None, ciphers=None):
url = (
'https://%s/mgmt/tm/ltm/profile/client-ssl' % CONF.lbaas_f5.host
)
data = {
"name": name,
"cert": cert_name,
"key": key_name
}
if options:
data['options'] = options
if ciphers:
data['ciphers'] = ciphers
resp = requests.post(
url=url,
data=json.dumps(data),
verify=False,
auth=(CONF.lbaas_f5.username, CONF.lbaas_f5.password),
headers={
'Accept': 'application/json',
'Content-Type': 'application/json'
}
)
return resp.json()
@staticmethod
def _update_ssl_profile(name, key_name, cert_name,
options=None, ciphers=None):
url = (
'https://%s/mgmt/tm/ltm/profile/client-ssl/%s'
% (CONF.lbaas_f5.host, name)
)
data = {
"cert": cert_name,
"key": key_name
}
if options:
data['options'] = options
if ciphers:
data['ciphers'] = ciphers
resp = requests.put(
url=url,
data=json.dumps(data),
verify=False,
auth=(CONF.lbaas_f5.username, CONF.lbaas_f5.password),
headers={
'Accept': 'application/json',
'Content-Type': 'application/json'
}
)
return resp.json()
def _assign_ssl_profile(self, ssl_info):
if not ssl_info:
return
# In this case, need to install the cert,
# the key and the new SSL profile.
path = ssl_info['path']
options = ssl_info.get('options', '')
ciphers = ssl_info.get('ciphers', '')
name = path.split('/')[-1].split('.')[0]
# Install the cert and the key.
self._install_cert(path, name)
self._install_key(path, name)
# Create a client SSL profile.
return self._create_or_update_ssl_profile(
name,
"%s.key" % name,
"%s.crt" % name,
options,
ciphers
)
@staticmethod
def _find_member_by_description(pool, mem_description):
all_members = pool.members_s.get_collection()
for mem in all_members:
# Search by name.
if mem.description == mem_description:
return mem
def create_listener(self, listener):
ssl_profile = self._assign_ssl_profile(listener.ssl_info)
if not listener.algorithm:
listener.algorithm = 'round-robin'
if not listener.address:
listener.address = '0.0.0.0'
# Create a new pool associated with current virtual server.
# Assign default ICMP monitor.
self.bigip.pools.pool.create(
name='pool-%s' % listener.name,
loadBalancingMode=listener.algorithm,
monitor='gateway_icmp'
)
kwargs = {
'name': 'virtual-%s' % listener.name,
'pool': 'pool-%s' % listener.name,
'destination': '%s:%s' % (
listener.address,
listener.protocol_port
),
'ipProtocol': listener.protocol
}
if ssl_profile:
kwargs['profiles'] = [{
'context': 'clientside',
'name': ssl_profile['name']
}]
# Create a new virtual server.
self.bigip.virtuals.virtual.create(**kwargs)
return listener
def delete_listener(self, listener):
pool = self.bigip.pools.pool.load(name='pool-%s' % listener.name)
virtual = self.bigip.virtuals.virtual.load(
name='virtual-%s' % listener.name
)
virtual.delete()
pool.delete()
def apply_changes(self):
pass
def delete_member(self, member):
# Delete pool member.
pool = self.bigip.pools.pool.load(
name='pool-%s' % member.listener.name,
)
# Get parent node.
node = self._get_node_by_address(member.address)
# Find given member in current pool.
mem = self._find_member_by_description(
pool,
'%s' % member.name
)
mem.delete()
# Try to delete parent node.
try:
node.delete()
except Exception as e:
# In case if parent node is still used by any other pool member.
LOG.warning(e)
def _get_node_by_address(self, address):
nodes = self.bigip.nodes.get_collection()
filtered = list(filter(lambda x: x.address == address, nodes))
return None if not filtered else filtered[0]
def create_member(self, member):
# Try to get parent node if it already exists.
node = self._get_node_by_address(member.address)
if not node:
# Create a new node.
node = self.bigip.nodes.node.create(
# params
name='%s' % member.address,
address=member.address,
monitor="default",
partition='Common'
)
pool = self.bigip.pools.pool.load(
name='pool-%s' % member.listener.name,
)
# Create a new member in current pool.
pool.members_s.members.create(
name='%s:%s' % (node.name, member.protocol_port),
partition='Common',
description=member.name
)
return member
def update_listener(self, listener):
ssl_profile = self._assign_ssl_profile(listener.ssl_info)
if not listener.algorithm:
listener.algorithm = 'round-robin'
# Get the pool associated with current virtual server.
pool = self.bigip.pools.pool.load(
name='pool-%s' % listener.name,
)
virtual = self.bigip.virtuals.virtual.load(
name='virtual-%s' % listener.name
)
# potential values that could be changed:
# pool: algorithm,
# virtual: address, protocol_port, protocol,
# address, protocol, protocol_port, algorithm, options, ssl_info.
destination = '%s:%s' % (listener.address, listener.protocol_port)
update_virtual = (
destination not in virtual.destination or
virtual.ipProtocol != listener.protocol or
ssl_profile
)
update_pool = pool.loadBalancingMode != listener.algorithm
if update_virtual:
# Update the virtual server.
kwargs = {
'destination': '%s:%s' % (
listener.address,
listener.protocol_port
),
'ipProtocol': listener.protocol
}
if ssl_profile:
kwargs['profiles'] = [{
'context': 'clientside',
'name': ssl_profile['name']
}]
virtual.update(**kwargs)
if update_pool:
# Update the pool.
pool.update(
loadBalancingMode=listener.algorithm
)
return listener
def update_member(self, member):
# Replace member on another one.
self.delete_member(member)
self.create_member(member)
return member

View File

@ -0,0 +1,3 @@
f5-sdk>=0.1
requests!=2.9.0,>=2.8.1 # Apache-2.0
oslo.config>=3.7.0 # Apache-2.0

View File

@ -0,0 +1,31 @@
[metadata]
name = f5_lbaas_driver
summary = LBaaS Project
description-file =
README.md
license = Apache License, Version 2.0
classifiers =
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
#License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
author = Mirantis Inc.
author-email = nmakhotkin@mirantis.com
[files]
packages =
f5_lbaas_driver
[entry_points]
lbaas.drivers =
f5ltm = f5_lbaas_driver.f5ltm:F5Driver
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0

View File

@ -0,0 +1,30 @@
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
# In python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.python.org/issue15881#msg170215
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr>=1.8'],
pbr=True
)

View File

@ -0,0 +1,104 @@
Version: 2
Application:
?:
type: io.murano.apps.lbaas.F5BigIPLTM
name: $.appConfiguration.name
host: $.bigIPconfiguration.host
username: $.bigIPconfiguration.username
password: $.bigIPconfiguration.password
instance:
?:
type: io.murano.resources.LinuxMuranoInstance
name: generateHostname($.instanceConfiguration.unitNamingPattern, 1)
flavor: $.instanceConfiguration.flavor
image: $.instanceConfiguration.osImage
keyname: $.instanceConfiguration.keyPair
availabilityZone: $.instanceConfiguration.availabilityZone
assignFloatingIp: $.appConfiguration.assignFloatingIP
Forms:
- bigIPconfiguration:
fields:
- name: username
type: string
label: BIG IP username
initial: 'admin'
- name: password
type: string
label: BIG IP password
initial: 'admin'
- name: host
type: string
label: BIG IP host
- appConfiguration:
fields:
- name: name
type: string
label: Application Name
initial: 'F5 BIG IP based LBaaS'
description: >-
Enter a desired name for the application. Just A-Z, a-z, 0-9, dash and
underline are allowed
- name: assignFloatingIP
type: boolean
label: Assign Floating IP
description: >-
Select to true to assign floating IP automatically
initial: true
required: false
widgetMedia:
css: {all: ['muranodashboard/css/checkbox.css']}
- instanceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
description: Specify some instance parameters on which the application would be created
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that application performance
depends on this parameter.
required: false
- name: osImage
type: image
imageType: linux
label: Instance image
description: >-
Select valid image for the application. Image should already be prepared and
registered in glance.
- name: keyPair
type: keypair
label: Key Pair
description: >-
Select the Key Pair to control access to instances. You can login to
instances using this KeyPair after the deployment of application.
required: false
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where application would be installed.
required: false
- name: network
type: network
label: Network
description: Select a network to join. 'Auto' corresponds to a default environment's network.
required: false
murano_networks: translate
- name: unitNamingPattern
type: string
label: Instance Naming Pattern
required: false
maxLength: 64
regexpValidator: '^[a-zA-z][-_\w]*$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
description: >-
Specify a string, that will be used in instance hostname.
Just A-Z, a-z, 0-9, dash and underline are allowed.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,24 @@
# 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.
Format: 1.0
Type: Application
FullName: io.murano.apps.lbaas.F5BigIPLTM
Name: F5 BIG IP LTM based LBaaS
Description: |
F5 BIG IP Local Traffic manager.
Author: 'Mirantis, Inc'
Tags: [HTTP, TCP, Load Balancing as a Service, F5, BIG IP, Local traffic]
Classes:
io.murano.apps.lbaas.F5BigIPLTM: F5BigIPLTM.yaml
Require:
io.murano.apps.lbaas.LBaaS: