Nova在线单网卡限速demo

Posted by 爱折腾的工程师 on Saturday, August 19, 2017

背景

OpenStack Nova限制虚拟机网速资源是通过设置Nova Flavor(云主机类型)的元数据来实现的,Nova在创建虚拟机的时候根据Flavor所设置的元数据生成对应的资源限制字段libvirt xml文件,最终作用在kvm虚拟机上。如果虚拟机运行期间管理员想针对虚拟机资源做限制,目前OpenStack只能通过Nova resize进行Flavor的更改,但是resize(实际上就是冷迁移)过程中会重启虚拟机,如果不是线上业务,都好办。

Flavor资源限制元数据设置参考:https://wiki.openstack.org/wiki/InstanceResourceQuota

libvirt xml format参考: https://libvirt.org/formatdomain.html

Nova可限制的资源有:

环境

CentOS 7.2 (OpenStack Ocata)单节点

具体实现

本次demo以在线限制虚拟机的某块网卡为例, 仅供参考。 添加server_set_interface api,得益于stevedore库插件式api特点

vim /usr/lib/python2.7/site-packages/nova-15.0.3-py2.7.egg-info/entry_points.txt
server_password = nova.api.openstack.compute.server_password:ServerPassword
server_tags = nova.api.openstack.compute.server_tags:ServerTags
server_usage = nova.api.openstack.compute.server_usage:ServerUsage
server_set_interface = nova.api.openstack.compute.server_set_interface:ServerSetInterface # 此处添加
servers = nova.api.openstack.compute.servers:Servers
services = nova.api.openstack.compute.services:Services
shelve = nova.api.openstack.compute.shelve:Shelve

server_set_interface api入口代码,继承extensions扩展nova api

[root@test-aio-2 nova(keystone_admin)]# cat /usr/lib/python2.7/site-packages/nova/api/openstack/compute/server_set_interface.py

"""Server set interface management extension."""

from nova.api.openstack import common
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova import compute

ALIAS = 'os-server-interface'

class Controller(wsgi.Controller):

    def __init__(self, *args, **kwargs):

        super(Controller, self).__init__(*args, **kwargs)

        self.compute_api = compute.API()


    @wsgi.action('setInterface')

    @wsgi.response(202)

    @extensions.expected_errors((400, 404, 409, 501))

    def set_interface(self, req, id, body):

        context = req.environ['nova.context']

        interface = body['setInterface']['interface']

        rate = body['setInterface']['rate']

        instance = common.get_instance(self.compute_api, context, id)

        info = self.compute_api.set_interface(context, instance, interface, rate)

    return info


class ServerSetInterface(extensions.V21APIExtensionBase):

    """Server set interface support."""


    name = "ServerSetInterface"

    alias = ALIAS

    version = 1


    def get_controller_extensions(self):

        controller = Controller()

        extension = extensions.ControllerExtension(self, 'servers', controller)

        return [extension]

    def get_resources(self):

        return []

self.compute_api.set_interface封装

[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/compute/api.py

@check_instance_lock

@check_instance_cell

@check_instance_state(vm_state=[vm_states.ACTIVE])

def set_interface(self, context, instance, interface=None, rate=None):



    self._record_action_start(context, instance,

                              instance_actions.SET_INTERFACE)


    info = self.compute_rpcapi.set_interface(context,

                                             instance=instance,

                                             interface=interface,

                                             rate=rate)

    return info

self.compute_rpcapi.set_interface rpc客户端调用接口封装

[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/compute/rpcapi.py

def set_interface(self, ctxt, instance, interface, rate):

    version = '4.0'

    cctxt = self.router.by_instance(ctxt, instance).prepare(

            server=_compute_host(None, instance), version=version)

    return cctxt.call(ctxt, 'set_interface',

                      instance=instance, interface=interface,

                      rate=rate)

rpc server接口封装

[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/compute/manager.py

@wrap_exception()

@reverts_task_state

@wrap_instance_event(prefix='compute')

@wrap_instance_fault

def set_interface(self, context, instance, interface, rate):

    try:

        info = self.driver.set_interface(instance, interface, rate)

        LOG.info(_LI("Interface set"), instance=instance)

    except NotImplementedError:

        LOG.warning(_LW('set_interface is not implemented '

                        'by this driver or guest instance.'),

                    instance=instance)

    return info

self.driver.set_interface接口封装

[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/virt/libvirt/driver.py

def set_interface(self, instance, interface, rate):

    guest = self._host.get_guest(instance)

    try:

        xml = guest.get_xml_desc()

        tree = etree.fromstring(xml)

        ifaces = tree.findall('devices/interface/target')

        i = ifaces[int(interface)-1].get('dev')

        params = {'outbound.peak': rate,

                  'inbound.peak': rate,

                  'inbound.burst': rate,

                  'inbound.average': rate,

                  'outbound.average': rate,

                  'outbound.burst': rate}

        guest.set_interface(i, params)

    except libvirt.libvirtError as ex:

        error_code = ex.get_error_code()

        msg = (_('Error from libvirt while set interface '

                 '"%(interface)s": [Error Code %(error_code)s] %(ex)s')

               % {'interface': interface, 'error_code': error_code, 'ex': ex})

        raise exception.InternalError(msg)

    return guest.get_interface(i)

guest.set_interface和guest.get_interface接口

[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/virt/libvirt/guest.py

def get_interface(self, interface):

    """Get interface rate."""

    return self._domain.interfaceParameters(interface, 0)


def set_interface(self, interface, params):

    """Configures interface rate."""

    self._domain.setInterfaceParameters(interface, params, 0)

测试

重启openstack-nova-api、openstack-nova-compute服务,在/var/log/nova/nova-api.log日志中看到o s-server-interface api extention已经加载成功了。

[root@test-aio-2 ~(keystone_admin)]# less /var/log/nova/nova-api.log  | grep os-server-interface
  2017-07-12 08:34:14.363 31376 INFO nova.api.openstack [req-015fb839-5a37-4630-8655-aa1967ffbcea - - - - -] Loaded extensions: ['extensions', 'flavors', 'image-metadata', 'image-size', 'images', 'ips', 'limits', 'os-admin-actions', 'os-admin-password', 'os-agents', 'os-aggregates', 'os-assisted-volume-snapshots', 'os-attach-interfaces', 'os-availability-zone', 'os-baremetal-nodes', 'os-block-device-mapping', 'os-cells', 'os-certificates', 'os-cloudpipe', 'os-config-drive', 'os-console-auth-tokens', 'os-console-output', 'os-consoles', 'os-create-backup', 'os-deferred-delete', 'os-evacuate', 'os-extended-availability-zone', 'os-extended-server-attributes', 'os-extended-status', 'os-extended-volumes', 'os-fixed-ips', 'os-flavor-access', 'os-flavor-extra-specs', 'os-flavor-manage', 'os-flavor-rxtx', 'os-floating-ip-dns', 'os-floating-ip-pools', 'os-floating-ips', 'os-floating-ips-bulk', 'os-fping', 'os-hide-server-addresses', 'os-hosts', 'os-hypervisors', 'os-instance-actions', 'os-instance-usage-audit-log', 'os-keypairs', 'os-lock-server', 'os-migrate-server', 'os-migrations', 'os-multinic', 'os-multiple-create', 'os-networks', 'os-networks-associate', 'os-pause-server', 'os-quota-class-sets', 'os-quota-sets', 'os-remote-consoles', 'os-rescue', 'os-scheduler-hints', 'os-security-group-default-rules', 'os-security-groups', 'os-server-diagnostics', 'os-server-external-events', 'os-server-groups', 'os-server-interface', 'os-server-password', 'os-server-tags', 'os-server-usage', 'os-services', 'os-shelve', 'os-simple-tenant-usage', 'os-suspend-server', 'os-tenant-networks', 'os-used-limits', 'os-user-data', 'os-virtual-interfaces', 'os-volumes', 'server-metadata', 'server-migrations', 'servers', 'versions']
  2017-07-12 08:34:14.847 31376 INFO nova.api.openstack [req-015fb839-5a37-4630-8655-aa1967ffbcea - - - - -] Loaded extensions: ['extensions', 'flavors', 'image-metadata', 'image-size', 'images', 'ips', 'limits', 'os-admin-actions', 'os-admin-password', 'os-agents', 'os-aggregates', 'os-assisted-volume-snapshots', 'os-attach-interfaces', 'os-availability-zone', 'os-baremetal-nodes', 'os-block-device-mapping', 'os-cells', 'os-certificates', 'os-cloudpipe', 'os-config-drive', 'os-console-auth-tokens', 'os-console-output', 'os-consoles', 'os-create-backup', 'os-deferred-delete', 'os-evacuate', 'os-extended-availability-zone', 'os-extended-server-attributes', 'os-extended-status', 'os-extended-volumes', 'os-fixed-ips', 'os-flavor-access', 'os-flavor-extra-specs', 'os-flavor-manage', 'os-flavor-rxtx', 'os-floating-ip-dns', 'os-floating-ip-pools', 'os-floating-ips', 'os-floating-ips-bulk', 'os-fping', 'os-hide-server-addresses', 'os-hosts', 'os-hypervisors', 'os-instance-actions', 'os-instance-usage-audit-log', 'os-keypairs', 'os-lock-server', 'os-migrate-server', 'os-migrations', 'os-multinic', 'os-multiple-create', 'os-networks', 'os-networks-associate', 'os-pause-server', 'os-quota-class-sets', 'os-quota-sets', 'os-remote-consoles', 'os-rescue', 'os-scheduler-hints', 'os-security-group-default-rules', 'os-security-groups', 'os-server-diagnostics', 'os-server-external-events', 'os-server-groups', 'os-server-interface', 'os-server-password', 'os-server-tags', 'os-server-usage', 'os-services', 'os-shelve', 'os-simple-tenant-usage', 'os-suspend-server', 'os-tenant-networks', 'os-used-limits', 'os-user-data', 'os-virtual-interfaces', 'os-volumes', 'server-metadata', 'server-migrations', 'servers', 'versions']

使用curl测试api

#interface: 网卡序号
#rate: 速率,单位字节
#获取token,可以使用openstack token
curl -g -i -X POST \ http://172.16.234.41:8774/v2.1/674bbd3aad0942f593487027e8df14c2/servers/0ced1991-f5dd-4906-b1a7-82997bb74363/action \
             -H "X-Auth-Token: 3e62cd28bfb5463b941d35b274372df6" \
             -H "Content-Type: application/json" \
             -d '{"setInterface": {"interface": 1, "rate": 10240}}'

查看虚拟机libvirt xml文件,如果有bandwidth字段,说明已经生效

[root@test-aio-2 ~(keystone_admin)]# virsh dumpxml 12 | grep -A 10 bandwidth

TODO

  • nova policy集成
  • api body schema校验
  • 虚拟机硬重启后限速依然生效
  • nova cell集成

参考链接

「真诚赞赏,手留余香」

爱折腾的工程师

真诚赞赏,手留余香

使用微信扫描二维码完成支付