openstack 之 novnc流程分析

这篇具有很好参考价值的文章主要介绍了openstack 之 novnc流程分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一.novnc简介

二.openstack中的novnc工作流程

三.源码分析


一.novnc简介

        noVNC是一个支持HTML5的VNC客户端,主要作用就是与远端的vnc server进行互通,从而实现对于远端主机的控制。说白了,我们可以通过VNC客户端或者支持HTML5的浏览器访问远端安装了vnc server的服务器桌面从而进行控制。

        但是vnc server发送的数据都是基于TCP之上的,而novnc处理的数据都是基于WebSocket之上的数据,所以vnc客户端无法直接与vnc server进行通讯,因此中间加入了一个代理服务器:WebSockify来实现WebSockify和TCP数据之间的转换。

二.openstack中的novnc工作流程

novnc,云计算,云计算

三.源码分析

a. 用户点击某一个虚拟机的console,请求访问通过该虚拟机的id访问该虚拟机的页面。

b. 浏览器向nova-api发送访问请求获得该虚拟机的url。

c. nova-api调用/nova/api/openstack/compute/remote_consoles.py的get_vnc_consoles()函数。

    def get_vnc_console(self, req, id, body):
        """Get text console output."""
        context = req.environ['nova.context']
        context.can(rc_policies.BASE_POLICY_NAME)

        # If type is not supplied or unknown, get_vnc_console below will cope
        console_type = body['os-getVNCConsole'].get('type')

        instance = common.get_instance(self.compute_api, context, id)
        try:
            output = self.compute_api.get_vnc_console(context,
                                                      instance,
                                                      console_type)
        except exception.ConsoleTypeUnavailable as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())
        except (exception.InstanceUnknownCell,
                     exception.InstanceNotFound) as e:
            raise webob.exc.HTTPNotFound(explanation=e.format_message())
        except exception.InstanceNotReady as e:
            raise webob.exc.HTTPConflict(explanation=e.format_message())
        except NotImplementedError:
            common.raise_feature_not_supported()

        return {'console': {'type': console_type, 'url': output['url']}}

上一个函数调用/nova/compute/api.py/API/get_vnc_console()函数,该函数再调用/nova/compute/rpcapi.py/ComputeAPI/get_vnc_console()函数向nova-compute进行同步RPC调用。

    def get_vnc_console(self, ctxt, instance, console_type):
        version = self._ver(ctxt, '4.0')
        cctxt = self.router.client(ctxt).prepare(
                server=_compute_host(None, instance), version=version)
        return cctxt.call(ctxt, 'get_vnc_console',
                          instance=instance, console_type=console_type)

d. nova-compute调用manager.py的get_vnc_console()函数获取该RPC消息,然后调用generate_uuid()函数生成uuid作为token;然后判断如果console_type为novnc,则生成access_url:novncproxy_base_url(nova.conf文件)+token(刚才生成的);接下来,通过驱动driver调用libvirt的get_vnc_console()函数从获取vnc_server的详细配置信息(console:host地址(计算节点连接各个instance的内网ip地址)和端口号port);最后将host,port和access_url连接起来生成connect_info。

/nova/compute/manager.py/ComputeManager/get_vnc_console():

    def get_vnc_console(self, context, console_type, instance):
        """Return connection information for a vnc console."""
        context = context.elevated()
        LOG.debug("Getting vnc console", instance=instance)
        token = uuidutils.generate_uuid()

        if not CONF.vnc.enabled:
            raise exception.ConsoleTypeUnavailable(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.vnc.novncproxy_base_url, token)
        elif console_type == 'xvpvnc':
            access_url = '%s?token=%s' % (CONF.vnc.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
            console = self.driver.get_vnc_console(context, instance)
            connect_info = console.get_connection_info(token, access_url)
        except exception.InstanceNotFound:
            if instance.vm_state != vm_states.BUILDING:
                raise
            raise exception.InstanceNotReady(instance_id=instance.uuid)

        return connect_info

/nova/virt/libvirt/driver.py/LibvirtDriver/get_vnc_console() :

    def get_vnc_console(self, context, instance):
        def get_vnc_port_for_instance(instance_name):
            guest = self._host.get_guest(instance)

            xml = guest.get_xml_desc()
            xml_dom = etree.fromstring(xml)

            graphic = xml_dom.find("./devices/graphics[@type='vnc']")
            if graphic is not None:
                return graphic.get('port')
            # NOTE(rmk): We had VNC consoles enabled but the instance in
            # question is not actually listening for connections.
            raise exception.ConsoleTypeUnavailable(console_type='vnc')

        port = get_vnc_port_for_instance(instance.name)
        host = CONF.vnc.server_proxyclient_address

        return ctype.ConsoleVNC(host=host, port=port)

e. nova-api继续调用/nova/compute/api.py/API/get_vnc_console()函数,到目前为止,nova-api已经通过get_vnc_console()函数获得了connect_info,然后再次调用consoleauth的authorize_console()函数,具体实现见后文。

    def get_vnc_console(self, context, instance, console_type):
        """Get a url to an instance Console."""
        connect_info = self.compute_rpcapi.get_vnc_console(context,
                instance=instance, console_type=console_type)

        self.consoleauth_rpcapi.authorize_console(context,
                connect_info['token'], console_type,
                connect_info['host'], connect_info['port'],
                connect_info['internal_access_path'], instance.uuid,
                access_url=connect_info['access_url'])

        return {'url': connect_info['access_url']}

f. /nova/consoleauth/rpcapi.py/ConsoleAuthAPI/authorize_console()函数,该函数会发送RPC调用给nova-consoleauth服务,该服务继续调用/nova/consoleauth/manager.py/ConsoleAuthManager/ authorize_console()函数处理请求,具体是nova-consoleauth会将instance –> token, token –> connect_info的信息cache起来。

    def authorize_console(self, context, token, console_type, host, port,
                          internal_access_path, instance_uuid,
                          access_url=None):

        token_dict = {'token': token,
                      'instance_uuid': instance_uuid,
                      'console_type': console_type,
                      'host': host,
                      'port': port,
                      'internal_access_path': internal_access_path,
                      'access_url': access_url,
                      'last_activity_at': time.time()}
        data = jsonutils.dumps(token_dict)

        self.mc.set(token.encode('UTF-8'), data)
        tokens = self._get_tokens_for_instance(instance_uuid)

        # Remove the expired tokens from cache.
        token_values = self.mc.get_multi(
            [tok.encode('UTF-8') for tok in tokens])
        tokens = [name for name, value in zip(tokens, token_values)
                  if value is not None]
        tokens.append(token)

        self.mc_instance.set(instance_uuid.encode('UTF-8'),
                             jsonutils.dumps(tokens))

        LOG.info("Received Token: %(token)s, %(token_dict)s",
                 {'token': token, 'token_dict': token_dict})

g. nova-api会将的access_url返回给浏览器,如:http://192.168.174.10:6082/spice_auto.html?token=2842a8d2-704e-4f00-967b-c4812ea68de5&title=vxlan_instance1(716e9189-fad1-4081-9bc9-aba5dd8da272),浏览器向nova-novncproxy发送这个url,然后该服务会调用/nova/console/websocketproxy.py/TenantSocket/new_websocket_client()函数,具体该函数的实现如下讲解:

h. nova-novncproxy服务发起RPC调用给nova-consoleauth服务,nova-consoleauth服务调用check_token函数,nova-consoleauth服务验证了这个token,将这个instance对应的connect_info返回给nova-novncproxy,最后nova-novncproxy通过connect_info中的host, port等信息,连接compute节点上的VNC Server,从而开始了proxy的工作。文章来源地址https://www.toymoban.com/news/detail-841844.html

    def new_websocket_client(self):
        """Called after a new WebSocket connection has been established."""
        # Reopen the eventlet hub to make sure we don't share an epoll
        # fd with parent and/or siblings, which would be bad
        from eventlet import hubs
        hubs.use_hub()

        # The nova expected behavior is to have token
        # passed to the method GET of the request
        parse = urlparse.urlparse(self.path)
        if parse.scheme not in ('http', 'https'):
            # From a bug in urlparse in Python < 2.7.4 we cannot support
            # special schemes (cf: http://bugs.python.org/issue9374)
            if sys.version_info < (2, 7, 4):
                raise exception.NovaException(
                    _("We do not support scheme '%s' under Python < 2.7.4, "
                      "please use http or https") % parse.scheme)

        query = parse.query
        token = urlparse.parse_qs(query).get("token", [""]).pop()
        if not token:
            # NoVNC uses it's own convention that forward token
            # from the request to a cookie header, we should check
            # also for this behavior
            hcookie = self.headers.get('cookie')
            if hcookie:
                cookie = Cookie.SimpleCookie()
                for hcookie_part in hcookie.split(';'):
                    hcookie_part = hcookie_part.lstrip()
                    try:
                        cookie.load(hcookie_part)
                    except Cookie.CookieError:
                        # NOTE(stgleb): Do not print out cookie content
                        # for security reasons.
                        LOG.warning('Found malformed cookie')
                    else:
                        if 'token' in cookie:
                            token = cookie['token'].value

        ctxt = context.get_admin_context()
        rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
        connect_info = rpcapi.check_token(ctxt, token=token)

        if not connect_info:
            raise exception.InvalidToken(token=token)

        # Verify Origin
        expected_origin_hostname = self.headers.get('Host')
        if ':' in expected_origin_hostname:
            e = expected_origin_hostname
            if '[' in e and ']' in e:
                expected_origin_hostname = e.split(']')[0][1:]
            else:
                expected_origin_hostname = e.split(':')[0]
        expected_origin_hostnames = CONF.console.allowed_origins
        expected_origin_hostnames.append(expected_origin_hostname)
        origin_url = self.headers.get('Origin')
        # missing origin header indicates non-browser client which is OK
        if origin_url is not None:
            origin = urlparse.urlparse(origin_url)
            origin_hostname = origin.hostname
            origin_scheme = origin.scheme
            if origin_hostname == '' or origin_scheme == '':
                detail = _("Origin header not valid.")
                raise exception.ValidationError(detail=detail)
            if origin_hostname not in expected_origin_hostnames:
                detail = _("Origin header does not match this host.")
                raise exception.ValidationError(detail=detail)
            if not self.verify_origin_proto(connect_info, origin_scheme):
                detail = _("Origin header protocol does not match this host.")
                raise exception.ValidationError(detail=detail)

        self.msg(_('connect info: %s'), str(connect_info))
        host = connect_info['host']
        port = int(connect_info['port'])

        # Connect to the target
        self.msg(_("connecting to: %(host)s:%(port)s") % {'host': host,
                                                          'port': port})
        tsock = self.socket(host, port, connect=True)

        # Handshake as necessary
        if connect_info.get('internal_access_path'):
            tsock.send(encodeutils.safe_encode(
                "CONNECT %s HTTP/1.1\r\n\r\n" %
                connect_info['internal_access_path']))
            end_token = "\r\n\r\n"
            while True:
                data = tsock.recv(4096, socket.MSG_PEEK)
                token_loc = data.find(end_token)
                if token_loc != -1:
                    if data.split("\r\n")[0].find("200") == -1:
                        raise exception.InvalidConnectionInfo()
                    # remove the response from recv buffer
                    tsock.recv(token_loc + len(end_token))
                    break

        if self.server.security_proxy is not None:
            tenant_sock = TenantSock(self)

            try:
                tsock = self.server.security_proxy.connect(tenant_sock, tsock)
            except exception.SecurityProxyNegotiationFailed:
                LOG.exception("Unable to perform security proxying, shutting "
                              "down connection")
                tenant_sock.close()
                tsock.shutdown(socket.SHUT_RDWR)
                tsock.close()
                raise

            tenant_sock.finish_up()

        # Start proxying
        try:
            self.do_proxy(tsock)
        except Exception:
            if tsock:
                tsock.shutdown(socket.SHUT_RDWR)
                tsock.close()
                self.vmsg(_("%(host)s:%(port)s: "
                          "Websocket client or target closed") %
                          {'host': host, 'port': port})
            raise

到了这里,关于openstack 之 novnc流程分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 云计算|OpenStack|社区版OpenStack安装部署文档(二---OpenStack运行环境搭建)

    一个良好的运行环境对于任何一个部署工作来说都是必须的,俗话说 万事开头难,其实很多部署工作失败的原因在于初始环境没有正确的配置,因此,按照官网的部署文档并结合自己的实际情况,配置一个合理的OpenStack运行环境是十分有必要的。 OpenStack的运行环境配置文档

    2023年04月08日
    浏览(47)
  • 云计算|OpenStack|社区版OpenStack安装部署文档(五 --- 计算服务nova安装部署---Rocky版)

    nova服务是openstack最重要的一个组件,没有之一,该组件是云计算的计算核心,大体组件如下: OpenStack Docs: Compute service overview 挑些重点,nova-api,libvirt,nova-placement-api,nova-api-metadata,nova-compute 并且nova安装部署是分为controller节点和computer节点了,controller节点就一个,comput

    2024年02月02日
    浏览(47)
  • 【海量数据挖掘/数据分析】 之 贝叶斯分类算法(朴素贝叶斯分类、贝叶斯分类计算流程、拉普拉斯修正、贝叶斯分类实例计算)

    目录 【海量数据挖掘/数据分析】 之 贝叶斯分类算法(朴素贝叶斯分类、贝叶斯分类计算流程、拉普拉斯修正、贝叶斯分类实例计算) 一、 贝叶斯分类器 1 . 贝叶斯分类器 : 2 . 贝叶斯分类器的类型 : 3 . 正向概率 与 逆向概率 : 4 . 贝叶斯公式 : 有两个事件 , 事件  A , 和事件

    2024年02月12日
    浏览(46)
  • 云计算|OpenStack|社区版OpenStack安装部署文档(十二--- openstack的网络模型解析---Rocky版)

    https://zskjohn.blog.csdn.net/article/details/128846360   云计算|OpenStack|社区版OpenStack安装部署文档(六 --- 网络服务neutron的安装部署---Rocky版) (######注:以上文章使用的是openstack的provider网络,也称之为提供者网络,对此网络的扩展selfservice,称之为自服务网络,这个文章没有涉及)

    2024年02月06日
    浏览(40)
  • openstack的port重建流程【neutron port-show 中的ip信息】

    为什么要重建port,是因为有一台虚拟机迁移失败回退后依然无法启动,原因是因为不能重建网络,所以尝试重建port解决这个问题。 而重建port信息后,问题解决,虚拟机可以正常启动了。 感兴趣的可以看一下 过程看下面所有命令吧 不复杂,直接放看应该能看懂。

    2024年02月07日
    浏览(42)
  • 云计算架构解析:云计算框架之OpenStack

    作者:禅与计算机程序设计艺术 什么是云计算?云计算就是通过网络将各种计算资源、存储资源、应用服务、IT基础设施等互联在一起形成的一种新型的计算模式。其最大的特征就是按需提供计算服务,因此用户不需要购买高配置的服务器、存储设备或数据中心,也无需为长

    2024年02月07日
    浏览(42)
  • openstack nova 源码分析

    从 github 下载 Victoria 版本的 Nova 源码 nova/ 文件夹下的目录 最新版的源码如下: nova/ 文件夹下的 python 文件: setup.cfg 配置文件,[entry_points] 小节指定了 nova 各个组件入口 2. nova-api nova-api 对外提供 RESTful API,没有对内的 RPC 。 nova/api/ 目录结构 openstack 目录中包含 WSGI 基础架构的

    2024年02月08日
    浏览(43)
  • 云计算OpenStack详解

    1、管理租户、用户和角色 租户(project)是OpenStack项目中的一个组织单元,用户(user)可以同时属于一个或多个租户,角色(role)定义了用户可以执行的操作类型。作为具有权限的管理员,可以管理所有的租户、用户和角色。 OpenStack 项目中的各个服务组件可以执行的操作可

    2024年02月02日
    浏览(32)
  • [云计算]OpenStack - Neutron

    可以为虚拟机或者 OpenStack 组件提供网络服务,(由 Nova-network 独立出来的)随着需承载业务的能力的增长作为 Nova 子组件无法满足。 过程:neutron-server 接收网络请求,会将其记录到 DB 中,调用不同的Agent,Agent 通过不同的驱动实现网络服务。 网络结构 功能 Management Network 提

    2024年02月15日
    浏览(45)
  • 云计算之OpenStack基础

    计算节点:虚拟机实例的网络: 1)下图中A就是虚拟机VM1的虚拟网卡,和它相连的B是一个tap设备,通常是以tap开头的一段名称,它挂载在Linux Bridge qbr上面。 tap 设备其实就是一个Linux内核虚拟化出来的一个网络接口,即虚拟网卡; 计算节点:集成网桥(br-int)的网络: 1)集成

    2024年02月06日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包