Files
python-libraclient/libraclient/libraapi.py
marcrp 536b5234c2 Added node weight support
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
2013-09-29 08:56:02 -04:00

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