目前主要还是基于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}}
八成是这个接口已经不适用了,这里先到此为止,这个测试场景修改了一下打通了,以备后面其它用例也需要这种用法
