#!/usr/bin/env python # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # 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. """ CLI interface for nova management. Connects to the running ADMIN api in the api daemon. """ import os import sys import time # If ../nova/__init__.py exists, add ../ to Python search path, so that # it will override what happens to be installed in /usr/(local/)lib/python... possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), os.pardir, os.pardir)) if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): sys.path.insert(0, possible_topdir) from nova import db from nova import flags from nova import utils from nova.auth import manager from nova.cloudpipe import pipelib from nova.endpoint import cloud FLAGS = flags.FLAGS class VpnCommands(object): """Class for managing VPNs.""" def __init__(self): self.manager = manager.AuthManager() self.pipe = pipelib.CloudPipe(cloud.CloudController()) def list(self): """Print a listing of the VPNs for all projects.""" print "%-12s\t" % 'project', print "%-12s\t" % 'ip:port', print "%s" % 'state' for project in self.manager.get_projects(): print "%-12s\t" % project.name, print "%s:%s\t" % (project.vpn_ip, project.vpn_port), vpn = self._vpn_for(project.id) if vpn: command = "ping -c1 -w1 %s > /dev/null; echo $?" out, _err = utils.execute(command % vpn['private_dns_name'], check_exit_code=False) if out.strip() == '0': net = 'up' else: net = 'down' print vpn['private_dns_name'], print vpn['node_name'], print vpn['instance_id'], print vpn['state_description'], print net else: print None def _vpn_for(self, project_id): """Get the VPN instance for a project ID.""" for instance in db.instance_get_all(): if (instance['image_id'] == FLAGS.vpn_image_id and not instance['state_description'] in ['shutting_down', 'shutdown'] and instance['project_id'] == project_id): return instance def spawn(self): """Run all VPNs.""" for p in reversed(self.manager.get_projects()): if not self._vpn_for(p.id): print 'spawning %s' % p.id self.pipe.launch_vpn_instance(p.id) time.sleep(10) def run(self, project_id): """Start the VPN for a given project.""" self.pipe.launch_vpn_instance(project_id) class RoleCommands(object): """Class for managing roles.""" def __init__(self): self.manager = manager.AuthManager() def add(self, user, role, project=None): """adds role to user if project is specified, adds project specific role arguments: user, role [project]""" self.manager.add_role(user, role, project) def has(self, user, role, project=None): """checks to see if user has role if project is specified, returns True if user has the global role and the project role arguments: user, role [project]""" print self.manager.has_role(user, role, project) def remove(self, user, role, project=None): """removes role from user if project is specified, removes project specific role arguments: user, role [project]""" self.manager.remove_role(user, role, project) class UserCommands(object): """Class for managing users.""" def __init__(self): self.manager = manager.AuthManager() def admin(self, name, access=None, secret=None): """creates a new admin and prints exports arguments: name [access] [secret]""" user = self.manager.create_user(name, access, secret, True) print_export(user) def create(self, name, access=None, secret=None): """creates a new user and prints exports arguments: name [access] [secret]""" user = self.manager.create_user(name, access, secret, False) print_export(user) def delete(self, name): """deletes an existing user arguments: name""" self.manager.delete_user(name) def exports(self, name): """prints access and secrets for user in export format arguments: name""" user = self.manager.get_user(name) if user: print_export(user) else: print "User %s doesn't exist" % name def list(self): """lists all users arguments: """ for user in self.manager.get_users(): print user.name def print_export(user): """Print export variables to use with API.""" print 'export EC2_ACCESS_KEY=%s' % user.access print 'export EC2_SECRET_KEY=%s' % user.secret class ProjectCommands(object): """Class for managing projects.""" def __init__(self): self.manager = manager.AuthManager() def add(self, project, user): """Adds user to project arguments: project user""" self.manager.add_to_project(user, project) def create(self, name, project_manager, description=None): """Creates a new project arguments: name project_manager [description]""" self.manager.create_project(name, project_manager, description) def delete(self, name): """Deletes an existing project arguments: name""" self.manager.delete_project(name) def environment(self, project_id, user_id, filename='novarc'): """Exports environment variables to an sourcable file arguments: project_id user_id [filename='novarc]""" rc = self.manager.get_environment_rc(project_id, user_id) with open(filename, 'w') as f: f.write(rc) def list(self): """Lists all projects arguments: """ for project in self.manager.get_projects(): print project.name def remove(self, project, user): """Removes user from project arguments: project user""" self.manager.remove_from_project(user, project) def zipfile(self, project_id, user_id, filename='nova.zip'): """Exports credentials for project to a zip file arguments: project_id user_id [filename='nova.zip]""" zip_file = self.manager.get_credentials(user_id, project_id) with open(filename, 'w') as f: f.write(zip_file) CATEGORIES = [ ('user', UserCommands), ('project', ProjectCommands), ('role', RoleCommands), ('vpn', VpnCommands), ] def lazy_match(name, key_value_tuples): """Finds all objects that have a key that case insensitively contains [name] key_value_tuples is a list of tuples of the form (key, value) returns a list of tuples of the form (key, value)""" result = [] for (k, v) in key_value_tuples: if k.lower().find(name.lower()) == 0: result.append((k, v)) if len(result) == 0: print "%s does not match any options:" % name for k, _v in key_value_tuples: print "\t%s" % k sys.exit(2) if len(result) > 1: print "%s matched multiple options:" % name for k, _v in result: print "\t%s" % k sys.exit(2) return result def methods_of(obj): """Get all callable methods of an object that don't start with underscore returns a list of tuples of the form (method_name, method)""" result = [] for i in dir(obj): if callable(getattr(obj, i)) and not i.startswith('_'): result.append((i, getattr(obj, i))) return result def main(): """Parse options and call the appropriate class/method.""" utils.default_flagfile('/etc/nova/nova-manage.conf') argv = FLAGS(sys.argv) script_name = argv.pop(0) if len(argv) < 1: print script_name + " category action []" print "Available categories:" for k, _ in CATEGORIES: print "\t%s" % k sys.exit(2) category = argv.pop(0) matches = lazy_match(category, CATEGORIES) # instantiate the command group object category, fn = matches[0] command_object = fn() actions = methods_of(command_object) if len(argv) < 1: print script_name + " category action []" print "Available actions for %s category:" % category for k, _v in actions: print "\t%s" % k sys.exit(2) action = argv.pop(0) matches = lazy_match(action, actions) action, fn = matches[0] # call the action with the remaining arguments try: fn(*argv) sys.exit(0) except TypeError: print "Wrong number of arguments supplied" print "%s %s: %s" % (category, action, fn.__doc__) sys.exit(2) if __name__ == '__main__': main()