diff options
| author | sateesh <sateesh.chodapuneedi@citrix.com> | 2011-03-24 22:43:46 +0000 |
|---|---|---|
| committer | Tarmac <> | 2011-03-24 22:43:46 +0000 |
| commit | 4e179b4fa9ab35dc50486e7f42e1dc6d06b74c81 (patch) | |
| tree | 44df493688ebe0d8566b127981a29fd888dc9c53 /nova/console | |
| parent | cc0108d8e2328a9511333d9322e5631416672f03 (diff) | |
| parent | b7150a461ab2aaee3c0a0f7e2b6588ddd4324b52 (diff) | |
| download | nova-4e179b4fa9ab35dc50486e7f42e1dc6d06b74c81.tar.gz nova-4e179b4fa9ab35dc50486e7f42e1dc6d06b74c81.tar.xz nova-4e179b4fa9ab35dc50486e7f42e1dc6d06b74c81.zip | |
Implementation of blueprint hypervisor-vmware-vsphere-support. (Link to blueprint: https://blueprints.launchpad.net/nova/+spec/hypervisor-vmware-vsphere-support)
Adds support for hypervisor vmware ESX/ESXi server in OpenStack (Nova).
Key features included are,
1) Support for FLAT and VLAN networking model
2) Support for Guest console access through VMware vmrc
3) Integrated with Glance service for image storage and retrival
Documentation: A readme file at "doc/source/vmwareapi_readme.rst" encapsulates configuration/installation instructions required to use this module/feature.
Diffstat (limited to 'nova/console')
| -rw-r--r-- | nova/console/vmrc.py | 144 | ||||
| -rw-r--r-- | nova/console/vmrc_manager.py | 158 |
2 files changed, 302 insertions, 0 deletions
diff --git a/nova/console/vmrc.py b/nova/console/vmrc.py new file mode 100644 index 000000000..521da289f --- /dev/null +++ b/nova/console/vmrc.py @@ -0,0 +1,144 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Citrix Systems, Inc. +# Copyright 2011 OpenStack LLC. +# +# 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. + +""" +VMRC console drivers. +""" + +import base64 +import json + +from nova import exception +from nova import flags +from nova import log as logging +from nova.virt.vmwareapi import vim_util + +flags.DEFINE_integer('console_vmrc_port', + 443, + "port for VMware VMRC connections") +flags.DEFINE_integer('console_vmrc_error_retries', + 10, + "number of retries for retrieving VMRC information") + +FLAGS = flags.FLAGS + + +class VMRCConsole(object): + """VMRC console driver with ESX credentials.""" + + def __init__(self): + super(VMRCConsole, self).__init__() + + @property + def console_type(self): + return 'vmrc+credentials' + + def get_port(self, context): + """Get available port for consoles.""" + return FLAGS.console_vmrc_port + + def setup_console(self, context, console): + """Sets up console.""" + pass + + def teardown_console(self, context, console): + """Tears down console.""" + pass + + def init_host(self): + """Perform console initialization.""" + pass + + def fix_pool_password(self, password): + """Encode password.""" + # TODO(sateesh): Encrypt pool password + return password + + def generate_password(self, vim_session, pool, instance_name): + """ + Returns VMRC Connection credentials. + + Return string is of the form '<VM PATH>:<ESX Username>@<ESX Password>'. + """ + username, password = pool['username'], pool['password'] + vms = vim_session._call_method(vim_util, "get_objects", + "VirtualMachine", ["name", "config.files.vmPathName"]) + vm_ds_path_name = None + vm_ref = None + for vm in vms: + vm_name = None + ds_path_name = None + for prop in vm.propSet: + if prop.name == "name": + vm_name = prop.val + elif prop.name == "config.files.vmPathName": + ds_path_name = prop.val + if vm_name == instance_name: + vm_ref = vm.obj + vm_ds_path_name = ds_path_name + break + if vm_ref is None: + raise exception.NotFound(_("instance - %s not present") % + instance_name) + json_data = json.dumps({"vm_id": vm_ds_path_name, + "username": username, + "password": password}) + return base64.b64encode(json_data) + + def is_otp(self): + """Is one time password or not.""" + return False + + +class VMRCSessionConsole(VMRCConsole): + """VMRC console driver with VMRC One Time Sessions.""" + + def __init__(self): + super(VMRCSessionConsole, self).__init__() + + @property + def console_type(self): + return 'vmrc+session' + + def generate_password(self, vim_session, pool, instance_name): + """ + Returns a VMRC Session. + + Return string is of the form '<VM MOID>:<VMRC Ticket>'. + """ + vms = vim_session._call_method(vim_util, "get_objects", + "VirtualMachine", ["name"]) + vm_ref = None + for vm in vms: + if vm.propSet[0].val == instance_name: + vm_ref = vm.obj + if vm_ref is None: + raise exception.NotFound(_("instance - %s not present") % + instance_name) + virtual_machine_ticket = \ + vim_session._call_method( + vim_session._get_vim(), + "AcquireCloneTicket", + vim_session._get_vim().get_service_content().sessionManager) + json_data = json.dumps({"vm_id": str(vm_ref.value), + "username": virtual_machine_ticket, + "password": virtual_machine_ticket}) + return base64.b64encode(json_data) + + def is_otp(self): + """Is one time password or not.""" + return True diff --git a/nova/console/vmrc_manager.py b/nova/console/vmrc_manager.py new file mode 100644 index 000000000..09beac7a0 --- /dev/null +++ b/nova/console/vmrc_manager.py @@ -0,0 +1,158 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Citrix Systems, Inc. +# Copyright 2011 OpenStack LLC. +# +# 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. + +""" +VMRC Console Manager. +""" + +from nova import exception +from nova import flags +from nova import log as logging +from nova import manager +from nova import rpc +from nova import utils +from nova.virt.vmwareapi_conn import VMWareAPISession + +LOG = logging.getLogger("nova.console.vmrc_manager") + +FLAGS = flags.FLAGS +flags.DEFINE_string('console_public_hostname', + '', + 'Publicly visible name for this console host') +flags.DEFINE_string('console_driver', + 'nova.console.vmrc.VMRCConsole', + 'Driver to use for the console') + + +class ConsoleVMRCManager(manager.Manager): + + """ + Manager to handle VMRC connections needed for accessing instance consoles. + """ + + def __init__(self, console_driver=None, *args, **kwargs): + self.driver = utils.import_object(FLAGS.console_driver) + super(ConsoleVMRCManager, self).__init__(*args, **kwargs) + + def init_host(self): + self.sessions = {} + self.driver.init_host() + + def _get_vim_session(self, pool): + """Get VIM session for the pool specified.""" + vim_session = None + if pool['id'] not in self.sessions.keys(): + vim_session = VMWareAPISession(pool['address'], + pool['username'], + pool['password'], + FLAGS.console_vmrc_error_retries) + self.sessions[pool['id']] = vim_session + return self.sessions[pool['id']] + + def _generate_console(self, context, pool, name, instance_id, instance): + """Sets up console for the instance.""" + LOG.debug(_("Adding console")) + + password = self.driver.generate_password( + self._get_vim_session(pool), + pool, + instance.name) + + console_data = {'instance_name': name, + 'instance_id': instance_id, + 'password': password, + 'pool_id': pool['id']} + console_data['port'] = self.driver.get_port(context) + console = self.db.console_create(context, console_data) + self.driver.setup_console(context, console) + return console + + @exception.wrap_exception + def add_console(self, context, instance_id, password=None, + port=None, **kwargs): + """ + Adds a console for the instance. If it is one time password, then we + generate new console credentials. + """ + instance = self.db.instance_get(context, instance_id) + host = instance['host'] + name = instance['name'] + pool = self.get_pool_for_instance_host(context, host) + try: + console = self.db.console_get_by_pool_instance(context, + pool['id'], + instance_id) + if self.driver.is_otp(): + console = self._generate_console( + context, + pool, + name, + instance_id, + instance) + except exception.NotFound: + console = self._generate_console( + context, + pool, + name, + instance_id, + instance) + return console['id'] + + @exception.wrap_exception + def remove_console(self, context, console_id, **_kwargs): + """Removes a console entry.""" + try: + console = self.db.console_get(context, console_id) + except exception.NotFound: + LOG.debug(_("Tried to remove non-existent console " + "%(console_id)s.") % + {'console_id': console_id}) + return + LOG.debug(_("Removing console " + "%(console_id)s.") % + {'console_id': console_id}) + self.db.console_delete(context, console_id) + self.driver.teardown_console(context, console) + + def get_pool_for_instance_host(self, context, instance_host): + """Gets console pool info for the instance.""" + context = context.elevated() + console_type = self.driver.console_type + try: + pool = self.db.console_pool_get_by_host_type(context, + instance_host, + self.host, + console_type) + except exception.NotFound: + pool_info = rpc.call(context, + self.db.queue_get_for(context, + FLAGS.compute_topic, + instance_host), + {"method": "get_console_pool_info", + "args": {"console_type": console_type}}) + pool_info['password'] = self.driver.fix_pool_password( + pool_info['password']) + pool_info['host'] = self.host + # ESX Address or Proxy Address + public_host_name = pool_info['address'] + if FLAGS.console_public_hostname: + public_host_name = FLAGS.console_public_hostname + pool_info['public_hostname'] = public_host_name + pool_info['console_type'] = console_type + pool_info['compute_host'] = instance_host + pool = self.db.console_pool_create(context, pool_info) + return pool |
