# 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 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): 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', 'Status'] columns = ['id', 'address', 'port', 'condition', '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', 'Status'] columns = ['id', 'address', 'port', 'condition', 'status'] self._render_list(column_names, columns, body['nodes']) def node_modify_lb(self, args): data = {'condition': args.condition} 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', 'Status'] columns = ['id', 'address', 'port', 'condition', '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 _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