Esempio n. 1
0
    def add_node_configuration(self, node_name, ip_address, connection_user,
                               connection_password, ca_key):
        """Add MCVirt node to configuration, generates a cluster user on the remote node
        and stores credentials against node in the MCVirt configuration.
        """
        self._get_registered_object('auth').assert_permission(
            PERMISSIONS.MANAGE_CLUSTER)

        # Create CA file
        ssl_object = self._get_registered_object(
            'certificate_generator_factory').get_cert_generator(node_name)
        ssl_object.ca_pub_file = ca_key

        # Connect to node and obtain cluster user
        remote = Connection(username=connection_user,
                            password=connection_password,
                            host=node_name)
        remote_user_factory = remote.get_connection('user_factory')
        connection_user = remote_user_factory.get_user_by_username(
            connection_user)
        remote.annotate_object(connection_user)
        username, password = connection_user.create_cluster_user(
            host=get_hostname())

        # Add node to configuration file
        def add_node_config(mcvirt_config):
            mcvirt_config['cluster']['nodes'][node_name] = {
                'ip_address': ip_address,
                'username': username,
                'password': password
            }

        MCVirtConfig().update_config(add_node_config)
Esempio n. 2
0
    def add_node_configuration(self, node_name, ip_address,
                               connection_user, connection_password,
                               ca_key):
        """Add MCVirt node to configuration, generates a cluster user on the remote node
        and stores credentials against node in the MCVirt configuration.
        """
        self._get_registered_object('auth').assert_permission(PERMISSIONS.MANAGE_CLUSTER)

        # Create CA file
        ssl_object = self._get_registered_object(
            'certificate_generator_factory').get_cert_generator(node_name)
        ssl_object.ca_pub_file = ca_key

        # Connect to node and obtain cluster user
        remote = Connection(username=connection_user, password=connection_password,
                            host=node_name)
        remote_user_factory = remote.get_connection('user_factory')
        connection_user = remote_user_factory.get_user_by_username(connection_user)
        remote.annotate_object(connection_user)
        username, password = connection_user.create_cluster_user(host=get_hostname())

        # Add node to configuration file
        def add_node_config(mcvirt_config):
            mcvirt_config['cluster']['nodes'][node_name] = {
                'ip_address': ip_address,
                'username': username,
                'password': password
            }
        MCVirtConfig().update_config(add_node_config)
Esempio n. 3
0
    def setUp(self):
        """Obtain connections to the daemon and create various
        member variables.
        """
        # Create and store RPC connection to daemon.
        self.rpc = Connection(self.RPC_USERNAME, self.RPC_PASSWORD)

        # Create and store parser instance
        self.parser = Parser(verbose=False)

        # Obtain the session ID from the RPC connection and re-use this,
        # so that the parser does not need to authenticate with a password
        # self.parser.parse_arguments('list --username %s --password %s' % (self.RPC_USERNAME,
        #                                                                   self.RPC_PASSWORD))

        self.parser.USERNAME = self.RPC_USERNAME
        self.parser.SESSION_ID = self.rpc.session_id

        # Setup variable for test VM
        self.test_vms = \
            {
                'TEST_VM_1':
                {
                    'name': 'mcvirt-unittest-vm',
                    'cpu_count': 1,
                    'memory_allocation': 100,
                    'disk_size': [100],
                    'networks': ['Production']
                },
                'TEST_VM_2':
                {
                    'name': 'mcvirt-unittest-vm2',
                    'cpu_count': 2,
                    'memory_allocation': 120,
                    'disk_size': [100],
                    'networks': ['Production']
                }
            }

        # Ensure any test VM is stopped and removed from the machine
        self.stop_and_delete(self.test_vms['TEST_VM_2']['name'])
        self.stop_and_delete(self.test_vms['TEST_VM_1']['name'])

        self.vm_factory = self.rpc.get_connection('virtual_machine_factory')

        self.test_network_name = 'testnetwork'
        self.test_physical_interface = 'vmbr0'
        self.network_factory = self.rpc.get_connection('network_factory')

        # Determine if the test network exists. If so, delete it
        if self.network_factory.check_exists(self.test_network_name):
            network = self.network_factory.get_network_by_name(
                self.test_network_name)
            self.rpc.annotate_object(network)
            network.delete()
Esempio n. 4
0
    def start(self):
        """Start the daemon, run the unit tests and tear down"""
        try:
            # Attempt to start daemon
            self.daemon_thread.start()

            # Attempt to run tests
            success = self.runner.run(self.all_tests).wasSuccessful()
        finally:
            # Set the run condition flag for daemon to False in order to
            # stop on next loop
            self.daemon_run = False
            OnlineMigrateTests.RPC_DAEMON = None
            try:
                # Perform final connection to daemon to ensure that it loops
                # to stop.
                Connection(username='******', password='******')
            except:
                pass

            # Wait for daemon to stop
            self.daemon_thread.join()

        # Return success state of tests
        return success
Esempio n. 5
0
    def parse_arguments(self, script_args=None):
        """Parse arguments and performs actions based on the arguments."""
        # If arguments have been specified, split, so that
        # an array is sent to the argument parser
        if script_args is not None:
            script_args = script_args.split()

        args = self.parser.parse_args(script_args)

        ignore_cluster = self.check_ignore_failed(args)

        if self.session_id and self.username:
            self.rpc = Connection(username=self.username, session_id=self.session_id,
                                  ignore_cluster=ignore_cluster)
        else:
            # Obtain connection to Pyro server
            if not (args.password or args.username):
                self.authenticate_saved_session(ignore_cluster)

            if not self.rpc:
                self.authenticate_username_password(args, ignore_cluster)

        self.store_cached_session(args)

        if args.ignore_drbd:
            self.rpc.ignore_drbd()

        # If a custom parser function has been defined, used this and exit
        # instead of running through (old) main parser workflow
        if 'func' in dir(args):
            args.func(args=args, p_=self)
        else:
            raise ArgumentParserException('No handler registered for parser')
Esempio n. 6
0
    def authenticate_saved_session(self, ignore_cluster):
        """Attempt to authenticate using saved session"""
        # Try logging in with saved session
        auth_session = None
        try:
            with open(self.auth_cache_file, 'r') as cache_fh:
                auth_username = cache_fh.readline().strip()
                auth_session = cache_fh.readline().strip()
        except IOError:
            pass

        if auth_session:
            try:
                self.rpc = Connection(username=auth_username, session_id=auth_session,
                                      ignore_cluster=ignore_cluster)
                self.session_id = self.rpc.session_id
                self.username = self.rpc.username
            except AuthenticationError:
                # If authentication fails with cached session,
                # print error, attempt to remove sessionn file and
                # remove rpc connection
                self.print_status('Authentication error occured when using saved session.')
                try:
                    os.remove(self.auth_cache_file)
                except OSError:
                    pass
                self.rpc = None
Esempio n. 7
0
    def test_add_delete_superuser(self):
        """Add/delete a user to/from the superuser role"""
        # Assert that the user is not already a superuser
        self.assertFalse(
            self.TEST_USERNAME in
            self.RPC_DAEMON.DAEMON.registered_factories['auth'].get_superusers()
        )

        # Add the user to the superuser group using the argument parser
        self.parser.parse_arguments('permission --add-superuser %s --global' % self.TEST_USERNAME)

        # Ensure that the auth object asserts that the user is a superuser
        self.assertTrue(
            self.TEST_USERNAME in
            self.RPC_DAEMON.DAEMON.registered_factories['auth'].get_superusers()
        )
        rpc_connection = Connection(username=self.TEST_USERNAME, password=self.TEST_PASSWORD)
        test_auth = rpc_connection.get_connection('auth')
        self.assertTrue(test_auth.is_superuser())

        # Ensure that user can start a test VM and delete it
        test_vm = self.create_vm('TEST_VM_1', 'Local')
        self.parse_command('start %s' % test_vm.get_name(),
                           username=self.TEST_USERNAME,
                           password=self.TEST_PASSWORD)

        self.parse_command('stop %s' % test_vm.get_name(),
                           username=self.TEST_USERNAME,
                           password=self.TEST_PASSWORD)

        self.parse_command('delete --delete-data %s' % test_vm.get_name(),
                           username=self.TEST_USERNAME,
                           password=self.TEST_PASSWORD)

        # Delete the user from the superuser group using the argument parser
        self.parser.parse_arguments('permission --delete-superuser %s --global' %
                                    self.TEST_USERNAME)

        # Assert that the user is no longer considered a superuser
        self.assertFalse(test_auth.is_superuser())

        # Assert that the user no longer has access to the superuser permission
        self.assertFalse(test_auth.is_superuser())
        self.assertFalse(
            self.TEST_USERNAME in
            self.RPC_DAEMON.DAEMON.registered_factories['auth'].get_superusers()
        )
Esempio n. 8
0
    def test_add_delete_superuser(self):
        """Add/delete a user to/from the superuser role"""
        # Assert that the user is not already a superuser
        self.assertFalse(self.TEST_USERNAME in self.RPC_DAEMON.DAEMON.
                         registered_factories['auth'].get_superusers())

        # Add the user to the superuser group using the argument parser
        self.parser.parse_arguments('permission --add-superuser %s --global' %
                                    self.TEST_USERNAME)

        # Ensure that the auth object asserts that the user is a superuser
        self.assertTrue(self.TEST_USERNAME in self.RPC_DAEMON.DAEMON.
                        registered_factories['auth'].get_superusers())
        rpc_connection = Connection(username=self.TEST_USERNAME,
                                    password=self.TEST_PASSWORD)
        test_auth = rpc_connection.get_connection('auth')
        self.assertTrue(test_auth.is_superuser())

        # Ensure that user can start a test VM and delete it
        test_vm = self.create_vm('TEST_VM_1', 'Local')
        self.parse_command('start %s' % test_vm.get_name(),
                           username=self.TEST_USERNAME,
                           password=self.TEST_PASSWORD)

        self.parse_command('stop %s' % test_vm.get_name(),
                           username=self.TEST_USERNAME,
                           password=self.TEST_PASSWORD)

        self.parse_command('delete --delete-data %s' % test_vm.get_name(),
                           username=self.TEST_USERNAME,
                           password=self.TEST_PASSWORD)

        # Delete the user from the superuser group using the argument parser
        self.parser.parse_arguments(
            'permission --delete-superuser %s --global' % self.TEST_USERNAME)

        # Assert that the user is no longer considered a superuser
        self.assertFalse(test_auth.is_superuser())

        # Assert that the user no longer has access to the superuser permission
        self.assertFalse(test_auth.is_superuser())
        self.assertFalse(self.TEST_USERNAME in self.RPC_DAEMON.DAEMON.
                         registered_factories['auth'].get_superusers())
Esempio n. 9
0
 def authenticate_username_password(self, args, ignore_cluster):
     """Authenticate using username and password"""
     # Check if user/password have been passed. Else, ask for them.
     username = args.username if args.username else System.getUserInput(
         'Username: '******'Password: ', password=True
         ).rstrip()
     self.rpc = Connection(username=username, password=password,
                           ignore_cluster=ignore_cluster)
     self.session_id = self.rpc.session_id
     self.username = self.rpc.username
Esempio n. 10
0
import sys
import os
import json

from mcvirt.client.rpc import Connection  # noqa
from mcvirt.constants import DirectoryLocation  # noqa

# Obtain Drbd resource name from argument
drbd_resource = os.environ['DRBD_RESOURCE']

# Determine sync state from arguments
sync_state = bool(int(sys.argv[1])) if len(sys.argv) > 1 else False

# Ensure that the hook config exists
if not os.path.exists(DirectoryLocation.DRBD_HOOK_CONFIG):
    sys.exit(0)

# Read the hook config
with open(DirectoryLocation.DRBD_HOOK_CONFIG, 'r') as fh:
    config = json.load(fh)

# Create RPC connection and obtain hard drive factory
rpc = Connection(username=config['username'], password=config['password'])
hard_drive_factory = rpc.get_connection('hard_drive_factory')

# Obtain hard drive object and set sync state
hard_drive_object = hard_drive_factory.getDrbdObjectByResourceName(
    drbd_resource)
rpc.annotate_object(hard_drive_object)
hard_drive_object.setSyncState(sync_state, update_remote=True)
Esempio n. 11
0
sys.path.insert(0, '/usr/lib')

from mcvirt.client.rpc import Connection  # noqa
from mcvirt.constants import DirectoryLocation  # noqa

# Obtain Drbd resource name from argument
drbd_resource = os.environ['DRBD_RESOURCE']

# Determine sync state from arguments
sync_state = bool(int(sys.argv[1])) if len(sys.argv) > 1 else False

# Ensure that the hook config exists
if not os.path.exists(DirectoryLocation.DRBD_HOOK_CONFIG):
    sys.exit(0)

# Read the hook config
with open(DirectoryLocation.DRBD_HOOK_CONFIG, 'r') as fh:
    config = json.load(fh)

# Create RPC connection and obtain hard drive factory
rpc = Connection(username=config['username'], password=config['password'])
hard_drive_factory = rpc.get_connection('hard_drive_factory')

# Obtain hard drive object and set sync state
hard_drive_object = hard_drive_factory.getDrbdObjectByResourceName(
    drbd_resource
)
rpc.annotate_object(hard_drive_object)
hard_drive_object.setSyncState(sync_state, update_remote=True)
Esempio n. 12
0
    def add_node(self, node_connection_string):
        """Connect to a remote MCVirt machine, setup shared authentication
        and clusters the machines.
        """
        # Ensure the user has privileges to manage the cluster
        self._get_registered_object('auth').assert_permission(
            PERMISSIONS.MANAGE_CLUSTER)

        # Ensure that the IP address configurations has been made correctly
        self.check_ip_configuration()

        try:
            config_json = base64.b64decode(node_connection_string)
            node_config = json.loads(config_json)
            assert 'username' in node_config and node_config['username']
            assert 'password' in node_config and node_config['password']
            assert 'ip_address' in node_config and node_config['ip_address']
            assert 'hostname' in node_config and node_config['hostname']
            assert 'ca_cert' in node_config and node_config['ca_cert']
        except (TypeError, ValueError, AssertionError):
            raise InvalidConnectionString('Connection string is invalid')

        # Determine if node is already connected to cluster
        if self.check_node_exists(node_config['hostname']):
            raise NodeAlreadyPresent(
                'Node %s is already connected to the cluster' %
                node_config['hostname'])

        # Create CA public key for machine
        ssl_object = self._get_registered_object(
            'certificate_generator_factory').get_cert_generator(
                node_config['hostname'])
        ssl_object.ca_pub_file = node_config['ca_cert']

        # Check remote machine, to ensure it can be synced without any
        # conflicts
        remote = Connection(username=node_config['username'],
                            password=node_config['password'],
                            host=node_config['hostname'])
        self.check_remote_machine(remote)
        remote = None
        original_cluster_nodes = self.get_nodes()

        # Add remote node
        self.add_node_configuration(
            node_name=node_config['hostname'],
            ip_address=node_config['ip_address'],
            connection_user=node_config['username'],
            connection_password=node_config['password'],
            ca_key=node_config['ca_cert'])

        # Obtain node connection to new node
        remote_node = self.get_remote_node(node_config['hostname'])

        # Generate local connection user for new remote node
        local_connection_info = self.generate_connection_info()

        # Add the local node to the new remote node
        remote_cluster_instance = remote_node.get_connection('cluster')
        remote_cluster_instance.add_node_configuration(
            node_name=local_connection_info[0],
            ip_address=local_connection_info[1],
            connection_user=local_connection_info[2],
            connection_password=local_connection_info[3],
            ca_key=local_connection_info[4])

        new_node_cert_gen_factory = remote_node.get_connection(
            'certificate_generator_factory')

        # Create client certificates for libvirt for the new node to connect to the
        # current cluster node
        new_node_cert_gen = new_node_cert_gen_factory.get_cert_generator(
            get_hostname())
        remote_node.annotate_object(new_node_cert_gen)

        # Generate CSR
        csr = new_node_cert_gen.generate_csr()

        # Sign CSR
        cert_gen_factory = self._get_registered_object(
            'certificate_generator_factory')
        cert_gen = cert_gen_factory.get_cert_generator(node_config['hostname'],
                                                       remote=True)
        pub_key = cert_gen.sign_csr(csr)

        # Add public key to new node
        new_node_cert_gen.add_public_key(pub_key)

        # Create client certificate for libvirt for the current cluster node to connect
        # to the new node
        cert_gen = cert_gen_factory.get_cert_generator(node_config['hostname'])

        # Generate CSR
        csr = cert_gen.generate_csr()

        # Sign CSR
        new_node_cert_gen = new_node_cert_gen_factory.get_cert_generator(
            get_hostname(), remote=True)
        remote_node.annotate_object(new_node_cert_gen)
        pub_key = new_node_cert_gen.sign_csr(csr)

        # Add public key to local node
        cert_gen.add_public_key(pub_key)

        # Sync credentials to/from old nodes in the cluster
        for original_node in original_cluster_nodes:
            # Share connection information between cluster node and new node
            original_node_remote = self.get_remote_node(original_node)
            original_cluster = original_node_remote.get_connection('cluster')
            original_node_con_info = original_cluster.generate_connection_info(
            )
            remote_cluster_instance.add_node_configuration(
                node_name=original_node_con_info[0],
                ip_address=original_node_con_info[1],
                connection_user=original_node_con_info[2],
                connection_password=original_node_con_info[3],
                ca_key=original_node_con_info[4])

            new_node_con_info = remote_cluster_instance.generate_connection_info(
            )
            original_cluster.add_node_configuration(
                node_name=new_node_con_info[0],
                ip_address=new_node_con_info[1],
                connection_user=new_node_con_info[2],
                connection_password=new_node_con_info[3],
                ca_key=new_node_con_info[4])

            # Create client certificates for libvirt for the new node to connect to the
            # current cluster node
            new_node_cert_gen = new_node_cert_gen_factory.get_cert_generator(
                original_node)
            remote_node.annotate_object(new_node_cert_gen)
            csr = new_node_cert_gen.generate_csr()
            original_node_cert_gen_factory = original_node_remote.get_connection(
                'certificate_generator_factory')
            original_node_cert_gen = original_node_cert_gen_factory.get_cert_generator(
                node_config['hostname'], remote=True)
            original_node_remote.annotate_object(original_node_cert_gen)
            pub_key = original_node_cert_gen.sign_csr(csr)
            new_node_cert_gen.add_public_key(pub_key)

            # Create client certificate for libvirt for the current cluster node to connect
            # to the new node
            original_node_cert_gen = original_node_cert_gen_factory.get_cert_generator(
                node_config['hostname'])
            original_node_remote.annotate_object(original_node_cert_gen)

            # Generate CSR
            csr = original_node_cert_gen.generate_csr()

            # Sign CSR
            new_node_cert_gen = new_node_cert_gen_factory.get_cert_generator(
                original_node, remote=True)
            remote_node.annotate_object(new_node_cert_gen)
            pub_key = new_node_cert_gen.sign_csr(csr)

            # Add public key to original node
            original_node_cert_gen.add_public_key(pub_key)

        # If Drbd is enabled on the local node, configure/enable it on the remote node
        if self._get_registered_object('node_drbd').is_enabled():
            remote_drbd = remote_node.get_connection('node_drbd')
            remote_drbd.enable(
                secret=MCVirtConfig().get_config()['drbd']['secret'])

        # Sync users
        self.sync_users(remote_node)

        # Sync networks
        self.sync_networks(remote_node)

        # Sync global permissions
        self.sync_permissions(remote_node)

        # Sync VMs
        self.sync_virtual_machines(remote_node)
Esempio n. 13
0
class TestBase(unittest.TestCase):
    """Provide base test case, with constructor/destructor
    for providing access to the parser and RPC
    """

    # Define RPC credentials, which are the default superuser credentials
    # that are supplied with MCVirt
    RPC_USERNAME = '******'
    RPC_PASSWORD = '******'

    def setUp(self):
        """Obtain connections to the daemon and create various
        member variables.
        """
        # Create and store RPC connection to daemon.
        self.rpc = Connection(self.RPC_USERNAME, self.RPC_PASSWORD)

        # Create and store parser instance
        self.parser = Parser(verbose=False)

        # Obtain the session ID from the RPC connection and re-use this,
        # so that the parser does not need to authenticate with a password
        # self.parser.parse_arguments('list --username %s --password %s' % (self.RPC_USERNAME,
        #                                                                   self.RPC_PASSWORD))

        self.parser.USERNAME = self.RPC_USERNAME
        self.parser.SESSION_ID = self.rpc.session_id

        # Setup variable for test VM
        self.test_vms = \
            {
                'TEST_VM_1':
                {
                    'name': 'mcvirt-unittest-vm',
                    'cpu_count': 1,
                    'memory_allocation': 100,
                    'disk_size': [100],
                    'networks': ['Production']
                },
                'TEST_VM_2':
                {
                    'name': 'mcvirt-unittest-vm2',
                    'cpu_count': 2,
                    'memory_allocation': 120,
                    'disk_size': [100],
                    'networks': ['Production']
                }
            }

        # Ensure any test VM is stopped and removed from the machine
        self.stop_and_delete(self.test_vms['TEST_VM_2']['name'])
        self.stop_and_delete(self.test_vms['TEST_VM_1']['name'])

        self.vm_factory = self.rpc.get_connection('virtual_machine_factory')

        self.test_network_name = 'testnetwork'
        self.test_physical_interface = 'vmbr0'
        self.network_factory = self.rpc.get_connection('network_factory')

        # Determine if the test network exists. If so, delete it
        if self.network_factory.check_exists(self.test_network_name):
            network = self.network_factory.get_network_by_name(
                self.test_network_name)
            self.rpc.annotate_object(network)
            network.delete()

    def tearDown(self):
        """Destroy stored objects."""
        # Ensure any test VM is stopped and removed from the machine
        self.rpc.ignore_drbd()
        self.stop_and_delete(self.test_vms['TEST_VM_2']['name'])
        self.stop_and_delete(self.test_vms['TEST_VM_1']['name'])

        # Remove the test network, if it exists
        if self.network_factory.check_exists(self.test_network_name):
            network = self.network_factory.get_network_by_name(
                self.test_network_name)
            self.rpc.annotate_object(network)
            network.delete()

        self.network_factory = None
        self.vm_factory = None
        self.rpc = None
        self.parser = None

    def create_vm(self, vm_name, storage_type):
        """Create a test VM, annotate object and ensure it exists"""
        vm_object = self.vm_factory.create(
            self.test_vms[vm_name]['name'],
            self.test_vms[vm_name]['cpu_count'],
            self.test_vms[vm_name]['memory_allocation'],
            self.test_vms[vm_name]['disk_size'],
            self.test_vms[vm_name]['networks'],
            storage_type=storage_type)
        self.rpc.annotate_object(vm_object)
        self.assertTrue(
            self.vm_factory.check_exists(self.test_vms[vm_name]['name']))
        return vm_object

    def stop_and_delete(self, vm_name):
        """Stop and remove a virtual machine"""
        virtual_machine_factory = self.rpc.get_connection(
            'virtual_machine_factory')

        if virtual_machine_factory.check_exists(vm_name):
            vm_object = virtual_machine_factory.getVirtualMachineByName(
                vm_name)
            self.rpc.annotate_object(vm_object)

            # Reset sync state for any Drbd disks
            for disk_object in vm_object.getHardDriveObjects():
                self.rpc.annotate_object(disk_object)
                if disk_object.get_type() == 'Drbd':
                    disk_object.setSyncState(True)

            if not vm_object.isRegistered():
                # Manually register VM on local node
                vm_object.register()

            # Stop the VM if it is running
            if vm_object.getPowerState() == PowerStates.RUNNING.value:
                vm_object.stop()

            if vm_object.getLockState() is LockStates.LOCKED.value:
                vm_object.setLockState(LockStates.UNLOCKED.value)

            # Delete VM
            vm_object.delete(True)
Esempio n. 14
0
class Parser(object):
    """Provides an argument parser for MCVirt."""

    AUTH_FILE = '.mcvirt-auth'

    def __init__(self, verbose=True):
        """Configure the argument parser object."""
        self.print_output = []
        self.username = None
        self.session_id = None
        self.rpc = None
        self.auth_cache_file = os.getenv('HOME') + '/' + self.AUTH_FILE
        self.verbose = verbose
        self.parent_parser = ThrowingArgumentParser(add_help=False)

        self.global_option = self.parent_parser.add_argument_group('Global optional arguments')
        self.global_option.add_argument('--username', '-U', dest='username',
                                        help='MCVirt username')
        self.global_option.add_argument('--password', dest='password',
                                        help='MCVirt password')
        self.global_option.add_argument('--cache-credentials', dest='cache_credentials',
                                        action='store_true',
                                        help=('Store the session ID, so it can be used for '
                                              'multiple MCVirt calls.'))
        self.global_option.add_argument('--ignore-failed-nodes', dest='ignore_failed_nodes',
                                        help='Ignores nodes that are inaccessible',
                                        action='store_true')
        self.global_option.add_argument('--accept-failed-nodes-warning',
                                        dest='accept_failed_nodes_warning',
                                        help=argparse.SUPPRESS, action='store_true')
        self.global_option.add_argument('--ignore-drbd', dest='ignore_drbd',
                                        help='Ignores Drbd state', action='store_true')

        argparser_description = "\nMCVirt - Managed Consistent Virtualisation\n\n" + \
                                'Manage the MCVirt host'
        argparser_epilog = "\nFor more information, see http://mcvirt.itdev.co.uk\n"

        # Create an argument parser object
        self.parser = ThrowingArgumentParser(description=argparser_description,
                                             epilog=argparser_epilog,
                                             formatter_class=argparse.RawDescriptionHelpFormatter)
        self.subparsers = self.parser.add_subparsers(dest='action', metavar='Action',
                                                     help='Action to perform')

        # Add arguments for starting a VM
        StartParser(self.subparsers, self.parent_parser)

        # Add arguments for stopping a VM
        StopParser(self.subparsers, self.parent_parser)

        # Add arguments for resetting a VM
        ResetParser(self.subparsers, self.parent_parser)

        # Add arguments for shutting down a VM
        ShutdownParser(self.subparsers, self.parent_parser)

        # Add arguments for fixing deadlock on a vm
        ClearMethodLockParser(self.subparsers, self.parent_parser)

        # Add arguments for ISO functions
        IsoParser(self.subparsers, self.parent_parser)

        # Add arguments for managing users
        UserParser(self.subparsers, self.parent_parser)

        # Add arguments for creating a VM
        CreateParser(self.subparsers, self.parent_parser)

        # Get arguments for deleting a VM
        DeleteParser(self.subparsers, self.parent_parser)

        RegisterParser(self.subparsers, self.parent_parser)
        UnregisterParser(self.subparsers, self.parent_parser)

        # Get arguments for updating a VM
        UpdateParser(self.subparsers, self.parent_parser)

        PermissionParser(self.subparsers, self.parent_parser)

        GroupParser(self.subparsers, self.parent_parser)

        # Create subparser for network-related commands
        NetworkParser(self.subparsers, self.parent_parser)

        # Get arguments for getting VM information
        InfoParser(self.subparsers, self.parent_parser)

        # Get arguments for listing VMs
        ListParser(self.subparsers, self.parent_parser)

        # Get arguments for cloning a VM
        CloneParser(self.subparsers, self.parent_parser)

        # Get arguments for cloning a VM
        DuplicateParser(self.subparsers, self.parent_parser)

        # Get arguments for migrating a VM
        MigrateParser(self.subparsers, self.parent_parser)

        # Create sub-parser for moving VMs
        MoveParser(self.subparsers, self.parent_parser)

        # Create sub-parser for cluster-related commands
        ClusterParser(self.subparsers, self.parent_parser)

        StorageParser(self.subparsers, self.parent_parser)

        HardDriveParser(self.subparsers, self.parent_parser)

        # Create subparser for commands relating to the local node configuration
        NodeParser(self.subparsers, self.parent_parser)

        # Create sub-parser for VM verification
        VerifyParser(self.subparsers, self.parent_parser)

        # Create sub-parser for VM Disk resync
        ResyncParser(self.subparsers, self.parent_parser)

        # Create sub-parser for Drbd-related commands
        DrbdParser(self.subparsers, self.parent_parser)

        # Create sub-parser for watchdog
        WatchdogParser(self.subparsers, self.parent_parser)

        # Create sub-parser for backup commands
        BackupParser(self.subparsers, self.parent_parser)

        # Create sub-parser for managing VM locks
        LockParser(self.subparsers, self.parent_parser)

        self.exit_parser = self.subparsers.add_parser('exit', help='Exits the MCVirt shell',
                                                      parents=[self.parent_parser])

    def print_status(self, status):
        """Print if the user has specified that the parser should print statuses."""
        if self.verbose:
            print status
        else:
            self.print_output.append(status)

    def check_ignore_failed(self, args):
        """Check ignore failed"""
        if args.ignore_failed_nodes:
            # If the user has specified to ignore the cluster,
            # print a warning and confirm the user's answer
            if not args.accept_failed_nodes_warning:
                self.print_status(('WARNING: Running MCVirt with --ignore-failed-nodes'
                                   ' can leave the cluster in an inconsistent state!'))
                continue_answer = System.getUserInput('Would you like to continue? (Y/n): ')

                if continue_answer.strip() is not 'Y':
                    self.print_status('Cancelled...')
                    return
            return True
        return False

    def authenticate_saved_session(self, ignore_cluster):
        """Attempt to authenticate using saved session"""
        # Try logging in with saved session
        auth_session = None
        try:
            with open(self.auth_cache_file, 'r') as cache_fh:
                auth_username = cache_fh.readline().strip()
                auth_session = cache_fh.readline().strip()
        except IOError:
            pass

        if auth_session:
            try:
                self.rpc = Connection(username=auth_username, session_id=auth_session,
                                      ignore_cluster=ignore_cluster)
                self.session_id = self.rpc.session_id
                self.username = self.rpc.username
            except AuthenticationError:
                # If authentication fails with cached session,
                # print error, attempt to remove sessionn file and
                # remove rpc connection
                self.print_status('Authentication error occured when using saved session.')
                try:
                    os.remove(self.auth_cache_file)
                except OSError:
                    pass
                self.rpc = None

    def authenticate_username_password(self, args, ignore_cluster):
        """Authenticate using username and password"""
        # Check if user/password have been passed. Else, ask for them.
        username = args.username if args.username else System.getUserInput(
            'Username: '******'Password: '******'w') as cache_fh:
                    cache_fh.write("%s\n%s" % (self.rpc.username, self.rpc.session_id))
            except OSError:
                pass

    def parse_arguments(self, script_args=None):
        """Parse arguments and performs actions based on the arguments."""
        # If arguments have been specified, split, so that
        # an array is sent to the argument parser
        if script_args is not None:
            script_args = script_args.split()

        args = self.parser.parse_args(script_args)

        ignore_cluster = self.check_ignore_failed(args)

        if self.session_id and self.username:
            self.rpc = Connection(username=self.username, session_id=self.session_id,
                                  ignore_cluster=ignore_cluster)
        else:
            # Obtain connection to Pyro server
            if not (args.password or args.username):
                self.authenticate_saved_session(ignore_cluster)

            if not self.rpc:
                self.authenticate_username_password(args, ignore_cluster)

        self.store_cached_session(args)

        if args.ignore_drbd:
            self.rpc.ignore_drbd()

        # If a custom parser function has been defined, used this and exit
        # instead of running through (old) main parser workflow
        if 'func' in dir(args):
            args.func(args=args, p_=self)
        else:
            raise ArgumentParserException('No handler registered for parser')