费解的service_available.neutron配置以及传递network参数

目前主要还是基于H版的OpenStack,因此Tempest也是H版,对比了下最新社区Tempest,已经有很多实现的改进,连最基础的create_server的方法也优化了,特别是传入的networks,还会先通过方法tenant_network来获取,而目前使用的H版的Tempest默认依旧无网卡创建,除了重写方法外,如果不做任何修改,很多用例都没办法直接通过,加上在用的OpenStack也做了相当多定制化的开发,很多地方已经面目全非,都需要完善

首先看一个testcase

lihui@MacBook  ~/work/cloud/openstack/tempest-ci/tempest/api/compute/admin   master ●✚  nosetests -sv test_fixed_ips.py
ERROR

======================================================================
ERROR: test suite for <class 'tempest.api.compute.admin.test_fixed_ips.fixedipstestjson'="">
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/nose-1.3.7-py2.7.egg/nose/suite.py", line 209, in run
    self.setUp()
  File "/usr/local/lib/python2.7/site-packages/nose-1.3.7-py2.7.egg/nose/suite.py", line 292, in setUp
    self.setupContext(ancestor)
  File "/usr/local/lib/python2.7/site-packages/nose-1.3.7-py2.7.egg/nose/suite.py", line 315, in setupContext
    try_run(context, names)
  File "/usr/local/lib/python2.7/site-packages/nose-1.3.7-py2.7.egg/nose/util.py", line 471, in try_run
    return func()
  File "/Users/lihui/work/cloud/openstack/tempest-ci/tempest/api/compute/admin/test_fixed_ips.py", line 29, in setUpClass
    raise cls.skipException(msg)
SkipTest: FixedIPsTestJson skipped as neutron is available

----------------------------------------------------------------------
Ran 0 tests in 0.011s

SetUp的时候直接就出错了,报错的地方在

class FixedIPsTestJson(base.BaseV2ComputeAdminTest):
    _interface = 'json'

    @classmethod
    def setUpClass(cls):
        if cls.config.service_available.neutron:
            msg = ("%s skipped as neutron is available" % cls.__name__)
            raise cls.skipException(msg)

也就是令人费解的if cls.config.service_available.neutron:返回的为真,也就是配置文件里配置neutron可用,查看配置文件

[service_available]
# Whether or not cinder is expected to be available
cinder = True
# Whether or not neutron is expected to be available
neutron = True
# Whether or not glance is expected to be available
glance = True
# Whether or not swift is expected to be available
swift = False
# Whether or not nova is expected to be available
nova = True
# Whether or not Heat is expected to be available
heat = False
# Whether or not Ceilometer is expected to be available
ceilometer = False

的确neutron配置的是可用,一脸懵逼,neutron使能和测试fixed ip有什么关系呢,直接看整个python文件

from tempest.api.compute import base
from tempest.test import attr


class FixedIPsTestJson(base.BaseV2ComputeAdminTest):
    _interface = 'json'

    @classmethod
    def setUpClass(cls):
        if cls.config.service_available.neutron:
            msg = ("%s skipped as neutron is available" % cls.__name__)
            raise cls.skipException(msg)

        super(FixedIPsTestJson, cls).setUpClass()

        cls.client = cls.os_adm.fixed_ips_client
        resp, server = cls.create_test_server(wait_until='ACTIVE')
        resp, server = cls.servers_client.get_server(server['id'])
        for ip_set in server['addresses']:
            for ip in server['addresses'][ip_set]:
                if ip['OS-EXT-IPS:type'] == 'fixed':
                    cls.ip = ip['addr']
                    break
            if cls.ip:
                break

    @attr(type='gate')
    def test_list_fixed_ip_details(self):
        resp, fixed_ip = self.client.get_fixed_ip_details(self.ip)
        self.assertEqual(fixed_ip['address'], self.ip)

    @attr(type='gate')
    def test_set_reserve(self):
        body = {"reserve": "None"}
        resp, body = self.client.reserve_fixed_ip(self.ip, body)
        self.assertEqual(resp.status, 202)

    @attr(type='gate')
    def test_set_unreserve(self):
        body = {"unreserve": "None"}
        resp, body = self.client.reserve_fixed_ip(self.ip, body)
        self.assertEqual(resp.status, 202)

除了setUp的时候,创建了一个VM,然后获取IP,其它三个testcase全部都是将这个IP传入,来验证接口正确性,看到这里,又看到了上次被坑的不带网络创建虚拟机的方法create_test_server,再看看下面三个test case,分别调用的方法基本也是计算服务

class FixedIPsClientJSON(RestClient):

    def __init__(self, config, username, password, auth_url, tenant_name=None):
        super(FixedIPsClientJSON, self).__init__(config, username, password,
                                                 auth_url, tenant_name)
        self.service = self.config.compute.catalog_type

    def get_fixed_ip_details(self, fixed_ip):
        url = "os-fixed-ips/%s" % (fixed_ip)
        resp, body = self.get(url)
        body = json.loads(body)
        return resp, body['fixed_ip']

    def reserve_fixed_ip(self, ip, body):
        """This reserves and unreserves fixed ips."""
        url = "os-fixed-ips/%s/action" % (ip)
        resp, body = self.post(url, json.dumps(body), self.headers)
        return resp, body

上次HTTP RESTful那篇有写,URL前面的是根据__init__里的self.service来定,而这里是从self.config.compute服务配置传过来的,也就是nova服务,和neutron也没半毛钱关系,完全不清楚这个test case要disable掉neutron作用是什么,有可能在后面哪里会用到,具体以后再看

这里眼下问题是解决case block,但是要认清这里不仅仅是neutron配置一个问题,还有个创建VM后返回fix ip,而本身create_test_server是不带网络创建的,特别由于我不太清楚tempest其它地方会不会需要不带网络,因为毕竟它这样设计,因此也不好直接将这个方法强行添加一个net-id来创建

但是最新社区这个方法是这么写的

    @classmethod
    def create_test_server(cls, validatable=False, volume_backed=False,
                           **kwargs):
        """Wrapper utility that returns a test server.

        This wrapper utility calls the common create test server and
        returns a test server. The purpose of this wrapper is to minimize
        the impact on the code of the tests already using this
        function.

        :param validatable: Whether the server will be pingable or sshable.
        :param volume_backed: Whether the instance is volume backed or not.
        """
        if 'name' not in kwargs:
            kwargs['name'] = data_utils.rand_name(cls.__name__ + "-server")
        tenant_network = cls.get_tenant_network()
        body, servers = compute.create_test_server(
            cls.os,
            validatable,
            validation_resources=cls.validation_resources,
            tenant_network=tenant_network,
            volume_backed=volume_backed,
            **kwargs)

        cls.servers.extend(servers)

        return body

可以看到这里传入了一个tenant_network,是通过cls.get_tenant_network()获取,猜测应该会根据具体租户网络情况,做测试的总喜欢穷追问底,继续看

    @classmethod
    def get_tenant_network(cls, credentials_type='primary'):
        """Get the network to be used in testing

        :param credentials_type: The type of credentials for which to get the
                                 tenant network

        :return: network dict including 'id' and 'name'
        """
        # Get a manager for the given credentials_type, but at least
        # always fall back on getting the manager for primary credentials
        if isinstance(credentials_type, six.string_types):
            manager = cls.get_client_manager(credential_type=credentials_type)
        elif isinstance(credentials_type, list):
            manager = cls.get_client_manager(roles=credentials_type[1:])
        else:
            manager = cls.get_client_manager()

        # Make sure cred_provider exists and get a network client
        networks_client = manager.compute_networks_client
        cred_provider = cls._get_credentials_provider()
        # In case of nova network, isolated tenants are not able to list the
        # network configured in fixed_network_name, even if they can use it
        # for their servers, so using an admin network client to validate
        # the network name
        if (not CONF.service_available.neutron and
                credentials.is_admin_available(
                    identity_version=cls.get_identity_version())):
            admin_creds = cred_provider.get_admin_creds()
            admin_manager = clients.Manager(admin_creds.credentials)
            networks_client = admin_manager.compute_networks_client
        return fixed_network.get_tenant_network(
            cred_provider, networks_client, CONF.compute.fixed_network_name)

看到这里,半脸懵逼,但是中间的注释深深地吸引了我,我已经不关心这个方法的实现了,而是nova network一下子让我有了无心插柳的感觉,我目前所有case全部都是compute服务接口,根本就没有neutron接口,那么上面service_available.neutron设置为disabled的原因,大概就是想走nova-network这个模式,而猜测如果不disabled的话,可能先走的neutron,而无法验证nova这个compute服务正确性,先确认一下

lihui@MacBook  ~  nova help | grep network
    add-fixed-ip        Add new IP address on a network to server.
    interface-attach    Attach a network interface to a server.
    interface-detach    Detach a network interface from a server.
    network-associate-host
                        Associate host with network.
    network-associate-project
                        Associate project with network.
    network-create      Create a network.
    network-disassociate
                        network.
    network-list        Print a list of available networks.
    network-show        Show details about the given network.
    reset-network       Reset network of a server.
                        Add a network interface to a baremetal node.
                        List network interfaces associated with a baremetal
                        Remove a network interface from a baremetal node.
    net                 Show a network
    net-create          Create a network
    net-delete          Delete a network
    net-list            List networks
 lihui@MacBook  ~ 
 lihui@MacBook  ~  nova net-list
+--------------------------------------+------------------------------------------+------+
| ID                                   | Label                                    | CIDR |
+--------------------------------------+------------------------------------------+------+
| 1e0801ae-7ee4-4fdb-bea6-80c14d2ea202 | public_admin_1                           | -    |
| 3c645e48-43b9-46ac-bd24-3c841e09c31d | public_admin                             | -    |
| ea69e216-ba00-47a7-8cb9-0d9c02c0e34d | private_66daf0ccaf3a41d383c24f4080c41f71 | -    |
+--------------------------------------+------------------------------------------+------+
 lihui@MacBook  ~ 
 lihui@MacBook  ~  neutron net-list
+--------------------------------------+------------------------------------------+-------------------------------------------------------+
| id                                   | name                                     | subnets                                               |
+--------------------------------------+------------------------------------------+-------------------------------------------------------+
| 1e0801ae-7ee4-4fdb-bea6-80c14d2ea202 | public_admin_1                           | 59f2086d-2394-46b2-a1ed-1f8282f6d3e9 60.191.82.0/24   |
| 3c645e48-43b9-46ac-bd24-3c841e09c31d | public_admin                             | f92163ef-86e1-40ac-a999-4bb27084a815 115.236.127.0/24 |
| ea69e216-ba00-47a7-8cb9-0d9c02c0e34d | private_66daf0ccaf3a41d383c24f4080c41f71 | 816f12d2-be98-4569-8ddd-b680cb1f6e50 10.180.80.0/23   |
+--------------------------------------+------------------------------------------+-------------------------------------------------------+

果然,nova-network和neutron就接口而言,是完全一致的,最早的OpenStack是全部集成在nova服务当中,包括网络,neutron也是后来才分开的,如此一来就说得通了

可是,这还是无济于事,因为目前H版Tempest创建VM这个方法network就是为空,为了不破坏当前的结构,我这里加一个条件fix这个case

先捋一下整体思路,现在的问题是create_server里,创建的VM不带网络,但是实现里有这么一段

            for option in ['personality', 'adminPass', 'key_name',
                       #'security_groups',
                       'networks', 'user_data',
                       'availability_zone', 'accessIPv4', 'accessIPv6',
                       'min_count', 'max_count', ('metadata', 'meta'),
                       ('OS-DCF:diskConfig', 'disk_config'),
                       'return_reservation_id']:
            if isinstance(option, tuple):
                post_param = option[0]
                key = option[1]
            else:
                post_param = option
                key = option
            value = kwargs.get(key)
            if value is not None:
                post_body[post_param] = value

看到option是可以遍历到networks的,只不过我们传入的networks的value为空,导致不会存入post_body里,因此我们的目标就是将network uuid穿进去就行了,也就是传到kwargs里,回头看下调用的地方

    @classmethod
    def create_test_server(cls, **kwargs):
        """Wrapper utility that returns a test server."""
        name = data_utils.rand_name(cls.__name__ + "-instance")
        if 'name' in kwargs:
            name = kwargs.pop('name')

        flavor = kwargs.get('flavor', cls.flavor_ref)
        image_id = kwargs.get('image_id', cls.image_ref)

        resp, body = cls.servers_client.create_server(
            name, image_id, flavor, **kwargs)

kwargs没做删减,只有pop,因此,继续看create_test_server调用的地方,也就是最开始

resp, server = cls.create_test_server(wait_until='ACTIVE')

这就太简单了,直接再添加一个networks=xxxx的参数进去,就行了,而这个参数值最好是写在配置文件当中,也就是tempest.conf里,由于这个版本还没有tenant_network方法,目前就直接配置进去,这样,整体思路就清晰了

tempest.conf => private_network_id => kwargs => create_test_server => kwargs => create_server

首先将neutron disabled

[service_available]
# Whether or not cinder is expected to be available
cinder = True
# Whether or not neutron is expected to be available
neutron = False
# Whether or not glance is expected to be available
glance = True
# Whether or not swift is expected to be available
swift = False
# Whether or not nova is expected to be available
nova = True
# Whether or not Heat is expected to be available

然后配置private_network_id,注意要写在[network]里

# Id of the private network that provides external connectivity.
private_network_id = 64916627-9d23-4b37-adcf-8cc0f407260b

此时并没有完,还没发读取,具体涉及到oslo_config的实现,下次有空研究下,刚刚就少修改了下面这处地方,在NetworkGroup里面

        cfg.StrOpt('private_network_id',
               default="",
               help="Id of the private network that provides external "
                    "connectivity"),

base.py里添加成员变量

class BaseComputeTest(tempest.test.BaseTestCase):
    """Base test case class for all Compute API tests."""

    force_tenant_isolation = False

    @classmethod
    def setUpClass(cls):
        super(BaseComputeTest, cls).setUpClass()
        if not cls.config.service_available.nova:
            skip_msg = ("%s skipped as nova is not available" % cls.__name__)
            raise cls.skipException(skip_msg)

        os = cls.get_client_manager()

        cls.os = os
        cls.build_interval = cls.config.compute.build_interval
        cls.build_timeout = cls.config.compute.build_timeout
        cls.ssh_user = cls.config.compute.ssh_user
        cls.image_ssh_user = cls.config.compute.image_ssh_user
        cls.image_ssh_password = cls.config.compute.image_ssh_password
        cls.image_alt_ssh_user = cls.config.compute.image_alt_ssh_user
        cls.image_alt_ssh_password = cls.config.compute.image_alt_ssh_password
        cls.image_ref = cls.config.compute.image_ref
        cls.image_ref_alt = cls.config.compute.image_ref_alt
        cls.flavor_ref = cls.config.compute.flavor_ref
        cls.private_network_id = cls.config.network.private_network_id
        cls.flavor_ref_alt = cls.config.compute.flavor_ref_alt
        cls.servers = []
        cls.images = []
        cls.multi_user = cls.get_multi_user()

做了这么多工作,只是为了让上面这个类派生出来的类能够直接获取成员变量

最后在我们的test case里创建VM的时候,添加一个network id参数

class FixedIPsTestJson(base.BaseV2ComputeAdminTest):
    _interface = 'json'

    @classmethod
    def setUpClass(cls):
        if cls.config.service_available.neutron:
            msg = ("%s skipped as neutron is available" % cls.__name__)
            raise cls.skipException(msg)

        super(FixedIPsTestJson, cls).setUpClass()

        cls.client = cls.os_adm.fixed_ips_client
        resp, server = cls.create_test_server(networks=[{'uuid': cls.private_network_id}], wait_until='ACTIVE')
        resp, server = cls.servers_client.get_server(server['id'])
        for ip_set in server['addresses']:
            for ip in server['addresses'][ip_set]:
                if ip['OS-EXT-IPS:type'] == 'fixed':
                    cls.ip = ip['addr']
                    break
            if cls.ip:
                break

通过这几步,就成功将network id传进去了,而且没有修改已有的创建VM的方法

执行以下test case,成功带网络创建,但是接口还是失败,返回的是404

nosetests -sv test_fixed_ips.py:FixedIPsTestJson.test_list_fixed_ip_details
tempest.api.compute.admin.test_fixed_ips.FixedIPsTestJson.test_list_fixed_ip_details ... FAIL

======================================================================
FAIL: tempest.api.compute.admin.test_fixed_ips.FixedIPsTestJson.test_list_fixed_ip_details
----------------------------------------------------------------------

 TRACE日志找不到

Traceback (most recent call last):
  File "/Users/lihui/work/cloud/openstack/tempest-ci/tempest/api/compute/admin/test_fixed_ips.py", line 45, in test_list_fixed_ip_details
    resp, fixed_ip = self.client.get_fixed_ip_details(self.ip)
  File "/Users/lihui/work/cloud/openstack/tempest-ci/tempest/services/compute/json/fixed_ips_client.py", line 32, in get_fixed_ip_details
    resp, body = self.get(url)
  File "/Users/lihui/work/cloud/openstack/tempest-ci/tempest/common/rest_client.py", line 320, in get
    return self.request('GET', url, headers)
  File "/Users/lihui/work/cloud/openstack/tempest-ci/tempest/common/rest_client.py", line 451, in request
    resp, resp_body)
  File "/Users/lihui/work/cloud/openstack/tempest-ci/tempest/common/rest_client.py", line 496, in _error_checker
    raise exceptions.NotFound(resp_body)
tempest.exceptions.NotFound: Object not found
Details: {"itemNotFound": {"message": "Fixed ip not found for address 10.180.80.24.", "code": 404}}

八成是这个接口已经不适用了,这里先到此为止,这个测试场景修改了一下打通了,以备后面其它用例也需要这种用法

 

 

发表回复