Merge "End to end scenario test case for l2gateway"

stable/ocata
Jenkins 8 years ago committed by Gerrit Code Review
commit b730370310

@ -0,0 +1,153 @@
# Copyright 2015 OpenStack Foundation
# Copyright 2015 Hewlett-Packard Development Company, L.P.
# 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 collections
import json
import logging
import random
import socket
import threading
import time
logging.basicConfig(level=logging.DEBUG)
def default_echo_handler(message, ovsconn):
logging.debug("responding to echo")
ovsconn.send({"result": message.get("params", None),
"error": None, "id": message['id']})
def default_message_handler(message, ovsconn):
ovsconn.responses.append(message)
class OVSDBConnection(threading.Thread):
"""Connects to an ovsdb server that has manager set using
ovs-vsctl set-manager ptcp:5000
clients can make calls and register a callback for results, callbacks
are linked based on the message ids.
clients can also register methods which they are interested in by
providing a callback.
"""
def __init__(self, IP, PORT, **handlers):
super(OVSDBConnection, self).__init__()
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((IP, PORT))
self.responses = []
self.callbacks = {}
self.read_on = True
self.handlers = handlers or {"echo": default_echo_handler}
self.start()
def send(self, message, callback=None):
if callback:
self.callbacks[message['id']] = callback
self.socket.send(json.dumps(message))
def response(self, id):
return [x for x in self.responses if x['id'] == id]
def set_handler(self, method_name, handler):
self.handlers[method_name] = handler
def _on_remote_message(self, message):
try:
json_m = json.loads(message,
object_pairs_hook=collections.OrderedDict)
handler_method = json_m.get('method', None)
if handler_method:
self.handlers.get(handler_method, default_message_handler)(
json_m, self)
elif json_m.get("result", None) and json_m['id'] in self.callbacks:
id = json_m['id']
if not self.callbacks[id](json_m, self):
self.callbacks.pop(id)
else:
default_message_handler(message, self)
except Exception as e:
logging.exception(
"exception [%s] in handling message [%s]", e.message, message)
def __echo_response(message, self):
self.send({"result": message.get("params", None),
"error": None, "id": message['id']})
def run(self):
chunks = []
lc = rc = 0
while self.read_on:
try:
response = self.socket.recv(4096)
if response:
response = response.decode('utf8')
message_mark = 0
for i, c in enumerate(response):
if c == '{':
lc += 1
elif c == '}':
rc += 1
if rc > lc:
raise Exception("json string not valid")
elif lc == rc and lc is not 0:
chunks.append(response[message_mark:i + 1])
message = "".join(chunks)
self._on_remote_message(message)
lc = rc = 0
message_mark = i + 1
chunks = []
chunks.append(response[message_mark:])
except Exception:
# Pass to avoid EOF error
pass
def stop(self, force=False):
self.read_on = False
if force:
self.socket.close()
def select_table(self, table):
select_dict = {"op": "select", "table": table, "where": []}
op_id = str(random.getrandbits(128))
params = ['hardware_vtep']
params.append(select_dict)
query_select = {"method": "transact",
"params": params,
"id": op_id}
return query_select
def find_row(self, net_id, count, resp_dec):
for i in range(count):
row = str(resp_dec['result'][0]['rows'][i])
if net_id in row:
return row
def get_response(self, OVSDB_IP, OVSDB_PORT, table):
query = self.select_table(table)
self.send(query)
time.sleep(2)
resp = self.responses
resp = str(resp[0])
return resp

@ -0,0 +1,388 @@
# Copyright 2015 OpenStack Foundation
# Copyright 2015 Hewlett-Packard Development Company, L.P.
# 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.
from networking_l2gw.tests.api import base_l2gw
from networking_l2gw.tests.scenario import ovsdb_connections
from networking_l2gw.tests.tempest import config
from neutron.i18n import _LI
from neutron.tests.api import base
from neutron.tests.tempest import exceptions
from neutron.tests.tempest import manager
from oslo_log import log
from tempest_lib.common import rest_client
from tempest_lib.common.utils import data_utils
from tempest_lib.common.utils import misc as misc_utils
from tempest_lib import exceptions as lib_exc
import json
import time
CONF = config.CONF
LOG = log.getLogger(__name__)
OVSDB_IP = CONF.network.ovsdb_ip
OVSDB_PORT = CONF.network.ovsdb_port
OVSDB_SCHEMA_NAME = CONF.network.ovsdb_schema_name
class TestL2GatewayBasic(base.BaseAdminNetworkTest):
"""This test case tests the basic end to end functionality of l2-gateway
and tests whether the appropriate entries are getting registered in
the ovsdb.
"""
@classmethod
def resource_setup(cls):
super(TestL2GatewayBasic, cls).resource_setup()
nova_creds = cls.isolated_creds.get_admin_creds()
cls.auth_provider = manager.get_auth_provider(nova_creds)
def _create_server(
self, name=None,
network=None, wait_on_boot=True, wait_on_delete=True):
region = CONF.compute.region
image = CONF.compute.image_ref
flavor = CONF.compute.flavor_ref
rs_client = rest_client.RestClient(
self.auth_provider, 'compute', region)
data = {'server': {
'name': name,
'imageRef': image,
'flavorRef': flavor,
'max_count': 1,
'min_count': 1,
'networks': [{'uuid': network}]}}
data = json.dumps(data)
(resp, body,) = rs_client.post('/servers', data)
rs_client.expected_success(202, resp.status)
body = json.loads(body)
server_id = body['server']['id']
self.wait_for_server_status(server_id, 'ACTIVE')
return server_id
def _delete_server(self, server=None):
rs_client = rest_client.RestClient(
self.auth_provider, 'compute', 'RegionOne')
(resp, body, ) = rs_client.delete('servers/%s' % str(server))
self.wait_for_server_termination(server)
rest_client.ResponseBody(resp, body)
def wait_for_server_status(self, server_id, status, ready_wait=True,
extra_timeout=0, raise_on_error=True):
"""Waits for a server to reach a given status."""
build_timeout = CONF.compute.build_timeout
build_interval = CONF.boto.build_interval
def _get_task_state(body):
return body.get('OS-EXT-STS:task_state', None)
rs = rest_client.RestClient(self.auth_provider, "compute", "RegionOne")
resp, body = rs.get("servers/%s" % str(server_id))
body = json.loads(body)
old_status = server_status = body['server']['status']
old_task_state = task_state = _get_task_state(body)
start_time = int(time.time())
timeout = build_timeout + extra_timeout
while True:
if status == 'BUILD' and server_status != 'UNKNOWN':
return
if server_status == status:
if ready_wait:
if status == 'BUILD':
return
if str(task_state) == "None":
time.sleep(CONF.compute.ready_wait)
return
else:
return
time.sleep(build_interval)
resp, body = rs.get("servers/%s" % str(server_id))
body = json.loads(body)
server_status = body['server']['status']
task_state = _get_task_state(body)
if (server_status != old_status) or (task_state != old_task_state):
oldstatus = '/'.join((old_status, str(old_task_state)))
serverstatus = '/'.join((server_status, str(task_state)))
waitsec = (time.time() - start_time)
LOG.info(
_LI('State transtion %(oldstatus)s => %(serverstatus)s'
'after %(waitsec)d second wait') %
{'oldstatus': oldstatus, 'serverstatus': serverstatus,
'waitsec': waitsec}
)
if (server_status == 'ERROR') and raise_on_error:
if 'fault' in body:
raise exceptions.BuildErrorException(body['fault'],
server_id=server_id)
else:
raise exceptions.BuildErrorException(server_id=server_id)
timed_out = int(time.time()) - start_time >= timeout
if timed_out:
expected_task_state = 'None' if ready_wait else 'n/a'
message = ('Server %(server_id)s failed to reach %(status)s '
'status and task state "%(expected_task_state)s" '
'within the required time (%(timeout)s s).' %
{'server_id': server_id,
'status': status,
'expected_task_state': expected_task_state,
'timeout': timeout})
message += ' Current status: %s.' % server_status
message += ' Current task state: %s.' % task_state
caller = misc_utils.find_test_caller()
if caller:
message = '(%s) %s' % (caller, message)
raise exceptions.TimeoutException(message)
old_status = server_status
old_task_state = task_state
def wait_for_server_termination(self, server_id, ignore_error=False):
"""Waits for server to reach termination."""
build_interval = CONF.boto.build_interval
while True:
try:
rs = rest_client.RestClient(
self.auth_provider, 'compute', 'RegionOne')
(resp, body,) = rs.get('servers/%s' % str(server_id))
body = json.loads(body)
except lib_exc.NotFound:
return
server_status = body['server']['status']
if server_status == 'ERROR' and not ignore_error:
raise exceptions.BuildErrorException(server_id=server_id)
time.sleep(build_interval)
def validate_ovsdb(self, seg_id, port, network_id_1, tunnel_key):
# Check Logical_Switch
objConnection = ovsdb_connections.OVSDBConnection(OVSDB_IP, OVSDB_PORT)
resp = objConnection.get_response(
OVSDB_IP, OVSDB_PORT, "Logical_Switch")
resp_dec = json.loads(resp)
count = resp.count('_uuid')
try:
self.assertIn(str(network_id_1), resp)
except Exception:
raise lib_exc.NotFound("Network not found in Logical Switch table")
row = objConnection.find_row(network_id_1, count, resp_dec)
try:
self.assertIn(str(tunnel_key), row)
except Exception:
raise lib_exc.NotFound(
"Tunnel key not found in Logical Switch table")
objConnection.stop("true")
# Check Physical_Port
objConnection = ovsdb_connections.OVSDBConnection(OVSDB_IP, OVSDB_PORT)
resp = objConnection.get_response(
OVSDB_IP, OVSDB_PORT, "Physical_Port")
count = resp.count('_uuid')
try:
self.assertIn(str(seg_id[0]), resp)
except Exception:
raise lib_exc.NotFound(
"Segmentation ID not found in Physical Port table")
objConnection.stop("true")
# Check Physical_Locator
objConnection = ovsdb_connections.OVSDBConnection(OVSDB_IP, OVSDB_PORT)
resp = objConnection.get_response(
OVSDB_IP, OVSDB_PORT, "Physical_Locator")
count = resp.count('_uuid')
port_str = str(port)
count_port = port_str.count('fixed_ips')
net_node_host = []
compute_node_host = []
# Extracting unique Network node host name and Compute host name
for i in range(count_port):
net_id = port['ports'][i]['network_id']
device_owner = port['ports'][i]['device_owner']
if net_id == network_id_1 and device_owner == 'network:dhcp':
if port['ports'][i]['binding:host_id'] not in net_node_host:
net_node_host.append(port['ports'][i]['binding:host_id'])
if port['ports'][i]['device_owner'] == 'compute:None':
if port['ports'][i]['binding:host_id'] not in net_node_host:
compute_node_host.append(
port['ports'][i]['binding:host_id'])
ip_SW = CONF.network.l2gw_switch_ip
host_and_ip = CONF.network.hosts
list_ = host_and_ip.split(', ')
host_ip_dict = {}
for i in list_:
sub_list = i.split(':')
host = sub_list[0]
ip = sub_list[1]
host_ip_dict.update({host: ip})
for net_node in net_node_host:
ip_NN = host_ip_dict[net_node]
try:
self.assertIn(ip_NN, resp)
except Exception:
raise lib_exc.NotFound(
"Network Node IP not found in Physical Locator table")
for compute_node in compute_node_host:
ip_CN = host_ip_dict[compute_node]
try:
self.assertIn(ip_CN, resp)
except Exception:
raise lib_exc.NotFound(
"Compute Node IP not found in Physical Locator table")
try:
self.assertIn(ip_SW, resp)
except Exception:
raise lib_exc.NotFound(
"Switch IP not found in Physical Locator table")
objConnection.stop("true")
# Check Ucast_macs_Remote
objConnection = ovsdb_connections.OVSDBConnection(OVSDB_IP, OVSDB_PORT)
resp = objConnection.get_response(
OVSDB_IP, OVSDB_PORT, "Ucast_Macs_Remote")
ip_mac_dict = {}
count_uuid = resp.count('_uuid')
resp_dec = json.loads(resp)
for i in range(count_port):
mac = port['ports'][i]['mac_address']
ip = port['ports'][i]['fixed_ips'][0]['ip_address']
ip_mac_dict.update({mac: ip})
try:
for key, value in ip_mac_dict.iteritems():
row = objConnection.find_row(key, count_uuid, resp_dec)
self.assertIn(value, row)
except Exception:
raise lib_exc.NotFound(
"MAC & its port not found in UCast MAC Remote table")
objConnection.stop("true")
def _create_l2_gateway(self, name, devices):
body_l2gateway = self.admin_client.create_l2_gateway(
name=name, devices=devices)
self.addCleanup(
self.admin_client.delete_l2_gateway,
body_l2gateway['l2_gateway']['id'])
return body_l2gateway
def _create_l2_gw_connection(
self, l2gw, net_id, seg_id=None, explicit=None):
l2gw_id = l2gw['l2_gateway']['id']
if l2gw['l2_gateway']['devices'][0]['interfaces'][
0]['segmentation_id']:
resp_l2gwconn = self.admin_client.create_l2_gateway_connection(
network_id=net_id, l2_gateway_id=l2gw_id)
else:
resp_l2gwconn = self.admin_client.create_l2_gateway_connection(
network_id=net_id,
l2_gateway_id=l2gw_id, segmentation_id=seg_id)
if explicit:
# Connection deleted explicitly, thus addCleanup not called
pass
else:
self.addCleanup(
self.admin_client.delete_l2_gateway_connection,
resp_l2gwconn['l2_gateway_connection']['id'])
return resp_l2gwconn
def _setup_network_and_server(self, cidr=None):
network = self.create_network()
self.addCleanup(self.client.delete_network, network['id'])
self.create_subnet(network=network, cidr=cidr)
name = data_utils.rand_name('server-smoke')
server_id = self._create_server(name, network=network['id'])
self.addCleanup(self._delete_server, server_id)
return network
def test_l2gw_create_connection(self):
network = self._setup_network_and_server()
network_body = self.admin_client.show_network(network['id'])
gw_name = data_utils.rand_name('l2gw')
devices = base_l2gw.get_l2gw_body(CONF.network.l2gw_switch)['devices']
l2_gw_body = self._create_l2_gateway(name=gw_name, devices=devices)
segmentation_id = l2_gw_body['l2_gateway']['devices'][0][
'interfaces'][0]['segmentation_id']
self._create_l2_gw_connection(l2_gw_body, network['id'])
tunnel_key = network_body['network']['provider:segmentation_id']
port = self.admin_client.list_ports()
self.validate_ovsdb(segmentation_id, port, network['id'], tunnel_key)
def test_multiple_connections(self):
# Create first connection and validate
network = self._setup_network_and_server()
network_body = self.admin_client.show_network(network['id'])
gw_name = data_utils.rand_name('l2gw')
devices = base_l2gw.get_l2gw_body(CONF.network.l2gw_switch)['devices']
l2_gw_body = self._create_l2_gateway(name=gw_name, devices=devices)
segmentation_id = l2_gw_body['l2_gateway']['devices'][0][
'interfaces'][0]['segmentation_id']
self._create_l2_gw_connection(l2_gw_body, network['id'])
tunnel_key = network_body['network']['provider:segmentation_id']
port = self.admin_client.list_ports()
self.validate_ovsdb(segmentation_id, port, network['id'], tunnel_key)
# Create second connection and validate
network_2 = self._setup_network_and_server()
network_body_2 = self.admin_client.show_network(network_2['id'])
gw_name_2 = data_utils.rand_name('l2gw')
devices_2 = base_l2gw.get_l2gw_body(
CONF.network.l2gw_switch_2)['devices']
l2_gw_body_2 = self._create_l2_gateway(
name=gw_name_2, devices=devices_2)
segmentation_id_2 = l2_gw_body_2['l2_gateway']['devices'][0][
'interfaces'][0]['segmentation_id']
self._create_l2_gw_connection(l2_gw_body_2, network_2['id'])
tunnel_key_2 = network_body_2['network']['provider:segmentation_id']
port_2 = self.admin_client.list_ports()
self.validate_ovsdb(
segmentation_id_2, port_2, network_2['id'], tunnel_key_2)
def test_boot_vm_after_create_connection(self):
network = self.create_network()
self.addCleanup(self.client.delete_network, network['id'])
self.create_subnet(network)
gw_name = data_utils.rand_name('l2gw')
devices = base_l2gw.get_l2gw_body(CONF.network.l2gw_switch)['devices']
l2_gw_body = self._create_l2_gateway(name=gw_name, devices=devices)
segmentation_id = l2_gw_body['l2_gateway']['devices'][0][
'interfaces'][0]['segmentation_id']
self._create_l2_gw_connection(l2_gw_body, network['id'])
name = data_utils.rand_name('server-smoke')
server_id = self._create_server(name, network=network['id'])
self.addCleanup(self._delete_server, server_id)
network_body = self.admin_client.show_network(network['id'])
segmentation_id = l2_gw_body['l2_gateway']['devices'][0][
'interfaces'][0]['segmentation_id']
tunnel_key = network_body['network']['provider:segmentation_id']
port = self.admin_client.list_ports()
self.validate_ovsdb(segmentation_id, port, network['id'], tunnel_key)
def test_create_new_connection_after_deleting_old_one(self):
network = self._setup_network_and_server()
network_body = self.admin_client.show_network(network['id'])
gw_name = data_utils.rand_name('l2gw')
devices = base_l2gw.get_l2gw_body(CONF.network.l2gw_switch)['devices']
l2_gw_body = self._create_l2_gateway(name=gw_name, devices=devices)
segmentation_id = l2_gw_body['l2_gateway']['devices'][0][
'interfaces'][0]['segmentation_id']
# Create a connection and validate ovsdb
l2gw_connection = self._create_l2_gw_connection(
l2_gw_body, network['id'], explicit=True)
tunnel_key = network_body['network']['provider:segmentation_id']
port = self.admin_client.list_ports()
self.validate_ovsdb(segmentation_id, port, network['id'], tunnel_key)
# Delete and create new connection and validate ovsdb
self.admin_client.delete_l2_gateway_connection(
l2gw_connection['l2_gateway_connection']['id'])
self._create_l2_gw_connection(l2_gw_body, network['id'])
self.validate_ovsdb(segmentation_id, port, network['id'], tunnel_key)

@ -403,7 +403,25 @@ network_group = cfg.OptGroup(name='network',
NetworkGroup = [
cfg.StrOpt('l2gw_switch',
default='',
help='Test'),
help='Switch name ,interface and vlan id information '),
cfg.StrOpt('l2gw_switch_2',
default='',
help='Switch name ,interface and vlan id information'),
cfg.StrOpt('hosts',
default='',
help='Network node and compute node host names and IPs'),
cfg.StrOpt('l2gw_switch_ip',
default='',
help='Switch IP'),
cfg.StrOpt('ovsdb_ip',
default='',
help='IP of ovsdb server'),
cfg.IntOpt('ovsdb_port',
default=6632,
help='Port of ovsdb server'),
cfg.StrOpt('ovsdb_schema_name',
default='',
help='schema name of ovsdb'),
cfg.StrOpt('catalog_type',
default='network',
help='Catalog type of the Neutron service.'),

@ -1,6 +1,12 @@
[l2_gateway]
[network]
#List of switch device,interfaces name and segmentation_ids for l2gateway
l2gw_switch = cell08-5930-01::FortyGigE1/0/1|100
l2gw_switch_2 = cell21-5930-01::FortyGigE1/0/1|101
#l2gw_switch = cell08-5930-01::FortyGigE1/0/1|100#200;FortyGigE1/0/2|300
#l2gw_switch = cell08-5930-01::FortyGigE1/0/1|100#200,cell08-5930-02::FortyGigE1/0/2|300
l2gw_switch_ip = switch_vtep_ip
hosts = cn_host_name:cn_ip, nn_host_name:nn_ip
ovsdb_ip = ovsdb_ip
ovsdb_port = 6632
ovsdb_schema_name = hardware_vtep

Loading…
Cancel
Save