1. openstack接管vCenter中已有的VM,通过VNC链接VM
代码添加以及调研流程
1): horizon代码
在api/nova.py中添加以下方法,该方法调研novaclient中get_vnc_console_for_vmware方法
def get_vnc_console_for_vmware(request, hypervisor,
instance_id,
host, console_type='novnc'):
return VNCConsole(novaclient(request).
vms_hypervisors.
get_vnc_console_for_vmware(hypervisor,
instance_id, host,
console_type=console_type)['console'])
2): novaclient代码
在novaclient/v1.1/servers.py中添加get_vnc_console_for_vmware方法,如下:
def get_vnc_console_for_vmware(self, server, console_type,
host, service_host):
"""
Get a vnc console for an instance
:param server: The :class:`Server` (or its ID) to add an IP to.
:param console_type: Type of vnc console to get ('novnc' or 'xvpvnc')
"""
return self._action('os-getVNCConsoleVmware', server,
{'type': console_type,
'host': host,
'service_host': service_host})[1]
该方法通过wsgi设置的os-getVNCConsoleVmware调用nova api中的get_vnc_console_for_vmware方法
3): nova代码
在nova/api/openstack/compute/contrib/consoles.py中添加如下方法:
@wsgi.action('os-getVNCConsoleVmware')
def get_vnc_console_for_vmware(self, req, id, body):
"""Get vnc connection information to access a server."""
context = req.environ['nova.context']
authorize(context)
# If type is not supplied or unknown, get_vnc_console below will cope
console_type = body['os-getVNCConsoleVmware'].get('type')
host = body['os-getVNCConsoleVmware'].get('host')
hypervisor_name = body['os-getVNCConsoleVmware'].get('hypervisor_name')
compute_nodes = self.host_api.compute_node_search_by_hypervisor(context, hypervisor_name)
service_host = compute_nodes[0].service.host
try:
output = self.compute_api.get_vnc_console_for_vmware(context,
console_type=console_type,
instance_id=id,
host= host,
service_host=service_host)
except exception.InstanceNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
except exception.InstanceNotReady as e:
raise webob.exc.HTTPConflict(
explanation=_('Instance not yet ready'))
except NotImplementedError:
msg = _("Unable to get vnc console, functionality not implemented")
raise webob.exc.HTTPNotImplemented(explanation=msg)
return {'console': {'type': console_type, 'url': output['url']}}
该方法中self.compute_api.get_vnc_console_for_vmware调研compute api中的get_vnc_console_for_vmware方法
在nova/compute/api.py中添加get_vnc_console_for_vmware方法如下:
def get_vnc_console_for_vmware(self, context, **kwargs):
"""Get a url to an instance Console."""
connect_info = self.compute_rpcapi.get_vnc_console_for_vmware(context,
console_type=kwargs['console_type'], instance_id=kwargs['instance_id'],
service_host=kwargs['service_host'])
self.consoleauth_rpcapi.authorize_console_for_vmware(context,
connect_info['token'], kwargs['console_type'],
kwargs['host'], connect_info['port'],
connect_info['internal_access_path'],
kwargs['instance_id'],kwargs['service_host'])
return {'url': connect_info['access_url']}
该方法中self.compute_rpcapi.get_vnc_console_for_vmware调用compute repapi中的get_vnc_console_for_vmware方法和
self.consoleauth_rpcapi.authorize_console_for_vmware调用consoleauth repapi中的authorize_console_for_vmware方法
分支一
在nova/compute/rpcapi.py中添加get_vnc_console_for_vmware方法如下代码:
def get_vnc_console_for_vmware(self, ctxt, console_type, **kwargs):
if self.client.can_send_version('3.2'):
version = '3.2'
else:
# NOTE(russellb) Havana compat
version = self._get_compat_version('3.0', '2.0')
#instance = jsonutils.to_primitive(instance)
cctxt = self.client.prepare(server=kwargs['service_host'],
version=version)
return cctxt.call(ctxt, 'get_vnc_console_for_vmware',
instance_id=kwargs['instance_id'], console_type=console_type)
该方法通过rpc请求调用nova/compute/manager.py中的get_vnc_console_for_vmware方法
在nova/compute/manager.py中添加get_vnc_console_for_vmware方法代码如下:
def get_vnc_console_for_vmware(self, context, console_type, instance_id):
"""Return connection information for a vnc console."""
context = context.elevated()
LOG.debug(_("Getting vnc console instance_id ======== %s ") % instance_id)
token = str(uuid.uuid4())
if not CONF.vnc_enabled:
raise exception.ConsoleTypeInvalid(console_type=console_type)
if console_type == 'novnc':
# For essex, novncproxy_base_url must include the full path
# including the html file (like http://myhost/vnc_auto.html)
access_url = '%s?token=%s' % (CONF.novncproxy_base_url, token)
elif console_type == 'xvpvnc':
access_url = '%s?token=%s' % (CONF.xvpvncproxy_base_url, token)
else:
raise exception.ConsoleTypeInvalid(console_type=console_type)
try:
# Retrieve connect info from driver, and then decorate with our
# access info token
connect_info = self.driver.get_vnc_console_for_vmware(context, instance_id)
connect_info['token'] = token
connect_info['access_url'] = access_url
except exception.InstanceNotFound:
raise exception.InstanceNotReady(instance_id=instance_id)
return connect_info
该方法中self.driver.get_vnc_console_for_vmware调研nova/virt/vmwareapi/driver.py中的get_vnc_console_for_vmware方法。该方法返回一个字典,
字典中包含token, novnc代理url, vm的vnc端口
在nova/virt/vmwareapi/driver.py中添加get_vnc_console_for_vmware方法如下:
def get_vnc_console_for_vmware(self, context, instance_id):
"""Return link to instance's VNC console."""
return self._vmops.get_vnc_console_for_vmware(instance_id)
该方法调用nova/vrit/vmwareapi/vmops.py中的get_vnc_console_for_vmware方法:
在nova/vrit/vmwareapi/vmops.py添加get_vnc_console_for_vmware方法如下:
def get_vnc_console_for_vmware(self, instance_id):
"""Return connection info for a vnc console."""
vm_ref = vm_util._get_vm_ref_from_vm_uuid(self._session, instance_id)
opt_value = self._session._call_method(vim_util,
'get_dynamic_property',
vm_ref, 'VirtualMachine',
vm_util.VNC_CONFIG_KEY)
if opt_value:
port = int(opt_value.value)
else:
raise exception.ConsoleTypeUnavailable(console_type='vnc')
return {'port': port,
'internal_access_path': None}
该方法返回一个字典,该字典中包含虚拟机(VM)的vnc端口
分支二
在nova/consoleauth/rpcapi.py中添加get_vnc_console_for_vmware方法如下代码:
def authorize_console_for_vmware(self, ctxt, token, console_type, host, port,
internal_access_path, instance_uuid, service_host):
# The remote side doesn't return anything, but we want to block
# until it completes.'
version = '2.0'
if not self.client.can_send_version('2.0'):
# NOTE(russellb) Havana compat
version = '1.2'
cctxt = self.client.prepare(version=version)
return cctxt.call(ctxt,
'authorize_console_for_vmware',
token=token, console_type=console_type,
host=host, port=port,
internal_access_path=internal_access_path,
instance_uuid=instance_uuid,
service_host=service_host)
该方法通过rpc请求调用nova/consoleauth/manager.py中的authorize_console_for_vmware方法
在nova/consoleauth/manager.py中添加authorize_console_for_vmware方法代码如下:
def authorize_console_for_vmware(self, context, token, console_type, host, port,
internal_access_path, instance_uuid, service_host):
token_dict = {'token': token,
'instance_uuid': instance_uuid,
'console_type': console_type,
'host': host,
'port': port,
'service_host': service_host,
'internal_access_path': internal_access_path,
'last_activity_at': time.time()}
data = jsonutils.dumps(token_dict)
self.mc.set(token.encode('UTF-8'), data, CONF.console_token_ttl)
tokens = self._get_tokens_for_instance(instance_uuid)
# Remove the expired tokens from cache.
for tok in tokens:
token_str = self.mc.get(tok.encode('UTF-8'))
if not token_str:
tokens.remove(tok)
tokens.append(token)
self.mc.set(instance_uuid.encode('UTF-8'),
jsonutils.dumps(tokens))
LOG.audit(_("Received Token: %(token)s, %(token_dict)s"),
{'token': token, 'token_dict': token_dict})
该方法注意是用存储token,console_type,instance_uuid等信息,用于验证。
以上代码返回的novnc_proxy_base_url,在浏览器中执行该url时,控制节点的noVNC服务监听到6080 HTTP 端口,接受此URL请求,然后向Nova-consoleauth发送check_token消息。
nova/consoleauth/rpcapi.py中代码:
def check_token(self, ctxt, token):
version = '2.0'
if not self.client.can_send_version('2.0'):
# NOTE(russellb) Havana compat
version = '1.0'
cctxt = self.client.prepare(version=version)
return cctxt.call(ctxt, 'check_token', token=token)
通过rpc调用nova/consoleauth/manager.py中的check_token方法
在nova/consoleauth/manager.py中代码如下:
def check_token(self, context, token):
token_str = self.mc.get(token.encode('UTF-8'))
token_valid = (token_str is not None)
LOG.audit(_("Checking Token: %(token)s, %(token_valid)s"),
{'token': token, 'token_valid': token_valid})
if token_valid:
token = jsonutils.loads(token_str)
if 'service_host' in token:
if self._validate_token_for_vmware(context, token):
return token
else:
if self._validate_token(context, token):
return token
调用_validate_token_for_vmware方法_validate_token_for_vmware方法代码如下:
def _validate_token_for_vmware(self, context, token):
instance_uuid = token['instance_uuid']
if instance_uuid is None:
return False
# NOTE(comstud): consoleauth was meant to run in API cells. So,
# if cells is enabled, we must call down to the child cell for
# the instance.
if CONF.cells.enable:
return self.cells_rpcapi.validate_console_port(context,
instance_uuid, token['port'], token['console_type'])
return self.compute_rpcapi.validate_console_port_for_vmware(context,
instance_id=instance_uuid,
port=token['port'],
console_type=token['console_type'],
service_host=token['service_host'])
该方法调研nova/compute/rpcapi.py中validate_console_port_for_vmware方法:
在nova/compute/rpcapi.py中添加代码如下:
def validate_console_port_for_vmware(self, ctxt, **kwargs):
if self.client.can_send_version('3.3'):
version = '3.3'
else:
# NOTE(russellb) Havana compat
version = self._get_compat_version('3.0', '2.26')
cctxt = self.client.prepare(server=kwargs['service_host'],
version=version)
return cctxt.call(ctxt, 'validate_console_port_for_vmware',
instance_id=kwargs['instance_id'], port=kwargs['port'],
console_type=kwargs['console_type'])
该方法通过rpc调研nova/compute/manager.py中的validate_console_port_for_vmware方法:
在nova/compute/manager.py中添加如下代码:
def validate_console_port_for_vmware(self, ctxt, instance_id, port, console_type):
console_info = self.driver.get_vnc_console_for_vmware(ctxt, instance_id)
return console_info['port'] == port
该方法调研driver中的get_vnc_console_for_vmware返回该VM的vnc端口号,然后和传递过来的端口进行比较判断,返回比较结果
|