
Note that weight can be specified using the node-modify only but will be displayed in load balancer status, node-list, node-status, node-add, and node-modify. To support the setting of weight with load balancer create or node-add, it would require a bigger change because with those, multiple nodes can be specified with --node. Perhaps a syntax of ip:port:weight would make sense if that was supported. Also note that the API interface only returns weight when the value is not the default (1). Therefore, the weight column also only displays a weight value when it is not 1. Change-Id: I34ea3e4a144aeb47af661c161f5f3ee4946cbe7e
297 lines
11 KiB
Python
297 lines
11 KiB
Python
# Copyright 2012 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.
|
|
|
|
import prettytable
|
|
import novaclient
|
|
import socket
|
|
import logging
|
|
|
|
from novaclient import client
|
|
|
|
|
|
# NOTE(LinuxJedi): Override novaclient's error handler as we send messages in
|
|
# a slightly different format which causes novaclient's to throw an exception
|
|
|
|
def from_response(response, body, url, method=None):
|
|
"""
|
|
Return an instance of an ClientException or subclass
|
|
based on an httplib2 response.
|
|
|
|
Usage::
|
|
|
|
resp, body = http.request(...)
|
|
if resp.status != 200:
|
|
raise exception_from_response(resp, body)
|
|
"""
|
|
cls = novaclient.exceptions._code_map.get(
|
|
response.status_code, novaclient.exceptions.ClientException
|
|
)
|
|
if response.headers:
|
|
request_id = response.headers.get('x-compute-request-id')
|
|
else:
|
|
request_id = None
|
|
if body:
|
|
message = "n/a"
|
|
details = "n/a"
|
|
if hasattr(body, 'keys'):
|
|
message = body.get('faultstring', None)
|
|
if not message:
|
|
message = body.get('message', None)
|
|
details = body.get('details', None)
|
|
return cls(code=response.status_code, message=message, details=details,
|
|
request_id=request_id, url=url, method=method)
|
|
else:
|
|
return cls(code=response.status_code, request_id=request_id, url=url,
|
|
method=method)
|
|
|
|
novaclient.exceptions.from_response = from_response
|
|
|
|
|
|
class LibraAPI(object):
|
|
def __init__(self, args):
|
|
if args.debug:
|
|
logger = logging.getLogger()
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
self.nova = client.HTTPClient(
|
|
args.os_username,
|
|
args.os_password,
|
|
args.os_tenant_name,
|
|
args.os_auth_url,
|
|
region_name=args.os_region_name,
|
|
service_type=args.service_type,
|
|
http_log_debug=args.debug,
|
|
insecure=args.insecure,
|
|
bypass_url=args.bypass_url
|
|
)
|
|
|
|
def limits_lb(self, args):
|
|
resp, body = self._get('/limits')
|
|
# Work around the fact that limits is missing from HP's API server
|
|
if 'rate' in body['limits']:
|
|
column_names = ['Verb', 'Value', 'Remaining', 'Unit',
|
|
'Next Available']
|
|
columns = ['verb', 'value', 'remaining', 'unit', 'next-available']
|
|
self._render_list(column_names, columns,
|
|
body['limits']['rate']['values']['limit'])
|
|
column_names = ['Values']
|
|
columns = ['values']
|
|
self._render_dict(column_names, columns, body['limits']['absolute'])
|
|
|
|
def protocols_lb(self, args):
|
|
resp, body = self._get('/protocols')
|
|
column_names = ['Name', 'Port']
|
|
columns = ['name', 'port']
|
|
self._render_list(column_names, columns, body['protocols'])
|
|
|
|
def algorithms_lb(self, args):
|
|
resp, body = self._get('/algorithms')
|
|
column_names = ['Name']
|
|
columns = ['name']
|
|
self._render_list(column_names, columns, body['algorithms'])
|
|
|
|
def list_lb(self, args):
|
|
if args.deleted:
|
|
resp, body = self._get('/loadbalancers?status=DELETED')
|
|
else:
|
|
resp, body = self._get('/loadbalancers')
|
|
column_names = ['Name', 'ID', 'Protocol', 'Port', 'Algorithm',
|
|
'Status', 'Created', 'Updated', 'Node Count']
|
|
columns = ['name', 'id', 'protocol', 'port', 'algorithm', 'status',
|
|
'created', 'updated', 'nodeCount']
|
|
self._render_list(column_names, columns, body['loadBalancers'])
|
|
|
|
def status_lb(self, args):
|
|
resp, body = self._get('/loadbalancers/{0}'.format(args.id))
|
|
column_names = ['ID', 'Name', 'Protocol', 'Port', 'Algorithm',
|
|
'Status', 'Status Description', 'Created', 'Updated',
|
|
'IPs', 'Nodes', 'Persistence Type',
|
|
'Connection Throttle', 'Node Count']
|
|
columns = ['id', 'name', 'protocol', 'port', 'algorithm', 'status',
|
|
'statusDescription', 'created', 'updated', 'virtualIps',
|
|
'nodes', 'sessionPersistence', 'connectionThrottle',
|
|
'nodeCount']
|
|
if 'sessionPersistence' not in body:
|
|
body['sessionPersistence'] = 'None'
|
|
if 'connectionThrottle' not in body:
|
|
body['connectionThrottle'] = 'None'
|
|
if 'statusDescription' in body:
|
|
body['statusDescription'] = body['statusDescription'].rstrip()
|
|
else:
|
|
body['statusDescription'] = 'None'
|
|
self._render_dict(column_names, columns, body)
|
|
|
|
def virtualips_lb(self, args):
|
|
resp, body = self._get('/loadbalancers/{0}/virtualips'.format(args.id))
|
|
column_names = ['ID', 'Address', 'Type', 'IP Version']
|
|
columns = ['id', 'address', 'type', 'ipVersion']
|
|
self._render_list(column_names, columns, body['virtualIps'])
|
|
|
|
def delete_lb(self, args):
|
|
self._delete('/loadbalancers/{0}'.format(args.id))
|
|
|
|
def create_lb(self, args):
|
|
data = {}
|
|
data['name'] = args.name
|
|
if args.port is not None:
|
|
data['port'] = args.port
|
|
if args.protocol is not None:
|
|
data['protocol'] = args.protocol
|
|
if args.algorithm is not None:
|
|
data['algorithm'] = args.algorithm
|
|
data['nodes'] = self._parse_nodes(args.node)
|
|
if args.vip is not None:
|
|
data['virtualIps'] = [{'id': args.vip}]
|
|
|
|
resp, body = self._post('/loadbalancers', body=data)
|
|
column_names = ['ID', 'Name', 'Protocol', 'Port', 'Algorithm',
|
|
'Status', 'Created', 'Updated', 'IPs', 'Nodes']
|
|
columns = ['id', 'name', 'protocol', 'port', 'algorithm', 'status',
|
|
'created', 'updated', 'virtualIps', 'nodes']
|
|
self._render_dict(column_names, columns, body)
|
|
|
|
def modify_lb(self, args):
|
|
data = {}
|
|
if args.name is not None:
|
|
data['name'] = args.name
|
|
if args.algorithm is not None:
|
|
data['algorithm'] = args.algorithm
|
|
self._put('/loadbalancers/{0}'.format(args.id), body=data)
|
|
|
|
def node_list_lb(self, args):
|
|
resp, body = self._get('/loadbalancers/{0}/nodes'.format(args.id))
|
|
column_names = ['ID', 'Address', 'Port', 'Condition', 'Weight',
|
|
'Status']
|
|
columns = ['id', 'address', 'port', 'condition', 'weight', 'status']
|
|
self._render_list(column_names, columns, body['nodes'])
|
|
|
|
def node_delete_lb(self, args):
|
|
self._delete('/loadbalancers/{0}/nodes/{1}'
|
|
.format(args.id, args.nodeid))
|
|
|
|
def node_add_lb(self, args):
|
|
data = {}
|
|
|
|
data['nodes'] = self._parse_nodes(args.node)
|
|
resp, body = self._post('/loadbalancers/{0}/nodes'
|
|
.format(args.id), body=data)
|
|
column_names = ['ID', 'Address', 'Port', 'Condition', 'Weight',
|
|
'Status']
|
|
columns = ['id', 'address', 'port', 'condition', 'weight', 'status']
|
|
self._render_list(column_names, columns, body['nodes'])
|
|
|
|
def node_modify_lb(self, args):
|
|
data = {'condition': args.condition, 'weight': args.weight}
|
|
self._put('/loadbalancers/{0}/nodes/{1}'
|
|
.format(args.id, args.nodeid), body=data)
|
|
|
|
def node_status_lb(self, args):
|
|
resp, body = self._get('/loadbalancers/{0}/nodes/{1}'
|
|
.format(args.id, args.nodeid))
|
|
column_names = ['ID', 'Address', 'Port', 'Condition',
|
|
'Weight', 'Status']
|
|
columns = ['id', 'address', 'port', 'condition', 'weight', 'status']
|
|
self._render_dict(column_names, columns, body)
|
|
|
|
def logs_lb(self, args):
|
|
data = {}
|
|
|
|
if args.storage:
|
|
data['objectStoreType'] = args.storage
|
|
if args.endpoint:
|
|
data['objectStoreEndpoint'] = args.endpoint
|
|
if args.basepath:
|
|
data['objectStoreBasePath'] = args.basepath
|
|
if args.token:
|
|
data['authToken'] = args.token
|
|
|
|
resp, body = self._post('/loadbalancers/{0}/logs'.format(args.id),
|
|
body=data)
|
|
|
|
def monitor_delete_lb(self, args):
|
|
resp, body = self._delete('/loadbalancers/{0}/healthmonitor'
|
|
.format(args.id))
|
|
|
|
def monitor_list_lb(self, args):
|
|
resp, body = self._get('/loadbalancers/{0}/healthmonitor'
|
|
.format(args.id))
|
|
column_names = ['Type', 'Delay', 'Timeout', 'Attempts', 'Path']
|
|
columns = ['type', 'delay', 'timeout', 'attemptsBeforeDeactivation',
|
|
'path']
|
|
self._render_dict(column_names, columns, body or {})
|
|
|
|
def monitor_modify_lb(self, args):
|
|
data = {}
|
|
data['type'] = args.type
|
|
data['delay'] = args.delay
|
|
data['timeout'] = args.timeout
|
|
data['attemptsBeforeDeactivation'] = args.attempts
|
|
if args.type.upper() != "CONNECT":
|
|
data['path'] = args.path
|
|
resp, body = self._put('/loadbalancers/{0}/healthmonitor'
|
|
.format(args.id), body=data)
|
|
|
|
def _render_list(self, column_names, columns, data):
|
|
table = prettytable.PrettyTable(column_names)
|
|
for item in data:
|
|
row = []
|
|
for column in columns:
|
|
if column in item:
|
|
rdata = item[column]
|
|
else:
|
|
rdata = ''
|
|
row.append(rdata)
|
|
table.add_row(row)
|
|
print table
|
|
|
|
def _render_dict(self, column_names, columns, data):
|
|
table = prettytable.PrettyTable(column_names)
|
|
row = []
|
|
for column in columns:
|
|
if column in data:
|
|
rdata = data[column]
|
|
else:
|
|
rdata = ''
|
|
row.append(rdata)
|
|
table.add_row(row)
|
|
print table
|
|
|
|
def _get(self, url, **kwargs):
|
|
return self.nova.get(url, **kwargs)
|
|
|
|
def _post(self, url, **kwargs):
|
|
return self.nova.post(url, **kwargs)
|
|
|
|
def _put(self, url, **kwargs):
|
|
return self.nova.put(url, **kwargs)
|
|
|
|
def _delete(self, url, **kwargs):
|
|
return self.nova.delete(url, **kwargs)
|
|
|
|
def _parse_nodes(self, nodes):
|
|
out_nodes = []
|
|
try:
|
|
for node in nodes:
|
|
addr = node.split(':')
|
|
# Test IP valid
|
|
# TODO: change to pton when we want to support IPv6
|
|
socket.inet_aton(addr[0])
|
|
# Test port valid
|
|
if int(addr[1]) < 0 or int(addr[1]) > 65535:
|
|
raise
|
|
out_nodes.append({'address': addr[0], 'port': addr[1]})
|
|
except:
|
|
raise Exception("Invalid IP:port specified for --node")
|
|
return out_nodes
|