Basic smoke test script

1. What is the problem?
Multi-region test has been added to our check/gate jobs, but the
test just installs Tricircle via DevStack and doesn't provision
any resources like network/subnet/router/server, so Tricircle
functionality is not tested.

2. What is the solution to the problem?
Add a script in the test to create a basic network topology via
central Neutron and check if local resources are correctly created.
In the topology, two tenant networks are connected by a router, an
external network is attached to the router. We boot one server in
each tenant network and associate a floating IP to one of the server.

This patch also fixes a problem brought by
(1) Eliminate lookup of "resource extend" funcs by name
92372b982f

(2) Defer service_plugins configuration
a8204752e3

We can put these changes in a standalone patch, but let's first put them
here to test by this smoke test.

3. What features need to be implemented to the Tricircle
to realize the solution?
Tricircle functionality can be tested.

Change-Id: Ib364a96fe4c3b9b635e5fac979c7c1cba2aaefc9
This commit is contained in:
zhiyuan_cai 2017-04-11 17:13:54 +08:00
parent 1eae571e56
commit a4a3a9ff11
9 changed files with 257 additions and 17 deletions

View File

@ -30,6 +30,7 @@ Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS=(network_vlan_ranges=bridge:2001:3000,extern:3001
Q_ML2_PLUGIN_VXLAN_TYPE_OPTIONS=(vni_ranges=1001:2000)
Q_ML2_PLUGIN_FLAT_TYPE_OPTIONS=(flat_networks=bridge,extern)
OVS_BRIDGE_MAPPINGS=bridge:br-vlan
ML2_L3_PLUGIN=tricircle.network.local_l3_plugin.TricircleL3Plugin
# Specify Central Region name
# CENTRAL_REGION_NAME=CentralRegion

View File

@ -34,6 +34,7 @@ Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS=(network_vlan_ranges=bridge:2001:3000,extern:3001
Q_ML2_PLUGIN_VXLAN_TYPE_OPTIONS=(vni_ranges=1001:2000)
Q_ML2_PLUGIN_FLAT_TYPE_OPTIONS=(flat_networks=bridge,extern)
OVS_BRIDGE_MAPPINGS=bridge:br-vlan,extern:br-ext
ML2_L3_PLUGIN=tricircle.network.local_l3_plugin.TricircleL3Plugin
# Specify Central Region name
# CENTRAL_REGION_NAME=CentralRegion

View File

@ -64,10 +64,9 @@ function init_common_tricircle_conf {
function init_local_neutron_conf {
iniset $NEUTRON_CONF DEFAULT core_plugin tricircle.network.local_plugin.TricirclePlugin
iniset $NEUTRON_CONF DEFAULT service_plugins tricircle.network.local_l3_plugin.TricircleL3Plugin
iniset $NEUTRON_CONF client auth_url http://$KEYSTONE_SERVICE_HOST:5000/v3
iniset $NEUTRON_CONF client identity_url http://$KEYSTONE_SERVICE_HOST:35357/v3
iniset $NEUTRON_CONF client auth_url http://$KEYSTONE_SERVICE_HOST/identity/v3
iniset $NEUTRON_CONF client identity_url http://$KEYSTONE_SERVICE_HOST/identity_admin/v3
iniset $NEUTRON_CONF client admin_username admin
iniset $NEUTRON_CONF client admin_password $ADMIN_PASSWORD
iniset $NEUTRON_CONF client admin_tenant demo
@ -162,6 +161,7 @@ function configure_tricircle_api_wsgi {
sudo cp $TRICIRCLE_API_APACHE_TEMPLATE $tricircle_api_apache_conf
sudo sed -e "
s|%TRICIRCLE_BIN%|$tricircle_bin_dir|g;
s|%PUBLICPORT%|$TRICIRCLE_API_PORT|g;
s|%APACHE_NAME%|$APACHE_NAME|g;
s|%PUBLICWSGI%|$tricircle_bin_dir/tricircle-api-wsgi|g;

View File

@ -35,10 +35,10 @@ from tricircle.db import models
client_opts = [
cfg.StrOpt('auth_url',
default='http://127.0.0.1:5000/v3',
default='http://127.0.0.1/identity/v3',
help='keystone authorization url'),
cfg.StrOpt('identity_url',
default='http://127.0.0.1:35357/v3',
default='http://127.0.0.1/identity_admin/v3',
help='keystone service url'),
cfg.BoolOpt('auto_refresh_endpoint',
default=False,

View File

@ -28,9 +28,9 @@ from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
import neutron.common.exceptions as ml2_exceptions
from neutron.db import _resource_extend as resource_extend
from neutron.db import api as q_db_api
from neutron.db.availability_zone import router as router_az
from neutron.db import common_db_mixin
from neutron.db import db_base_plugin_v2
from neutron.db import external_net_db
from neutron.db import extradhcpopt_db
@ -218,13 +218,11 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
availability_zone=diff.pop())
@staticmethod
@resource_extend.extends([attributes.NETWORKS])
def _extend_availability_zone(net_res, net_db):
net_res[az_ext.AZ_HINTS] = az_ext.convert_az_string_to_list(
net_db[az_ext.AZ_HINTS])
common_db_mixin.CommonDbMixin.register_dict_extend_funcs(
attributes.NETWORKS, ['_extend_availability_zone'])
@staticmethod
def _ensure_az_set_for_external_network(context, req_data):
external = req_data.get(external_net.EXTERNAL)

View File

@ -40,11 +40,23 @@ function _setup_tricircle_multinode {
ENABLE_TRICIRCLE="enable_plugin tricircle https://git.openstack.org/openstack/tricircle/"
# Configure primary node
export DEVSTACK_LOCAL_CONFIG="$ENABLE_TRICIRCLE"
export DEVSTACK_LOCAL_CONFIG+=$'\n'"TRICIRCLE_START_SERVICES=True"
export DEVSTACK_LOCAL_CONFIG+=$'\n'"REGION_NAME=RegionOne"
export DEVSTACK_LOCAL_CONFIG+=$'\n'"HOST_IP=$PRIMARY_NODE_IP"
ML2_CONFIG=$'\n'"ML2_L3_PLUGIN=tricircle.network.local_l3_plugin.TricircleL3Plugin"
ML2_CONFIG+=$'\n'"[[post-config|/"'$Q_PLUGIN_CONF_FILE]]'
ML2_CONFIG+=$'\n'"[ml2]"
ML2_CONFIG+=$'\n'"mechanism_drivers = openvswitch,linuxbridge,l2population"
ML2_CONFIG+=$'\n'"[agent]"
ML2_CONFIG+=$'\n'"tunnel_types=vxlan"
ML2_CONFIG+=$'\n'"l2_population=True"
export DEVSTACK_LOCAL_CONFIG+=$ML2_CONFIG
# Configure sub-node
export DEVSTACK_SUBNODE_CONFIG="$ENABLE_TRICIRCLE"
export DEVSTACK_SUBNODE_CONFIG+=$'\n'"TRICIRCLE_START_SERVICES=False"
export DEVSTACK_SUBNODE_CONFIG+=$'\n'"REGION_NAME=RegionTwo"
@ -59,6 +71,8 @@ function _setup_tricircle_multinode {
export DEVSTACK_SUBNODE_CONFIG+=$'\n'"DATABASE_HOST=$SUBNODE_IP"
export DEVSTACK_SUBNODE_CONFIG+=$'\n'"GLANCE_HOSTPORT=$SUBNODE_IP:9292"
export DEVSTACK_SUBNODE_CONFIG+=$'\n'"Q_HOST=$SUBNODE_IP"
export DEVSTACK_SUBNODE_CONFIG+=$ML2_CONFIG
}
if [ "$DEVSTACK_GATE_TOPOLOGY" == "multinode" ]; then

View File

@ -94,11 +94,7 @@ iniset $TEMPEST_CONF volume-feature-enabled api_v1 false
iniset $TEMPEST_CONF validation connect_method fixed
# Run the Network Tempest tests
cd $TRICIRCLE_TEMPEST_PLUGIN_DIR
sudo BASE=$BASE ./tempest_network.sh
# Run the Scenario Tempest tests
# cd $TRICIRCLE_TEMPEST_PLUGIN_DIR
# sudo BASE=$BASE ./tempest_scenario.sh
if [ "$DEVSTACK_GATE_TOPOLOGY" == "multinode" ]; then
cd $TRICIRCLE_TEMPEST_PLUGIN_DIR
sudo BASE=$BASE bash smoke_test.sh
fi

View File

@ -0,0 +1,155 @@
#!/bin/bash -xe
DEST=$BASE/new
DEVSTACK_DIR=$DEST/devstack
source $DEVSTACK_DIR/openrc admin demo
unset OS_REGION_NAME
openstacktop="openstack --os-region-name CentralRegion"
openstackpod1="openstack --os-region-name RegionOne"
openstackpod2="openstack --os-region-name RegionTwo"
echo list networks before running
$openstacktop network list
echo create external network
$openstacktop network create --external --provider-network-type vlan \
--provider-physical-network extern --availability-zone-hint RegionTwo ext-net
echo show networks after running
for id in $($openstacktop network list -c ID -f value)
do $openstacktop network show $id
done
echo create external subnet
$openstacktop subnet create --subnet-range 163.3.124.0/24 --network ext-net \
--no-dhcp ext-subnet
echo create router
router_id=$($openstacktop router create router -c id -f value)
echo attach router to external network
$openstacktop router set --external-gateway ext-net router
echo create network1
$openstacktop network create net1
echo create subnet1
$openstacktop subnet create --subnet-range 10.0.1.0/24 --network net1 subnet1
echo create port1
port1_id=$($openstacktop port create --network net1 port1 -c id -f value)
echo attach subnet1 to router
$openstacktop router add subnet router subnet1
echo associate floating ip to port1
$openstacktop floating ip create --port $port1_id ext-net -c id -f value
image1_id=$($openstackpod1 image list -c ID -f value)
echo create server1
$openstackpod1 server create --flavor 1 --image $image1_id --nic port-id=$port1_id vm1
echo create network2
net2_id=$($openstacktop network create net2 -c id -f value)
echo create subnet2
$openstacktop subnet create --subnet-range 10.0.2.0/24 --network net2 subnet2
image2_id=$($openstackpod2 image list -c ID -f value)
echo create server2
$openstackpod2 server create --flavor 1 --image $image2_id --nic net-id=$net2_id vm2
echo attach subnet2 to router
$openstacktop router add subnet router subnet2
sleep 20
TOP_DIR=$DEVSTACK_DIR
source $DEVSTACK_DIR/stackrc
source $DEVSTACK_DIR/inc/meta-config
extract_localrc_section $TOP_DIR/local.conf $TOP_DIR/localrc $TOP_DIR/.localrc.auto
source $DEVSTACK_DIR/functions-common
source $DEVSTACK_DIR/lib/database
initialize_database_backends
if [ "$DATABASE_TYPE" == "mysql" ]; then
for i in $(seq 1 11); do
if [ $i == 11 ]; then
# we check fail job at the end to give fail job a chance to redo
fail_result=$(mysql -u$DATABASE_USER -p$DATABASE_PASSWORD -h$DATABASE_HOST -Dtricircle -e 'SELECT COUNT(*) FROM async_jobs WHERE status = "0_Fail"')
fail_count=$(echo $fail_result | grep -o "[0-9]\{1,\}")
if [ $fail_count -ne 0 ]; then
echo "Listing fail job"
mysql -u$DATABASE_USER -p$DATABASE_PASSWORD -h$DATABASE_HOST -Dtricircle -e 'SELECT * FROM async_jobs WHERE status = "0_Fail";'
die $LINENO "Smoke test fails, $fail_count job fail"
fi
die $LINENO "Smoke test fails, exceed max wait time for job"
fi
full_result=$(mysql -u$DATABASE_USER -p$DATABASE_PASSWORD -h$DATABASE_HOST -Dtricircle -e 'SELECT COUNT(*) FROM async_jobs;')
full_count=$(echo $full_result | grep -o "[0-9]\{1,\}")
if [ $full_count -ne 0 ]; then
echo "Wait for job to finish"
sleep 5
else
break
fi
done
else
for i in $(seq 1 11); do
if [ $i == 11 ]; then
# we check fail job at the end to give fail job a chance to redo
fail_result=$(psql -h$DATABASE_HOST -U$DATABASE_USER -dtricircle -c 'SELECT COUNT(*) FROM async_jobs WHERE status = "0_Fail"')
fail_count=$(echo $fail_result | grep -o "[0-9]\{1,\}")
if [ $fail_count -ne 0 ]; then
echo "Listing fail job"
psql -h$DATABASE_HOST -U$DATABASE_USER -dtricircle -c 'SELECT * FROM async_jobs WHERE status = "0_Fail";'
die $LINENO "Smoke test fails, $fail_count job fail"
fi
die $LINENO "Smoke test fails, exceed max wait time for job"
fi
full_result=$(psql -h$DATABASE_HOST -U$DATABASE_USER -dtricircle -c 'SELECT COUNT(*) FROM async_jobs;')
full_count=$(echo $full_result | grep -o "[0-9]\{1,\}")
if [ $full_count -ne 0 ]; then
echo "Wait for job to finish"
sleep 5
else
break
fi
done
fi
$openstackpod1 server list -f json | python smoke_test_validation.py server 1
if [ $? != 0 ]; then
die $LINENO "Smoke test fails, error in server of RegionOne"
fi
$openstackpod2 server list -f json | python smoke_test_validation.py server 2
if [ $? != 0 ]; then
die $LINENO "Smoke test fails, error in server of RegionTwo"
fi
$openstackpod1 subnet list -f json | python smoke_test_validation.py subnet 1
if [ $? != 0 ]; then
die $LINENO "Smoke test fails, error in subnet of RegionOne"
fi
$openstackpod2 subnet list -f json | python smoke_test_validation.py subnet 2
if [ $? != 0 ]; then
die $LINENO "Smoke test fails, error in subnet of RegionTwo"
fi
$openstackpod1 port list --router $router_id -f json | python smoke_test_validation.py router_port 1
if [ $? != 0 ]; then
die $LINENO "Smoke test fails, error in router port of RegionOne"
fi
$openstackpod2 port list --router $router_id -f json | python smoke_test_validation.py router_port 2
if [ $? != 0 ]; then
die $LINENO "Smoke test fails, error in router port of RegionTwo"
fi
$openstackpod1 router show $router_id -c routes -f json | python smoke_test_validation.py router 1
if [ $? != 0 ]; then
die $LINENO "Smoke test fails, error in router of RegionOne"
fi
$openstackpod2 router show $router_id -c routes -f json | python smoke_test_validation.py router 2
if [ $? != 0 ]; then
die $LINENO "Smoke test fails, error in router of RegionTwo"
fi

View File

@ -0,0 +1,75 @@
# Copyright 2017 Huawei Technologies Co., Ltd.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import sys
class ContainedString(object):
def __init__(self, txt):
self.content = txt
def __eq__(self, other):
return other.find(self.content) != -1
def __ne__(self, other):
return other.find(self.content) == -1
CONDITIONS = {
'1': {'server': [{'Name': 'vm1', 'Status': 'ACTIVE'}],
'subnet': [{'Subnet': '100.0.0.0/24'}, {'Subnet': '10.0.1.0/24'}],
'router_port': [{'Fixed IP Addresses': ContainedString('10.0.1')},
{'Fixed IP Addresses': ContainedString('100.0.0')}],
'router': [
{'routes': ContainedString(
"destination='0.0.0.0/0', gateway='100.0.0.1'")},
{'routes': ContainedString("destination='10.0.2")}]},
'2': {'server': [{'Name': 'vm2', 'Status': 'ACTIVE'}],
'subnet': [{'Subnet': '100.0.0.0/24'}, {'Subnet': '10.0.1.0/24'},
{'Subnet': '10.0.2.0/24'}, {'Subnet': '163.3.124.0/24'}],
'router_port': [{'Fixed IP Addresses': ContainedString('10.0.2')},
{'Fixed IP Addresses': ContainedString('100.0.0')}],
'router': [
{'routes': ContainedString(
"destination='0.0.0.0/0', gateway='100.0.0.1'")},
{'routes': ContainedString("destination='10.0.1")}]}
}
def validate_condition(result, condition):
if not isinstance(result, list):
result = [result]
for res in result:
if all(res[key] == value for (key, value) in condition.items()):
return True
return False
def validate_result(result, region, res_type):
for condition in CONDITIONS[region][res_type]:
if not validate_condition(result, condition):
return False
return True
if __name__ == '__main__':
res_type, region = sys.argv[1:]
raw_result = ''.join([line for line in sys.stdin])
result = json.loads(raw_result)
passed = validate_result(result, region, res_type)
# True is casted to 1, but 1 indicates error in shell
sys.exit(1 - int(passed))