404 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			404 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/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.
 | 
						|
 | 
						|
# Interactive shell based on Django:
 | 
						|
#
 | 
						|
# Copyright (c) 2005, the Lawrence Journal-World
 | 
						|
# All rights reserved.
 | 
						|
#
 | 
						|
# Redistribution and use in source and binary forms, with or without modification,
 | 
						|
# are permitted provided that the following conditions are met:
 | 
						|
#
 | 
						|
#     1. Redistributions of source code must retain the above copyright notice,
 | 
						|
#        this list of conditions and the following disclaimer.
 | 
						|
#
 | 
						|
#     2. Redistributions in binary form must reproduce the above copyright
 | 
						|
#        notice, this list of conditions and the following disclaimer in the
 | 
						|
#        documentation and/or other materials provided with the distribution.
 | 
						|
#
 | 
						|
#     3. Neither the name of Django nor the names of its contributors may be used
 | 
						|
#        to endorse or promote products derived from this software without
 | 
						|
#        specific prior written permission.
 | 
						|
#
 | 
						|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 | 
						|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
						|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
						|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 | 
						|
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
						|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
						|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 | 
						|
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
						|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
						|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
 | 
						|
 | 
						|
"""
 | 
						|
  CLI interface for nova management.
 | 
						|
"""
 | 
						|
 | 
						|
import os
 | 
						|
import sys
 | 
						|
import time
 | 
						|
 | 
						|
import IPy
 | 
						|
 | 
						|
# 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 exception
 | 
						|
from nova import flags
 | 
						|
from nova import quota
 | 
						|
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 ShellCommands(object):
 | 
						|
    def run(self):
 | 
						|
        "Runs a Python interactive interpreter. Tries to use IPython, if it's available."
 | 
						|
        try:
 | 
						|
            import IPython
 | 
						|
            # Explicitly pass an empty list as arguments, because otherwise IPython
 | 
						|
            # would use sys.argv from this script.
 | 
						|
            shell = IPython.Shell.IPShell(argv=[])
 | 
						|
            shell.mainloop()
 | 
						|
        except ImportError:
 | 
						|
            import code
 | 
						|
            try: # Try activating rlcompleter, because it's handy.
 | 
						|
                import readline
 | 
						|
            except ImportError:
 | 
						|
                pass
 | 
						|
            else:
 | 
						|
                # We don't have to wrap the following import in a 'try', because
 | 
						|
                # we already know 'readline' was imported successfully.
 | 
						|
                import rlcompleter
 | 
						|
                readline.parse_and_bind("tab:complete")
 | 
						|
            code.interact()
 | 
						|
 | 
						|
 | 
						|
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."""
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    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
 | 
						|
 | 
						|
 | 
						|
    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)
 | 
						|
        self._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)
 | 
						|
        self._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:
 | 
						|
            self._print_export(user)
 | 
						|
        else:
 | 
						|
            print "User %s doesn't exist" % name
 | 
						|
 | 
						|
    def list(self):
 | 
						|
        """lists all users
 | 
						|
        arguments: <none>"""
 | 
						|
        for user in self.manager.get_users():
 | 
						|
            print user.name
 | 
						|
 | 
						|
 | 
						|
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: <none>"""
 | 
						|
        for project in self.manager.get_projects():
 | 
						|
            print project.name
 | 
						|
 | 
						|
    def quota(self, project_id, key=None, value=None):
 | 
						|
        """Set or display quotas for project
 | 
						|
        arguments: project_id [key] [value]"""
 | 
						|
        if key:
 | 
						|
            quo = {'project_id': project_id, key: value}
 | 
						|
            try:
 | 
						|
                db.quota_update(None, project_id, quo)
 | 
						|
            except exception.NotFound:
 | 
						|
                db.quota_create(None, quo)
 | 
						|
        project_quota = quota.get_quota(None, project_id)
 | 
						|
        for key, value in project_quota.iteritems():
 | 
						|
            print '%s: %s' % (key, value)
 | 
						|
 | 
						|
    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)
 | 
						|
 | 
						|
 | 
						|
class FloatingIpCommands(object):
 | 
						|
    """Class for managing floating ip."""
 | 
						|
 | 
						|
    def create(self, host, range):
 | 
						|
        """Creates floating ips for host by range
 | 
						|
        arguments: host ip_range"""
 | 
						|
        for address in IPy.IP(range):
 | 
						|
            db.floating_ip_create(None, {'address': str(address),
 | 
						|
                                         'host': host})
 | 
						|
 | 
						|
    def delete(self, ip_range):
 | 
						|
        """Deletes floating ips by range
 | 
						|
        arguments: range"""
 | 
						|
        for address in IPy.IP(ip_range):
 | 
						|
            db.floating_ip_destroy(None, str(address))
 | 
						|
 | 
						|
 | 
						|
    def list(self, host=None):
 | 
						|
        """Lists all floating ips (optionally by host)
 | 
						|
        arguments: [host]"""
 | 
						|
        if host == None:
 | 
						|
            floating_ips = db.floating_ip_get_all(None)
 | 
						|
        else:
 | 
						|
            floating_ips = db.floating_ip_get_all_by_host(None, host)
 | 
						|
        for floating_ip in floating_ips:
 | 
						|
            instance = None
 | 
						|
            if floating_ip['fixed_ip']:
 | 
						|
                instance = floating_ip['fixed_ip']['instance']['str_id']
 | 
						|
            print "%s\t%s\t%s" % (floating_ip['host'],
 | 
						|
                                  floating_ip['address'],
 | 
						|
                                  instance)
 | 
						|
 | 
						|
 | 
						|
CATEGORIES = [
 | 
						|
    ('user', UserCommands),
 | 
						|
    ('project', ProjectCommands),
 | 
						|
    ('role', RoleCommands),
 | 
						|
    ('shell', ShellCommands),
 | 
						|
    ('vpn', VpnCommands),
 | 
						|
    ('floating', FloatingIpCommands)
 | 
						|
]
 | 
						|
 | 
						|
 | 
						|
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 [<args>]"
 | 
						|
        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 [<args>]"
 | 
						|
        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()
 |