diff --git a/devstack/plugin.sh b/devstack/plugin.sh index a2f6b446..9e0ee594 100644 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -113,6 +113,9 @@ function init_local_neutron_variables { OVS_BRIDGE_MAPPINGS=$vlan_mapping,$ext_mapping fi + if [ "$TRICIRCLE_ENABLE_TRUNK" == "True" ]; then + _neutron_service_plugin_class_add trunk + fi } function add_default_bridges { @@ -279,6 +282,10 @@ function start_central_neutron_server { iniset $NEUTRON_CONF.$server_index client auto_refresh_endpoint True iniset $NEUTRON_CONF.$server_index client top_region_name $CENTRAL_REGION_NAME + if [ "$TRICIRCLE_ENABLE_TRUNK" == "True" ]; then + iniset $NEUTRON_CONF.$server_index DEFAULT service_plugins "tricircle.network.central_trunk_plugin.TricircleTrunkPlugin" + fi + local type_drivers='' local tenant_network_types='' if [ "$Q_ML2_PLUGIN_VXLAN_TYPE_OPTIONS" != "" ]; then diff --git a/devstack/settings b/devstack/settings index 0cfb1de9..365ee4dc 100644 --- a/devstack/settings +++ b/devstack/settings @@ -10,6 +10,9 @@ TRICIRCLE_START_SERVICES=${TRICIRCLE_START_SERVICES:-True} TRICIRCLE_DEPLOY_WITH_WSGI=${TRICIRCLE_DEPLOY_WITH_WSGI:-True} TRICIRCLE_DEPLOY_WITH_CELL=${TRICIRCLE_DEPLOY_WITH_CELL:-False} +# extensions working with tricircle +TRICIRCLE_ENABLE_TRUNK=${TRICIRCLE_ENABLE_TRUNK:-False} + # these default settings are used for devstack based gate/check jobs TRICIRCLE_DEFAULT_VLAN_BRIDGE=${TRICIRCLE_DEFAULT_VLAN_BRIDGE:-br-vlan} TRICIRCLE_DEFAULT_VLAN_RANGE=${TRICIRCLE_DEFAULT_VLAN_RANGE:-101:150} diff --git a/tricircle/tempestplugin/gate_hook.sh b/tricircle/tempestplugin/gate_hook.sh index 5e78e18e..bb7ce5ca 100755 --- a/tricircle/tempestplugin/gate_hook.sh +++ b/tricircle/tempestplugin/gate_hook.sh @@ -43,6 +43,7 @@ function _setup_tricircle_multinode { # Configure primary node export DEVSTACK_LOCAL_CONFIG="$ENABLE_TRICIRCLE" export DEVSTACK_LOCAL_CONFIG+=$'\n'"TRICIRCLE_START_SERVICES=True" + export DEVSTACK_LOCAL_CONFIG+=$'\n'"TRICIRCLE_ENABLE_TRUNK=True" export DEVSTACK_LOCAL_CONFIG+=$'\n'"REGION_NAME=RegionOne" export DEVSTACK_LOCAL_CONFIG+=$'\n'"HOST_IP=$PRIMARY_NODE_IP" @@ -59,6 +60,7 @@ function _setup_tricircle_multinode { # Configure sub-node export DEVSTACK_SUBNODE_CONFIG="$ENABLE_TRICIRCLE" export DEVSTACK_SUBNODE_CONFIG+=$'\n'"TRICIRCLE_START_SERVICES=False" + export DEVSTACK_SUBNODE_CONFIG+=$'\n'"TRICIRCLE_ENABLE_TRUNK=True" export DEVSTACK_SUBNODE_CONFIG+=$'\n'"REGION_NAME=RegionTwo" export DEVSTACK_SUBNODE_CONFIG+=$'\n'"HOST_IP=$SUBNODE_IP" export DEVSTACK_SUBNODE_CONFIG+=$'\n'"KEYSTONE_REGION_NAME=RegionOne" diff --git a/tricircle/tempestplugin/smoke_test.sh b/tricircle/tempestplugin/smoke_test.sh index d82fa29e..e2bab5cb 100644 --- a/tricircle/tempestplugin/smoke_test.sh +++ b/tricircle/tempestplugin/smoke_test.sh @@ -13,3 +13,7 @@ python run_yaml_test.py multi_gw_topology_test.yaml "$OS_AUTH_URL" "$OS_TENANT_N if [ $? != 0 ]; then die $LINENO "Smoke test fails, error in multi gateway topology test" fi +python run_yaml_test.py trunk_test.yaml "$OS_AUTH_URL" "$OS_TENANT_NAME" "$OS_USERNAME" "$OS_PASSWORD" +if [ $? != 0 ]; then + die $LINENO "Smoke test fails, error in trunk test" +fi diff --git a/tricircle/tempestplugin/task_runner.py b/tricircle/tempestplugin/task_runner.py index 04de67ab..86affccb 100644 --- a/tricircle/tempestplugin/task_runner.py +++ b/tricircle/tempestplugin/task_runner.py @@ -22,17 +22,23 @@ import yaml from openstack import connection from openstack import profile +from tricircle.tests.network_sdk import network_service from tricircle.tests.tricircle_sdk import multiregion_network_service LOG = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) +SLEEP_INTERVAL = 20 + class DummyRunner(object): class DummyResource(object): def __init__(self, _id): self.id = _id + def __getattr__(self, item): + return item + def __init__(self): self.id_pool = {} @@ -68,7 +74,7 @@ class SDKRunner(object): 'region1': 'RegionOne', 'region2': 'RegionTwo'} serv_reslist_map = { - 'network': ['network', 'subnet', 'port', 'router', 'fip'], + 'network_sdk': ['network', 'subnet', 'port', 'router', 'fip', 'trunk'], 'compute': ['server'], 'image': ['image'], 'tricircle_sdk': ['job']} @@ -94,6 +100,8 @@ class SDKRunner(object): serv = multiregion_network_service.MultiregionNetworkService( version='v1') prof._add_service(serv) + net_serv = network_service.NetworkService(version='v2') + prof._add_service(net_serv) prof.set_region(profile.Profile.ALL, region) param['profile'] = prof conn = connection.Connection(**param) @@ -363,7 +371,7 @@ class RunnerEngine(object): if i == run_time - 1: raise else: - time.sleep(10) + time.sleep(SLEEP_INTERVAL) LOG.info('Redo failed task %s', task_id) def run_tasks(self, task_set_id, depend_task_set_result={}): diff --git a/tricircle/tempestplugin/trunk_test.yaml b/tricircle/tempestplugin/trunk_test.yaml new file mode 100644 index 00000000..272d696b --- /dev/null +++ b/tricircle/tempestplugin/trunk_test.yaml @@ -0,0 +1,229 @@ +- task_set_id: preparation + tasks: + - task_id: image1 + type: image + region: region1 + query: + get_one: true + - task_id: net1 + region: central + type: network + params: + name: net1 + provider_network_type: vlan + - task_id: subnet1 + region: central + type: subnet + depend: [net1] + params: + name: subnet1 + ip_version: 4 + cidr: 10.0.1.0/24 + network_id: net1@id + - task_id: net2 + region: central + type: network + params: + name: net2 + provider_network_type: vlan + - task_id: subnet2 + region: central + type: subnet + depend: [net2] + params: + name: subnet2 + ip_version: 4 + cidr: 10.0.2.0/24 + network_id: net2@id + - task_id: p1 + region: central + type: port + depend: + - net1 + - subnet1 + params: + name: p1 + network_id: net1@id + - task_id: p2 + region: central + type: port + depend: + - net2 + - subnet2 + params: + name: p2 + network_id: net2@id + - task_id: vm1 + region: region1 + type: server + depend: + - p1 + - image1 + - trunk1 + params: + flavor_id: 1 + image_id: image1@id + name: vm1 + networks: + - port: p1@id + - task_id: trunk1 + region: central + depend: + - p1 + - p2 + - net2 + type: trunk + params: + name: trunk1 + port_id: p1@id + sub_ports: + - port_id: p2@id + segmentation_type: vlan + segmentation_id: net2@provider_segmentation_id +- task_set_id: wait-for-job + tasks: + # ensure server is active and thus sync_trunk job is registered + - task_id: check-servers + region: region1 + type: server + validate: + predicate: any + retries: 10 + condition: + - status: ACTIVE + name: vm1 + - task_id: check-job + region: central + type: job + depend: [check-servers] + validate: + predicate: all + retries: 10 + condition: + - status: SUCCESS +- task_set_id: check + depend: [preparation] + tasks: + - task_id: check-ports + region: region1 + type: port + validate: + predicate: any + condition: + - name: p1 + fixed_ips: + - ip_address: 10.0.1* + - name: p2 + fixed_ips: + - ip_address: 10.0.2* + - task_id: check-trunks + region: region1 + type: trunk + validate: + predicate: any + condition: + - name: trunk1 + port_id: preparation@p1@id + sub_ports: + - port_id: preparation@p2@id + segmentation_type: vlan + segmentation_id: preparation@net2@provider_segmentation_id +- task_set_id: clean + depend: [preparation] + tasks: + - task_id: delete-server + region: region1 + type: server + action: + target: preparation@vm1@id + method: delete + - task_id: delete-trunk + region: central + type: trunk + depend: [delete-server] + action: + target: preparation@trunk1@id + method: delete + retries: 3 + - task_id: delete-p1 + region: central + type: port + depend: [delete-trunk] + action: + target: preparation@p1@id + method: delete + - task_id: delete-p2 + region: central + type: port + depend: [delete-trunk] + action: + target: preparation@p2@id + method: delete + - task_id: delete-subnet1 + region: central + type: subnet + depend: [delete-p1] + action: + target: preparation@subnet1@id + method: delete + retries: 3 + - task_id: delete-subnet2 + region: central + type: subnet + depend: [delete-p2] + action: + target: preparation@subnet2@id + method: delete + retries: 3 + - task_id: delete-net1 + region: central + type: network + depend: [delete-subnet1] + action: + target: preparation@net1@id + method: delete + - task_id: delete-net2 + region: central + type: network + depend: [delete-subnet2] + action: + target: preparation@net2@id + method: delete +- task_set_id: clean-check + tasks: + - task_id: check-no-trunks1 + region: region1 + type: trunk + validate: + predicate: all + condition: + - name: invalid-name + - task_id: check-no-trunks2 + region: region2 + type: trunk + validate: + predicate: all + condition: + - name: invalid-name + - task_id: check-no-networks1 + region: region1 + type: network + validate: + predicate: all + condition: + - name: invalid-name + - task_id: check-no-networks2 + region: region2 + type: network + validate: + predicate: all + condition: + - name: invalid-name + - task_id: check-jobs + region: central + type: job + validate: + predicate: all + retries: 10 + condition: + - status: SUCCESS diff --git a/tricircle/tests/network_sdk/__init__.py b/tricircle/tests/network_sdk/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tricircle/tests/network_sdk/network_service.py b/tricircle/tests/network_sdk/network_service.py new file mode 100644 index 00000000..28c20e4b --- /dev/null +++ b/tricircle/tests/network_sdk/network_service.py @@ -0,0 +1,24 @@ +# 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 openstack import service_filter + + +class NetworkService(service_filter.ServiceFilter): + """The network service.""" + + valid_versions = [service_filter.ValidVersion('v2', 'v2.0')] + + def __init__(self, version=None): + """Create a network service.""" + super(NetworkService, self).__init__(service_type='network', + version=version) diff --git a/tricircle/tests/network_sdk/v2/__init__.py b/tricircle/tests/network_sdk/v2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tricircle/tests/network_sdk/v2/_proxy.py b/tricircle/tests/network_sdk/v2/_proxy.py new file mode 100644 index 00000000..d3a8515a --- /dev/null +++ b/tricircle/tests/network_sdk/v2/_proxy.py @@ -0,0 +1,41 @@ +# 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 openstack.network.v2 import _proxy + +import tricircle.tests.network_sdk.v2.trunk as _trunk + + +class Proxy(_proxy.Proxy): + def create_trunk(self, **attrs): + return self._create(_trunk.Trunk, **attrs) + + def delete_trunk(self, trunk, ignore_missing=True): + self._delete(_trunk.Trunk, trunk, ignore_missing=ignore_missing) + + def update_trunk(self, trunk, **attrs): + return self._update(_trunk.Trunk, trunk, **attrs) + + def trunks(self, **query): + return self._list(_trunk.Trunk, pagination=False, **query) + + def add_subports(self, trunk, subports=[]): + trunk = self._get_resource(_trunk.Trunk, trunk) + body = {'sub_ports': subports} + return trunk.add_subports(self._session, **body) + + def remove_subports(self, trunk, subports=[]): + trunk = self._get_resource(_trunk.Trunk, trunk) + body = {'sub_ports': subports} + return trunk.remove_subports(self._session, **body) diff --git a/tricircle/tests/network_sdk/v2/trunk.py b/tricircle/tests/network_sdk/v2/trunk.py new file mode 100644 index 00000000..e136f4a0 --- /dev/null +++ b/tricircle/tests/network_sdk/v2/trunk.py @@ -0,0 +1,44 @@ +# 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 openstack import resource2 +from openstack import utils + +from tricircle.tests.network_sdk import network_service + + +class Trunk(resource2.Resource): + resource_key = 'trunk' + resources_key = 'trunks' + base_path = '/trunks' + service = network_service.NetworkService() + + allow_create = True + allow_get = True + allow_update = True + allow_delete = True + allow_list = True + + status = resource2.Body('status') + name = resource2.Body('name') + port_id = resource2.Body('port_id') + sub_ports = resource2.Body('sub_ports', type=list) + + def add_subports(self, session, **body): + url = utils.urljoin(self.base_path, self.id, 'add_subports') + resp = session.put(url, endpoint_filter=self.service, json=body) + return resp.json() + + def remove_subports(self, session, **body): + url = utils.urljoin(self.base_path, self.id, 'remove_subports') + resp = session.put(url, endpoint_filter=self.service, json=body) + return resp.json()