Merge branch 'v2.0' 67/2167/1 v3.0.0 v3.0.0rc v3.0.0rc16 v3.0.0rc17 v3.0.0rc2 v3.0.1
authortierno <alfonso.tiernosepulveda@telefonica.com>
Wed, 13 Sep 2017 12:10:56 +0000 (14:10 +0200)
committertierno <alfonso.tiernosepulveda@telefonica.com>
Wed, 13 Sep 2017 12:10:56 +0000 (14:10 +0200)
29 files changed:
Dockerfile
MANIFEST.in
Makefile
devops-stages/stage-build.sh
osm_openvim/host_thread.py
osm_openvim/ovim.py
osm_openvim/vim_schema.py
scripts/install-openvim.sh
setup.py
setup_lite.py
test/PY_TEST_README.rst [new file with mode: 0644]
test/__init__.py [new file with mode: 0644]
test/conftest.py [new file with mode: 0644]
test/fixtures/__init__.py [new file with mode: 0644]
test/fixtures/post/__init__.py [new file with mode: 0644]
test/fixtures/post/post_fixtures_delete.py [new file with mode: 0644]
test/fixtures/pre/__init__.py [new file with mode: 0644]
test/fixtures/pre/pre_fixtures_create.py [new file with mode: 0644]
test/flavors/cirros_flavor.yaml [new file with mode: 0644]
test/images/cirros_image.yaml [new file with mode: 0644]
test/lib/__init__.py [new file with mode: 0644]
test/lib/config_parser.py [new file with mode: 0644]
test/lib/ssh.py [new file with mode: 0644]
test/lib/test_utils.py [new file with mode: 0644]
test/servers/cirros_server_template.yaml [new file with mode: 0644]
test/tenants/test_tenant.yaml [new file with mode: 0644]
test/test_openvim_fake.yaml [new file with mode: 0644]
test/test_openvim_inf.py [new file with mode: 0644]
tox.ini [new file with mode: 0644]

index 2e05659..7dac9e8 100644 (file)
@@ -1,10 +1,8 @@
 FROM ubuntu:16.04
 
 RUN  apt-get update && \
-  DEBIAN_FRONTEND=noninteractive apt-get -y install git build-essential && \
-  DEBIAN_FRONTEND=noninteractive apt-get -y install python python-dev python-all python-stdeb fakeroot pypi2deb && \
-  DEBIAN_FRONTEND=noninteractive apt-get -y install python-pip libmysqlclient-dev libssl-dev libffi-dev libvirt-dev && \
-  DEBIAN_FRONTEND=noninteractive pip install --upgrade pip && \
-  DEBIAN_FRONTEND=noninteractive pip install --upgrade setuptools && \
-  DEBIAN_FRONTEND=noninteractive apt-get -y install python-argcomplete python-jsonschema python-logutils python-mysqldb python-paramiko python-requests python-yaml python-bottle python-libvirt
+  DEBIAN_FRONTEND=noninteractive apt-get -y install git make python python-pip debhelper && \
+  DEBIAN_FRONTEND=noninteractive pip install -U pip && \
+  DEBIAN_FRONTEND=noninteractive pip install -U setuptools setuptools-version-command stdeb
+
 
index 3a9e9a9..71cb3ec 100644 (file)
@@ -1,5 +1,5 @@
 include README.rst
-include OPENVIM_VERSION
+#include OPENVIM_VERSION
 include openflow
 include openvimd
 include openvim
index bae4fe1..553d349 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,25 +1,36 @@
 #!/usr/bin/env bash
+.PHONY: all test clean
+
 SHELL := /bin/bash
 
-all: clean build pip install
-lite: clean build_lite pip_lite install_lite
+all:
+       $(MAKE) clean_build build
+       $(MAKE) clean_build openvim
+       $(MAKE) clean_build build_lite
+       $(MAKE) clean_build lite
+
+openvim: package_openvim
+
+lite: package_lib_openvim
 
-clean_deb:
+clean: clean_build
        rm -rf .build
-clean:
+
+clean_build:
        rm -rf build
        find osm_openvim -name '*.pyc' -delete
        find osm_openvim -name '*.pyo' -delete
 
 prepare_lite:
-       pip install --user --upgrade setuptools
+       #pip install --user --upgrade setuptools
        mkdir -p build
-       VER1=$(shell git describe | sed -e 's/^v//' |cut -d- -f1); \
-       VER2=$(shell git describe | cut -d- -f2); \
-       VER3=$(shell git describe | cut -d- -f3); \
-       echo "$$VER1.dev$$VER2+$$VER3" > build/OVIM_VERSION
+       #VER1=$(shell git describe | sed -e 's/^v//' |cut -d- -f1); \
+       #VER2=$(shell git describe | cut -d- -f2); \
+       #VER3=$(shell git describe | cut -d- -f3); \
+       #echo "$$VER1.dev$$VER2+$$VER3" > build/OVIM_VERSION
+       cp tox.ini build/
        cp MANIFEST.in build/
-       sed -i "s/include OPENVIM_VERSION/include OVIM_VERSION/g" build/MANIFEST.in
+       #sed -i "s/include OPENVIM_VERSION/include OVIM_VERSION/g" build/MANIFEST.in
        sed -i "s/recursive-include osm_openvim/recursive-include lib_osm_openvim/g" build/MANIFEST.in
        sed -i "s/include openflow/include openflow-lib/g" build/MANIFEST.in
        sed -i '/include openvimd/d' build/MANIFEST.in
@@ -39,12 +50,13 @@ prepare_lite:
        sed -i "s/__import__(\"osm_openvim\.\"/__import__(\"lib_osm_openvim\.\"/g" build/lib_osm_openvim/ovim.py
 
 prepare:
-       pip install --user --upgrade setuptools
+       #pip install --user --upgrade setuptools
        mkdir -p build
-       VER1=$(shell git describe | sed -e 's/^v//' |cut -d- -f1); \
-       VER2=$(shell git describe | cut -d- -f2); \
-       VER3=$(shell git describe | cut -d- -f3); \
-       echo "$$VER1.dev$$VER2+$$VER3" > build/OPENVIM_VERSION
+       #VER1=$(shell git describe | sed -e 's/^v//' |cut -d- -f1); \
+       #VER2=$(shell git describe | cut -d- -f2); \
+       #VER3=$(shell git describe | cut -d- -f3); \
+       #echo "$$VER1.dev$$VER2+$$VER3" > build/OPENVIM_VERSION
+       cp tox.ini build/
        cp MANIFEST.in build/
        cp README.rst build/
        cp setup.py build/
@@ -61,17 +73,19 @@ prepare:
 
 build: prepare
        python -m py_compile build/osm_openvim/*.py
+       #cd build && tox -e flake8
 
 build_lite: prepare_lite
        python -m py_compile build/lib_osm_openvim/*.py
+       #cd build && tox -e flake8
 
-pip: clean build
+pip: build
        cd build && ./setup.py sdist
 
-pip_lite: clean build_lite
+pip_lite: build_lite
        cd build && ./setup.py sdist
 
-package_openvim: clean prepare
+package_openvim: prepare
        #apt-get install -y python-stdeb
        cd build && python setup.py --command-packages=stdeb.command sdist_dsc --with-python2=True
        cd build && cp osm_openvim/scripts/python-osm-openvim.postinst deb_dist/osm-openvim*/debian/
@@ -79,15 +93,14 @@ package_openvim: clean prepare
        mkdir -p .build
        cp build/deb_dist/python-*.deb .build/
 
-package_lib: clean prepare_lite
+package_lib_openvim: prepare_lite
        #apt-get install -y python-stdeb
-       cd build && python setup.py --command-packages=stdeb.command sdist_dsc --with-python2=True
-       cd build/deb_dist/lib-osm-openvim* && dpkg-buildpackage -rfakeroot -uc -us
+       #cd build && python setup.py --command-packages=stdeb.command sdist_dsc --with-python2=True
+       #cd build/deb_dist/lib-osm-openvim* && dpkg-buildpackage -rfakeroot -uc -us
+       cd build && python setup.py --command-packages=stdeb.command bdist_deb
        mkdir -p .build
        cp build/deb_dist/python-*.deb .build/
 
-package: clean_deb package_openvim package_lib
-
 snap:
        echo "Nothing to be done yet"
 
index 8505499..c38c275 100755 (executable)
@@ -1,2 +1,2 @@
 #!/bin/sh
-make package
+make clean all
index 368dcbc..78be1c9 100644 (file)
@@ -842,7 +842,7 @@ class host_thread(threading.Thread):
             command = 'sudo ip netns exec {} cat {}'.format(dhcp_namespace, pid_file)
             content = self.run_command(command, ignore_exit_status=True)
             dns_pid = content.replace('\n', '')
-            command = 'sudo ip netns exec {} kill -9 '.format(dhcp_namespace, dns_pid)
+            command = 'sudo ip netns exec {} kill -9 {}'.format(dhcp_namespace, dns_pid)
             self.run_command(command, ignore_exit_status=True)
 
         except RunCommandException as e:
@@ -952,7 +952,7 @@ class host_thread(threading.Thread):
             command = 'sudo ip link set dev ovim-{} down'.format(str(vlan))
             self.run_command(command)
 
-            command = 'sudo ifconfig {} down &&  sudo brctl delbr {}'.format(port_name, port_name)
+            command = 'sudo  ip link delete  {}  &&  sudo brctl delbr {}'.format(port_name, port_name)
             self.run_command(command)
             return True
         except RunCommandException as e:
@@ -975,14 +975,14 @@ class host_thread(threading.Thread):
 
             # Delete ovs veth pair
             command = 'sudo ip link set dev {} down'.format(br_ovs_name)
-            self.run_command(command)
+            self.run_command(command, ignore_exit_status=True)
 
             command = 'sudo ovs-vsctl del-port br-int {}'.format(br_ovs_name)
             self.run_command(command)
 
             # Delete br veth pair
             command = 'sudo ip link set dev {} down'.format(br_tap_name)
-            self.run_command(command)
+            self.run_command(command, ignore_exit_status=True)
 
             # Delete br veth interface form bridge
             command = 'sudo brctl delif {} {}'.format(link, br_tap_name)
@@ -990,7 +990,7 @@ class host_thread(threading.Thread):
 
             # Delete br veth pair
             command = 'sudo ip link set dev {} down'.format(link)
-            self.run_command(command)
+            self.run_command(command, ignore_exit_status=True)
 
             return True
         except RunCommandException as e:
@@ -1213,6 +1213,9 @@ class host_thread(threading.Thread):
             command = 'sudo ip link set dev {} down'.format(ovs_veth_name)
             self.run_command(command, ignore_exit_status=True)  # to end session
 
+            command = 'sudo ip link delete {} '.format(ovs_veth_name)
+            self.run_command(command, ignore_exit_status=True)
+
             command = 'sudo ip netns exec {} ip link set dev {} down'.format(dhcp_namespace, br_veth_name)
             self.run_command(command, ignore_exit_status=True)
 
@@ -1291,26 +1294,37 @@ class host_thread(threading.Thread):
             qrouter_ns_router_veth = '{}-vethQB'.format(str(vlan))
 
             command = 'sudo ovs-vsctl del-port br-int {}'.format(qrouter_ovs_veth)
-            self.run_command(command)
+            self.run_command(command, ignore_exit_status=True)
 
             # down ns veth
             command = 'sudo ip netns exec {} ip link set dev {} down'.format(ns_qouter, qrouter_ns_veth)
-            self.run_command(command)
+            self.run_command(command, ignore_exit_status=True)
+
+            command = 'sudo ip netns exec {}  ip link delete {} '.format(ns_qouter, qrouter_ns_veth)
+            self.run_command(command, ignore_exit_status=True)
 
             command = 'sudo ip netns del ' + ns_qouter
             self.run_command(command)
 
             # down ovs veth interface
             command = 'sudo ip link set dev {} down'.format(qrouter_br_veth)
-            self.run_command(command)
+            self.run_command(command, ignore_exit_status=True)
 
             # down br veth interface
             command = 'sudo ip link set dev {} down'.format(qrouter_ovs_veth)
-            self.run_command(command)
+            self.run_command(command, ignore_exit_status=True)
+
+            # delete  veth interface
+            command = 'sudo ip link delete {} '.format(link, qrouter_ovs_veth)
+            self.run_command(command, ignore_exit_status=True)
 
             # down br veth interface
             command = 'sudo ip link set dev {} down'.format(qrouter_ns_router_veth)
-            self.run_command(command)
+            self.run_command(command, ignore_exit_status=True)
+
+            # delete  veth interface
+            command = 'sudo ip link delete {} '.format(link, qrouter_ns_router_veth)
+            self.run_command(command, ignore_exit_status=True)
 
             # down br veth interface
             command = 'sudo brctl delif {} {}'.format(link, qrouter_br_veth)
@@ -1344,7 +1358,7 @@ class host_thread(threading.Thread):
 
             # Create pait veth
             command = 'sudo ip link add {} type veth peer name {}'.format(qrouter_ns_veth, qrouter_ovs_veth)
-            self.run_command(command)
+            self.run_command(command,  ignore_exit_status=True)
 
             # up ovs veth interface
             command = 'sudo ip link set dev {} up'.format(qrouter_ovs_veth)
index 5650cd5..53a39eb 100755 (executable)
@@ -43,8 +43,8 @@ import openflow_conn
 
 __author__ = "Alfonso Tierno, Leonardo Mirabal"
 __date__ = "$06-Feb-2017 12:07:15$"
-__version__ = "0.5.18-r534"
-version_date = "Jun 2017"
+__version__ = "0.5.20-r536"
+version_date = "Sep 2017"
 database_version = 21      #needed database schema version
 
 HTTP_Bad_Request =          400
@@ -301,13 +301,14 @@ class ovim():
                     links = net.get('links')
                     if links:
                         links = yaml.safe_load(net.get('links'))
-                    self.launch_dhcp_server(net.get('vlan'),
-                                            net.get('dhcp_first_ip'),
-                                            net.get('dhcp_last_ip'),
-                                            net.get('cidr'),
-                                            net.get('gateway_ip'),
-                                            dns,
-                                            routes)
+                    if net.get('enable_dhcp'):
+                        self.launch_dhcp_server(net.get('vlan'),
+                                                net.get('dhcp_first_ip'),
+                                                net.get('dhcp_last_ip'),
+                                                net.get('cidr'),
+                                                net.get('gateway_ip'),
+                                                dns,
+                                                routes)
                     self.launch_link_bridge_to_ovs(net['vlan'], net.get('gateway_ip'), net.get('cidr'), links, routes)
                     if net["status"] == "ERROR":
                         self.db.update_rows("nets", UPDATE={"status": "ACTIVE", "last_error": None},
@@ -671,11 +672,10 @@ class ovim():
             network['routes'] = yaml.safe_dump(network['routes'], default_flow_style=True, width=256)
 
         result, content = self.db.new_row('nets', network, True, True)
-
         if result >= 0 and dhcp_integrity:
             if bridge_net:
                 bridge_net[3] = content
-            if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge':
+            if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge': # \
                 if network["name"] in self.config["dhcp_server"].get("nets", ()):
                     self.config["dhcp_nets"].append(content)
                     self.logger.debug("dhcp_server: add new net", content)
@@ -784,6 +784,7 @@ class ovim():
         :param network_id:  network id
         :return:
         """
+        net_data = self.show_network(network_id)
 
         # delete from the data base
         result, content = self.db.delete_row('nets', network_id)
@@ -797,7 +798,18 @@ class ovim():
                     break
             if self.config.get("dhcp_server") and network_id in self.config["dhcp_nets"]:
                 self.config["dhcp_nets"].remove(network_id)
-            return content
+
+            if net_data.get('enable_dhcp'):
+                dhcp_path = self.config['ovs_controller_file_path']
+                dhcp_controller = self.get_dhcp_controller()
+                dhcp_controller.delete_dhcp_server(net_data['vlan'], network_id, dhcp_path)
+                dhcp_controller.delete_dhcp_port(net_data['vlan'], network_id, dhcp_path)
+                links = yaml.load(net_data.get('links'))
+                if links:
+                    links = yaml.load(net_data.get('links'))
+                    self.delete_link_bridge_to_ovs(net_data['vlan'], links)
+
+                return content
         else:
             raise ovimException("Error deleting network '{}': {}".format(network_id, content), -result)
 
@@ -1349,6 +1361,8 @@ class ovim():
                 map['switch_dpid'] = switch_dpid
             if region:
                 map['region'] = region
+            if map.get("pci"):
+                map["pci"] = map["pci"].lower()
 
         for of_map in of_maps:
             result, uuid = self.db.new_row('of_port_mappings', of_map, True)
index 9440872..697b4e9 100644 (file)
@@ -32,27 +32,36 @@ __date__ ="$10-jul-2014 12:07:15$"
 #
 
 path_schema = {"type": "string", "maxLength": 255, "pattern": "^(\.){0,2}(/[^/\"':{}\(\)]+)+$"}
-http_schema={"type":"string", "pattern":"^https?://[^'\"=]+$"}
-port_schema={"type":"integer","minimum":1,"maximun":65534}
-ip_schema={"type":"string","pattern":"^([0-9]{1,3}.){3}[0-9]{1,3}$"}
-cidr_schema={"type":"string","pattern":"^([0-9]{1,3}.){3}[0-9]{1,3}/[0-9]{1,2}$"}
-name_schema={"type" : "string", "minLength":1, "maxLength":255, "pattern" : "^[^,;()'\"]+$"}
-nameshort_schema={"type" : "string", "minLength":1, "maxLength":64, "pattern" : "^[^,;()'\"]+$"}
-nametiny_schema={"type" : "string", "minLength":1, "maxLength":12, "pattern" : "^[^,;()'\"]+$"}
-xml_text_schema={"type" : "string", "minLength":1, "maxLength":1000, "pattern" : "^[^']+$"}
-description_schema={"type" : ["string","null"], "maxLength":255, "pattern" : "^[^'\"]+$"}
-id_schema_fake = {"type" : "string", "minLength":2, "maxLength":36 }  #"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
-id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
-pci_schema={"type":"string", "pattern":"^[0-9a-fA-F]{4}(:[0-9a-fA-F]{2}){2}\.[0-9a-fA-F]$"}
-bandwidth_schema={"type":"string", "pattern" : "^[0-9]+ *([MG]bps)?$"}
-integer0_schema={"type":"integer","minimum":0}
-integer1_schema={"type":"integer","minimum":1}
-vlan_schema={"type":"integer","minimum":1,"maximun":4095}
-vlan1000_schema={"type":"integer","minimum":1000,"maximun":4095}
-mac_schema={"type":"string", "pattern":"^[0-9a-fA-F][02468aceACE](:[0-9a-fA-F]{2}){5}$"}  #must be unicast LSB bit of MSB byte ==0 
-net_bind_schema={"oneOf":[{"type":"null"},{"type":"string", "pattern":"^(default|((bridge|macvtap):[0-9a-zA-Z\.\-]{1,50})|openflow:[/0-9a-zA-Z\.\-]{1,50}(:vlan)?)$"}]}
-yes_no_schema={"type":"string", "enum":["yes", "no"]}
-log_level_schema={"type":"string", "enum":["DEBUG", "INFO", "WARNING","ERROR","CRITICAL"]}
+http_schema = {"type": "string", "pattern": "^https?://[^'\"=]+$"}
+port_schema = {"type": "integer","minimum": 1, "maximun": 65534}
+ip_schema = {"type": "string", "pattern": "^([0-9]{1,3}.){3}[0-9]{1,3}$"}
+cidr_schema = {"type": "string", "pattern": "^([0-9]{1,3}.){3}[0-9]{1,3}/[0-9]{1,2}$"}
+name_schema = {"type": "string", "minLength": 1, "maxLength": 255, "pattern": "^[^,;()'\"]+$"}
+nameshort_schema = {"type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[^,;()'\"]+$"}
+nametiny_schema = {"type": "string", "minLength": 1, "maxLength": 12, "pattern": "^[^,;()'\"]+$"}
+xml_text_schema = {"type": "string", "minLength": 1, "maxLength": 1000, "pattern": "^[^']+$"}
+description_schema = {"type": ["string", "null"], "maxLength": 255, "pattern": "^[^'\"]+$"}
+id_schema_fake = {"type": "string", "minLength": 2, "maxLength": 36}
+                  # "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
+id_schema = {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
+pci_schema = {"type": "string", "pattern": "^[0-9a-fA-F]{4}(:[0-9a-fA-F]{2}){2}\.[0-9a-fA-F]$"}
+# used by VIO neutron-port-show <host_moid>-<domain>:<bus>:<device>.<function>-<VF-id>
+pci_extended_schema = {"type": "string", "pattern": "^([0-9a-fA-F]{4}-)?[0-9a-fA-F]{4}(:[0-9a-fA-F]{2}){2}"
+                                                     "\.[0-9a-fA-F](-[0-9a-fA-F]{3})?$"}
+bandwidth_schema = {"type": "string", "pattern": "^[0-9]+ *([MG]bps)?$"}
+integer0_schema = {"type": "integer", "minimum": 0}
+integer1_schema = {"type": "integer", "minimum": 1}
+vlan_schema = {"type": "integer", "minimum": 1, "maximun": 4095}
+vlan1000_schema = {"type": "integer", "minimum": 1000, "maximun": 4095}
+mac_schema = {"type": "string", "pattern": "^[0-9a-fA-F][02468aceACE](:[0-9a-fA-F]{2}){5}$"}
+            # must be unicast LSB bit of MSB byte ==0
+net_bind_schema = {"oneOf": [
+    {"type": "null"},
+    {"type": "string", "pattern":
+        "^(default|((bridge|macvtap):[0-9a-zA-Z\.\-]{1,50})|openflow:[/0-9a-zA-Z\.\-]{1,50}(:vlan)?)$"}
+]}
+yes_no_schema = {"type": "string", "enum": ["yes", "no"]}
+log_level_schema = {"type": "string", "enum":["DEBUG", "INFO", "WARNING","ERROR","CRITICAL"]}
 
 config_schema = {
     "title": "main configuration information schema",
@@ -765,7 +774,7 @@ of_port_new_schema = {
         "ofc_id": id_schema,
         "region": nameshort_schema,
         "compute_node": nameshort_schema,
-        "pci": pci_schema,
+        "pci": pci_extended_schema,
         "switch_dpid": nameshort_schema,
         "switch_port": nameshort_schema,
         "switch_mac": mac_schema
index feaee4e..b25a6b0 100755 (executable)
@@ -232,8 +232,8 @@ then
         "#################################################################\n"\
         "#####        INSTALL PYTHON PACKAGES                        #####\n"\
         "#################################################################"
-    [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-yaml python-libvirt python-bottle python-mysqldb python-jsonschema python-paramiko python-argcomplete python-requests python-netaddr"
-    [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "PyYAML libvirt-python MySQL-python python-jsonschema python-paramiko python-argcomplete python-requests python-netaddr"
+    [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-yaml python-libvirt python-bottle python-mysqldb python-jsonschema python-paramiko python-argcomplete python-requests python-netaddr python-pexpect"
+    [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "PyYAML libvirt-python MySQL-python python-jsonschema python-paramiko python-argcomplete python-requests python-netaddr python-pexpect"
     # The only way to install python-bottle on Centos7 is with easy_install or pip
     [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && easy_install -U bottle
 
index 6856ff1..4d4073a 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,6 @@
 from setuptools import setup
 
 __name = 'osm_openvim'
-__version = open('OPENVIM_VERSION').read().strip()
 __description = 'OSM Openvim'
 __author = 'ETSI OSM'
 __author_email = 'alfonso.tiernosepulveda@telefonica.com'
@@ -13,7 +12,7 @@ __license = 'Apache 2.0'
 __url = 'https://osm.etsi.org/gitweb/?p=osm/openvim.git;a=summary'
 
 _req = [
-    "asn1crypto",
+    #"asn1crypto",
     "cffi",
     "enum34",
     "functools32",
@@ -34,9 +33,12 @@ _req = [
     "requestsexceptions",
     "netaddr",
     "bottle",
-    "MySQL-python",
+    #"MySQL-python",
+    #"mysqlclient",
     "paramiko",
-    "libvirt-python"
+    "libvirt-python",
+    "pytest",
+    "pexpect"
 ]
 
 __scripts__ = ['openflow',
@@ -50,7 +52,7 @@ __scripts__ = ['openflow',
                'osm_openvim/scripts/get_dhcp_lease.sh']
 
 setup(name=__name,
-      version=__version,
+      version_command=('git describe', 'pep440-git'),
       description=__description,
       long_description=__description,
       author=__author,
@@ -67,7 +69,8 @@ setup(name=__name,
       data_files = [('/etc/osm/', ['osm_openvim/openvimd.cfg']),
                    ('/etc/systemd/system/', ['osm_openvim/osm-openvim.service']),
                    ],
-      install_requires=_req
+      install_requires=_req,
+      setup_requires=['setuptools-version-command'],
       )
 
 
index 9d2b2ef..7bb1c79 100755 (executable)
@@ -3,7 +3,6 @@
 from setuptools import setup
 
 __name = 'lib_osm_openvim'
-__version = open('OVIM_VERSION').read().strip()
 __description = 'OSM Openvim library'
 __author = 'ETSI OSM'
 __author_email = 'alfonso.tiernosepulveda@telefonica.com'
@@ -13,7 +12,7 @@ __license = 'Apache 2.0'
 __url = 'https://osm.etsi.org/gitweb/?p=osm/openvim.git;a=summary'
 
 _req = [
-    "asn1crypto",
+    #"asn1crypto",
     "cffi",
     "enum34",
     "functools32",
@@ -34,14 +33,15 @@ _req = [
     "requestsexceptions",
     "netaddr",
     "bottle",
-    "MySQL-python",
-    "paramiko",
+    #"MySQL-python",
+    #"mysqlclient",
+    "paramiko"
 ]
 
 __scripts__ = ['openflow-lib']
 
 setup(name=__name,
-      version=__version,
+      version_command=('git describe', 'pep440-git'),
       description=__description,
       long_description=__description,
       author=__author,
@@ -55,7 +55,8 @@ setup(name=__name,
       scripts=__scripts__,
       package_data={'lib_osm_openvim': ['*']},
       include_package_data=True,
-      install_requires=_req
+      install_requires=_req,
+      setup_requires=['setuptools-version-command'],
       )
 
 
diff --git a/test/PY_TEST_README.rst b/test/PY_TEST_README.rst
new file mode 100644 (file)
index 0000000..2480844
--- /dev/null
@@ -0,0 +1,23 @@
+For launch openvim pytest regresion:
+
+    pytest -v -s test/test_openvim_inf.py --config=test/test_openvim_fake.yaml
+
+The regresion must be launch from an user included in the visudo.
+If the regression will be use real infrastructure (fake_mode=False) create a new yaml or modify
+the existing template -> test/test_openvim_fake.yaml
+
+
+test.yaml example:
+
+---
+host:
+   host_1: <path_to_host_descriptor_1>
+   host_2: <path_to_host_descriptor_1>
+   host_n: <path_to_host_descriptor_1>
+tenant: test/tenants/test_tenant.yaml
+flavor: test/flavors/cirros_flavor.yaml
+image: <path_to_iamge_descriptor>
+server: test/servers/cirros_server_template.yaml
+net: test/networks/net-example5.yaml
+fake_mode:  <True/False>                     # depend on openvimd.cfg mode (test,normal, host only, OF only, development)
+create_inf: <True/False>                     # Create host and mgmt net if True, if False host and mgmt net need to be precreated.
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644 (file)
index 0000000..2ad3e53
--- /dev/null
@@ -0,0 +1,6 @@
+import os
+
+def pytest_addoption(parser):
+    parser.addoption('--config',
+                     help='Indicate tests are run on CI server')
+    os.environ['OPENVIM_ROOT_FOLDER'] = os.getcwd()
diff --git a/test/fixtures/__init__.py b/test/fixtures/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/fixtures/post/__init__.py b/test/fixtures/post/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/fixtures/post/post_fixtures_delete.py b/test/fixtures/post/post_fixtures_delete.py
new file mode 100644 (file)
index 0000000..c2213bd
--- /dev/null
@@ -0,0 +1,126 @@
+import pytest
+import time
+from ...lib.test_utils import *
+
+
+@pytest.fixture(autouse=True)
+def post_install_service():
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    yield post_install_service
+    print "Stoping service openvim "
+    service_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'scripts', 'service-openvim')
+    execute_local("{} stop".format(service_path))
+
+
+@pytest.fixture()
+def post_delete_server(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_server
+
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+
+        if config['create_inf']:
+            vm_id = os.environ['OPENVIM_VM']
+            openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+            execute_local("{} vm-delete {}".format(openvim_path, vm_id))
+
+
+@pytest.fixture()
+def post_delete_net(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_net
+
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        if config['create_inf']:
+            net_id = os.environ['OPENVIM_TEST_MGMT_NET']
+            openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+            execute_local("{} net-delete -f {}".format(openvim_path, net_id))
+
+
+@pytest.fixture()
+def post_delete_vm():
+    """
+    Fixture to be executed after test
+    :return:
+    """
+    yield post_delete_vm
+    # destroy vm
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    out = execute_local('{} vm-delete -f'.format(openvim_path))
+
+
+@pytest.fixture()
+def post_delete_flavor():
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_flavor
+
+    flavor_id = os.environ['OPENVIM_TEST_FLAVOR']
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    execute_local("{} flavor-delete -f {}".format(openvim_path, flavor_id))
+
+
+@pytest.fixture()
+def post_delete_image():
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_image
+
+    img_id = os.environ['OPENVIM_TEST_IMAGE']
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    execute_local("{} image-delete -f {}".format(openvim_path, img_id))
+
+
+@pytest.fixture()
+def post_delete_host(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_host
+
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        if config['create_inf']:
+            host_ids = search_host_in_env_var()
+            for host_ids in host_ids:
+                openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+                execute_local("{} host-remove -f {}".format(openvim_path, os.environ[host_ids]))
+        time.sleep(30)
+
+
diff --git a/test/fixtures/pre/__init__.py b/test/fixtures/pre/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/fixtures/pre/pre_fixtures_create.py b/test/fixtures/pre/pre_fixtures_create.py
new file mode 100644 (file)
index 0000000..2ad733e
--- /dev/null
@@ -0,0 +1,168 @@
+import pytest
+import os
+from ...lib.ssh import *
+from ...lib.test_utils import *
+
+
+@pytest.fixture(autouse=True)
+def pre_launch_openvim_service():
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    service_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'scripts', 'service-openvim')
+
+    execute_local('{} stop'.format(service_path))
+    print "launching service openvim"
+    execute_local('{} start'.format(service_path))
+    out = execute_local('{} -h '.format(service_path))
+    assert out
+
+@pytest.fixture()
+def pre_create_flavor(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        flavor = config['flavor']
+
+        openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+
+        flavor_id = execute_local("{} flavor-create {}".format(openvim_path, flavor))
+        flavor_id = parse_uuid(flavor_id)
+        assert flavor_id
+        os.environ['OPENVIM_TEST_FLAVOR'] = flavor_id
+
+
+@pytest.fixture()
+def pre_create_host(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+
+        if config['create_inf']:
+            hosts = config['host']
+            counter = 0
+            for key, value in hosts.iteritems():
+                openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+
+                host_id = execute_local("{} host-add {}".format(openvim_path, value))
+                host_id = parse_uuid(host_id)
+                assert host_id
+                os.environ['OPENVIM_TEST_HOST_' + str(counter)] = host_id
+                counter += 1
+
+
+@pytest.fixture()
+def pre_create_image(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        img = config['image']
+
+        openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+        image_id = execute_local("{} image-create {}".format(openvim_path, img))
+        image_id = parse_uuid(image_id)
+        assert image_id
+        os.environ['OPENVIM_TEST_IMAGE'] = image_id
+
+
+@pytest.fixture()
+def pre_create_image(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        img = config['image']
+        openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+        image_id = execute_local("{} image-create {}".format(openvim_path, img))
+        image_id = parse_uuid(image_id)
+        assert image_id
+        os.environ['OPENVIM_TEST_IMAGE'] = image_id
+
+
+@pytest.fixture()
+def pre_create_net(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        if config['create_inf']:
+            net_yaml = config['net']
+            openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+            net_id = execute_local("{} net-create {}".format(openvim_path, net_yaml))
+            net_id = parse_uuid(net_id)
+            assert net_id
+
+            vlan = execute_local("{} net-list {} -vvv | grep provider:vlan:".format(openvim_path, net_id))
+            vlan = vlan.replace('provider:vlan:','')
+            vlan = vlan.replace(' ', '')
+            vlan = vlan.replace('\n', '')
+
+            os.environ['OPENVIM_TEST_MGMT_NET'] = net_id
+            os.environ['OPENVIM_TEST_MGMT_NET_VLAN'] = vlan
+
+
+@pytest.fixture()
+def pre_create_tenant(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param monkeypatch:
+    :return:
+    """
+
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        tenant_yaml = config['tenant']
+        openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+
+        tenant_id = execute_local("{} tenant-create {}".format(openvim_path, tenant_yaml))
+        tenant_id = parse_uuid(tenant_id)
+        assert tenant_id
+        os.environ['OPENVIM_TENANT'] = tenant_id
+
+
+@pytest.fixture()
+def pre_init_db(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        if config['create_inf']:
+            init_db_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'database_utils', 'init_vim_db.sh')
+            execute_local('{}'.format(init_db_path))
diff --git a/test/flavors/cirros_flavor.yaml b/test/flavors/cirros_flavor.yaml
new file mode 100644 (file)
index 0000000..aacb0cf
--- /dev/null
@@ -0,0 +1,67 @@
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openvim
+# 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.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact with: nfvlabs@tid.es
+##
+
+
+flavor:
+    name:        flavor-cirros
+    description: flavor-cirros
+    # cloud type requirements
+    ram:         1024                    # Memory in MB. Ignored if provided 'memory' at 'extended'
+    vcpus:       1                       # Number of cpus.  Ignored if provided at 'extended'
+
+    # NFV type requirements
+    # allocating EXCLUSIVE resoureces in the same NUMA node. 
+   # extended:                             # optional
+    #    processor_ranking: 100            # minimal processor family. Not used in current version
+    #    numas:                            # list of numa set. Only one supported in current version
+    #    -  memory:         2              # GByte of huge pages at this numa
+
+           #Choose among one of "cores", "paired-threads", "threads"
+           #paired-threads: 2              # Cores with two paired hyper threads
+           #paired-threads-id: [[0,1],[2,3],[4,5],[6,7],[8,9]] # Guess pinning. By default follows incremental order
+           #threads:       10             # threads awereness of the hyperthreading
+           ##threads-id: [0,1,2,3,4,5,6,7,8,9] #Optional. Guess pinning
+           #cores:         5              # Complete cores, without hyperthreading. VIM ensures the other paired thread is idle
+           ##cores-id:      [0,1,2,3,4]   # Optional. Guess pinning of cores
+
+           #Optional: Dataplane needed interfaces
+#           interfaces:
+#           -   name:       xe0            # Optional. User friendly name
+#               vpci:       "0000:00:10.0" # Optional. Guess PCI
+#               bandwidth:  10 Gbps        # Needed minimun bandwidth
+#               dedicated:  "yes"          # "yes"(passthrough), "no"(sriov with vlan tags), "yes:sriov"(sriovi, but exclusive and without vlan tag)
+#           -   name:       xe1
+#               vpci:       "0000:00:11.0"
+#               bandwidth:  10 Gbps
+#               dedicated:  "no"
+
+        #Optional: List of extra devices
+#        devices:                       # order determines device letter asignation (hda, hdb, ...)
+#        -   type:      disk            # "disk","cdrom","xml","usb"
+#            imageRef:  37598e34-ccb3-11e4-a996-52540030594e # UUID of an image, only for disk,cdrom,xml
+            # vpci:      "0000:00:03.0"   # Optional, not for disk or cdrom
+            # xml:     'Only for type xml: a XML described device xml text. Do not use single quotes inside
+            #        The following words, if found, will be replaced:
+            #        __file__    by image path, (imageiRef must be provided)
+            #        __format__  by qcow2 or raw (imageRef must be provided)
+            #        __dev__     by device letter (b, c, d ...)
+            #        __vpci__    by vpci (vpci must be provided)
+
diff --git a/test/images/cirros_image.yaml b/test/images/cirros_image.yaml
new file mode 100644 (file)
index 0000000..a771d55
--- /dev/null
@@ -0,0 +1,33 @@
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openvim
+# 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.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact with: nfvlabs@tid.es
+##
+
+
+image:
+    name:         image-cirros-test
+    description:  image-cirros
+    path:         /opt/VNF/images/cirros-0.3.5-x86_64-disk.img
+    metadata:     # Optional extra metadata of the image. All fields are optional
+        use_incremental: "yes"          # "yes" by default, "no" Deployed using a incremental qcow2 image
+        #vpci:            "0000:10:00.0" #requiered PCI at guess
+        os_distro:       cirros            # operating system distribution
+        os_type:         linux        # operating system type "linux" by default, "windows"
+        os_version:      "7"            # operating system version
+        bus:            "ide"           # By default "virtio" for linux, "ide" for windows
diff --git a/test/lib/__init__.py b/test/lib/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/lib/config_parser.py b/test/lib/config_parser.py
new file mode 100644 (file)
index 0000000..1981fce
--- /dev/null
@@ -0,0 +1,15 @@
+import yaml
+
+
+def get_config(file):
+    """
+    Parse test config file
+    :param file: 
+    :return: 
+    """
+    with open(file, 'r') as stream:
+        try:
+            return yaml.load(stream)
+        except yaml.YAMLError as exc:
+            print(exc)
+
diff --git a/test/lib/ssh.py b/test/lib/ssh.py
new file mode 100644 (file)
index 0000000..5480483
--- /dev/null
@@ -0,0 +1,126 @@
+import subprocess
+import pexpect
+
+
+def execute_local(cmd):
+    """
+    Execute a command in locally    
+    :param cmd: command to be executed
+    :return: 
+    """
+
+    print "execute_local cmd = {}".format(cmd)
+    p = subprocess.Popen(['bash', "-c", cmd], stdout=subprocess.PIPE)
+    out = p.stdout.read()
+    print "execute_local result = {}".format(out)
+    return out
+
+
+def execute_namespace(vlan, cmd):
+    """
+    Execute a command inside a namespace created by openvim 
+    :param vlan: namespace id
+    :param cmd: command to be executed
+    :return: 
+    """
+
+    n_cmd = 'sudo ip netns exec {}-qrouter {} '.format(vlan, cmd)
+    return execute_local(n_cmd)
+
+
+def ping_ok(vlan_id, ip, retries=200):
+    """
+
+    :param vlan_id: namespace id
+    :param ip: vm ip to be pinged
+    :param retries: retries, by default 200
+    :return: 
+    """
+    print 'waiting for vm to be active vm with ip = {}'.format(ip)
+    for i in xrange(retries):
+        try:
+            subprocess.check_output(['bash', "-c", "sudo ip netns exec {}-qrouter ping -c 1 {}".format(vlan_id, ip)])
+            return True
+        except Exception, e:
+            pass
+    return False
+
+
+def ping_ok_btw_2_vms(vlan_id, ip_1, ip_2, retires=8):
+    """
+    Check net connectivity between to VM
+    :param vlan_id: namepsace id
+    :param ip_1: first vm ip
+    :param ip_2: second vm ip
+    :param retires: 
+    :return: 
+    """
+    for i in xrange(retires):
+
+        try:
+            ns_cmd = 'sudo ip netns exec {}-qrouter '.format(vlan_id)
+            cmd = ns_cmd + ' ssh -oStrictHostKeyChecking=no cirros@{} "ping -c 1 {}"'.format(ip_1, ip_2)
+            child = pexpect.spawn(cmd)
+            child.expect('.*assword*')
+            child.sendline('cubswin:)')
+            child.sendline('cubswin:)')
+
+            cmd = ns_cmd + ' ssh -oStrictHostKeyChecking=no  cirros@{} "ping -c 1 {}"'.format(ip_2, ip_1)
+            child = pexpect.spawn(cmd)
+            child.expect('.*assword*')
+            child.sendline('cubswin:)')
+            child.sendline('cubswin:)')
+
+        except EOFError as e:
+            if i == retires:
+                return False
+            pass
+
+    return True
+
+
+def copy_rsa_keys_into_vm(vlan_id, ip, rsa_key_path):
+    """
+    copy an RSA key given by the user to a vm 
+    :param vlan_id: 
+    :param ip: 
+    :param rsa_key_path: 
+    :return: 
+    """
+
+    try:
+        execute_local('sudo ssh-keygen -f "/root/.ssh/known_hosts" -R  {}'.format(ip))
+        cmd = 'sudo ip netns exec {}-qrouter ssh-copy-id -i {} ' \
+              '-oStrictHostKeyChecking=no -f cirros@{}'.format(vlan_id, rsa_key_path + '.pub', ip)
+
+        print 'copy_rsa_keys_into_vm = ' + cmd
+        child = pexpect.spawn(cmd)
+        child.expect('.*assword*')
+        child.sendline('cubswin:)')
+        child.sendline('cubswin:)')
+        return True
+    except EOFError as e:
+        return False
+
+
+def execute_check_output(vlan_id, cmd):
+    """
+    Execute a command inside a namespace and raise an expection in case of command fail
+    :param vlan_id: namepsace id
+    :param cmd: command
+    :return: 
+    """
+    try:
+        cmd = "sudo ip netns exec {}-qrouter  {}".format(vlan_id, cmd)
+        print "execute_check_output = {}".format(cmd)
+        subprocess.check_output(['bash', "-c", cmd])
+        return True
+    except Exception, e:
+        print "error execute_check_output"
+        return False
+
+
+
+
+
+
diff --git a/test/lib/test_utils.py b/test/lib/test_utils.py
new file mode 100644 (file)
index 0000000..bf09899
--- /dev/null
@@ -0,0 +1,113 @@
+import yaml
+import re
+import os
+from string import Template
+from ssh import *
+
+
+def save_tmp_yaml(name, data):
+    """
+    Save a yaml file into a file to be declare in openvim
+    :param name: file name
+    :param data: 
+    :return: 
+    """
+    with open(name, "w") as text_file:
+        text_file.write(data)
+
+
+def delete_tmp_yaml(name):
+    """
+    Delete yaml form Filesystem
+    :param name: File name
+    :return: 
+    """
+    execute_local('rm {}'.format(name))
+
+
+def search_host_in_env_var():
+    """
+    Search for OPENVIM_TEST_HOST_X env var declare by pre_create_host fixture with the host id after creation. 
+    :return: All env vars founded
+    """
+    return search('OPENVIM_TEST_HOST_')
+
+
+def template_substitute(file_path, values):
+    """
+    Modify a Yaml template with values.
+    :param file_path: template file
+    :param values: values to be substituted
+    :return: a string with the file content modified
+    """
+    with open(file_path, 'r') as server_yaml:
+        template = Template(server_yaml.read())
+        server_yaml = template.safe_substitute(values)
+        return server_yaml
+
+
+def search(reg_ex):
+    """
+    Search for environment vars. 
+    :param reg_ex: regular expresion to be applied during the search
+    :return: return results
+    """
+    result = {}
+    for key in os.environ:
+        if reg_ex in key:
+            result[key] = os.environ[key]
+    return result
+
+
+def parse_uuid(data):
+    """
+    Parse an UUID value from a string given.
+    :param data: String to be evaluated
+    :return: the uuid value
+    """
+    match = re.compile(r'[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}', re.I).findall(data)
+    if match:
+        data = match[0].replace(' ', '')
+        data = data.replace('\n', '')
+        return data
+    else:
+        return []
+
+
+def get_config(data):
+    """
+    Parse test config file
+    :param data: config file path
+    :return: config dict 
+    """
+    with open(data, 'r') as stream:
+        try:
+            return yaml.load(stream)
+        except yaml.YAMLError as exc:
+            print(exc)
+
+
+def get_vm_ip(vm_id):
+    """
+    Parse vm id IP from openvim client.
+    :param vm_id: vm id
+    :return: IP value
+    """
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    ip = execute_local("{} vm-list {} -vvv | grep ip_address:".format(openvim_path, vm_id))
+    ip = ip.replace('ip_address:', '')
+    ip = ip.replace(' ', '')
+    return ip
+
+
+def get_net_status(net_id):
+    """
+    Parse a net status from openvim client
+    :param net_id: network id
+    :return: 
+    """
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    net_status = execute_local("{} net-list {} -vvv | grep status:".format(openvim_path, net_id))
+    net_status = net_status.replace('status:', '')
+    net_status = net_status.replace(' ', '')
+    return net_status
diff --git a/test/servers/cirros_server_template.yaml b/test/servers/cirros_server_template.yaml
new file mode 100644 (file)
index 0000000..1b1ae50
--- /dev/null
@@ -0,0 +1,35 @@
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openvim
+# 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.
+#
+##
+
+server:
+    name:         vm-test        # name
+    description:  vm-cirros  # Optional user description
+    imageRef:     $OPENVIM_TEST_IMAGE
+    flavorRef:    $OPENVIM_TEST_FLAVOR
+    networks:  # List of control plane interfaces, Optional
+    -   name:     mgmt         #friendly user name
+        #vpci:     "0000:00:0a.0" #Optional guess PCI
+        uuid:     $OPENVIM_TEST_MGMT_NET
+        #mac_address:  #guess concrete mac address, by default one is asigned
+        #model:        "virtio","e1000","ne2k_pci","pcnet","rtl8139", By default auto, normally virtio
+    start:    "yes"    # "yes","no","paused".  By default it is started upon creted
+    hostId:   $HOST_ID
+   
+    # allocating EXCLUSIVE resoureces in the same NUMA node. 
+    # If provided, it overrides extended values at flavor
diff --git a/test/tenants/test_tenant.yaml b/test/tenants/test_tenant.yaml
new file mode 100644 (file)
index 0000000..31ed219
--- /dev/null
@@ -0,0 +1,25 @@
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openvim
+# 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.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact with: nfvlabs@tid.es
+##
+
+
+tenant:
+    name: test-tenant
+    description:  tenant for testing purpouse
diff --git a/test/test_openvim_fake.yaml b/test/test_openvim_fake.yaml
new file mode 100644 (file)
index 0000000..a303a4c
--- /dev/null
@@ -0,0 +1,10 @@
+host:
+   host_1: test/hosts/host-example0.yaml
+   host_2: test/hosts/host-example1.yaml
+tenant: test/tenants/test_tenant.yaml
+flavor: test/flavors/cirros_flavor.yaml
+image: test/images/cirros_image.yaml
+server: test/servers/cirros_server_template.yaml
+net: test/networks/net-example5.yaml
+fake_mode: False
+create_inf: True
diff --git a/test/test_openvim_inf.py b/test/test_openvim_inf.py
new file mode 100644 (file)
index 0000000..8e9bd3a
--- /dev/null
@@ -0,0 +1,194 @@
+import pytest
+import time
+from fixtures.pre.pre_fixtures_create import *
+from fixtures.post.post_fixtures_delete import *
+from lib.ssh import *
+from lib.test_utils import *
+
+
+@pytest.mark.usefixtures('pre_init_db', 'pre_create_host', 'pre_create_tenant', 'pre_create_net', 'pre_create_image',
+                         'pre_create_flavor', 'post_delete_net', 'post_delete_host')
+def test_osm_01_create_vm(request, pre_init_db,
+                          pre_create_host,
+                          pre_create_tenant,
+                          pre_create_net,
+                          pre_create_image,
+                          pre_create_flavor,
+                          post_delete_image,
+                          post_delete_flavor,
+                          post_delete_vm,
+                          post_delete_net,
+                          post_delete_host):
+    """
+    Create a vm and check network connection btw qrouter namespace and vm, evaluate using ping againt the ip given by
+    ovs dhcp server. Openvim is launched as a service by pre_launch_openvim_service before regresion start, is stopped
+    by  post_stop_openvim_service after test regresion ends.
+
+    :param request: Users argument --config=<test yaml>
+    :param pre_init_db: initialize the openvim db
+    :param pre_create_host: Create hosts
+    :param pre_create_tenant: Create tenant
+    :param pre_create_net: Create a mgmt net
+    :param pre_create_image: Create an image
+    :param pre_create_flavor: Create a flavor
+    :param post_delete_image: Delete the image declare by the test
+    :param post_delete_flavor: Delete the flavor declare by the test
+    :param post_delete_vm: Delete the vm declare by the test
+    :param post_delete_net: Delete the mgmt net declare by the test
+    :param post_delete_host: remove the host attached by the test
+    :return:
+    """
+    config_path = request.config.getoption('config')
+    config = get_config(config_path)
+    if not config['fake_mode']:
+        vm_id = create_vm_per_host(config, 1)
+        assert vm_id
+        # get vlan id
+        vlan_id = os.environ['OPENVIM_TEST_MGMT_NET_VLAN']
+        assert vlan_id
+        # get vm ip
+        ip = get_vm_ip(vm_id)
+        assert ip
+        # check ping against the vm
+        result = ping_ok(vlan_id, ip)
+        assert result
+
+
+@pytest.mark.usefixtures('pre_init_db', 'pre_create_host', 'pre_create_tenant', 'pre_create_net', 'pre_create_image',
+                         'pre_create_flavor', 'post_delete_net','post_delete_host')
+def test_osm_02_create_2_vm_ping_btw(request, pre_init_db,
+                                     pre_create_host,
+                                     pre_create_tenant,
+                                     pre_create_net,
+                                     pre_create_image,
+                                     pre_create_flavor,
+                                     post_delete_flavor,
+                                     post_delete_image,
+                                     post_delete_vm,
+                                     post_delete_net,
+                                     post_delete_host):
+    """
+    Create 2 vms and check network connection btw qrouter namespace and vms, after ssh ready check ping btw both vms
+    to validate the vlxan mesh btw computes is ok. The vms ips are handle by ovs dhcp server. Openvim is launched as a
+    service by pre_launch_openvim_service before regresion start, is stopped by  post_stop_openvim_service after
+    test regresion ends.
+
+    :param request: Users argument --config=<test yaml>
+    :param pre_init_db: initialize the openvim db
+    :param pre_create_host: Create hosts
+    :param pre_create_tenant: Create tenant
+    :param pre_create_net: Create a mgmt net
+    :param pre_create_image: Create an image
+    :param pre_create_flavor: Create a flavor
+    :param post_delete_image: Delete the image declare by the test
+    :param post_delete_flavor: Delete the flavor declare by the test
+    :param post_delete_vm: Delete the vm declare by the test
+    :param post_delete_net: Delete the mgmt net declare by the test
+    :param post_delete_host: remove the host attached by the test
+    :return:
+    """
+    config_path = request.config.getoption('config')
+    config = get_config(config_path)
+
+    if not config['fake_mode']:
+        vm_id_1 = create_vm_per_host(config, 0)
+        assert vm_id_1
+
+        vm_id_2 = create_vm_per_host(config, 1)
+        assert vm_id_2
+
+        # get vlan id
+        vlan_id = os.environ['OPENVIM_TEST_MGMT_NET_VLAN']
+        assert vlan_id
+
+        # get vm ip
+        ip_1 = get_vm_ip(vm_id_1)
+        ip_2 = get_vm_ip(vm_id_2)
+        assert ip_2
+
+        # check ping against the vms
+        result = ping_ok(vlan_id, ip_1)
+        assert result
+        result = ping_ok(vlan_id, ip_2)
+        assert result
+
+        # Wait for ssh to be ready
+        print "Wait for ssh to be ready"
+        time.sleep(90)
+        result = ping_ok_btw_2_vms(vlan_id, ip_1, ip_2)
+        assert result
+
+
+@pytest.mark.usefixtures('pre_init_db', 'pre_create_host', 'pre_create_tenant', 'pre_create_net', 'pre_create_image',
+                         'pre_create_flavor', 'post_delete_net','post_delete_host')
+def test_osm_03_test_service_openvim(request, pre_init_db,
+                                     pre_create_host,
+                                     pre_create_tenant,
+                                     pre_create_net,
+                                     pre_create_image,
+                                     pre_create_flavor,
+                                     post_delete_flavor,
+                                     post_delete_image,
+                                     post_delete_net,
+                                     post_delete_host):
+    """
+    Create a net, restart openvim service and check net status to avoid issues with preexisting nets during openvim
+    startup. Openvim is launched as a service by pre_launch_openvim_service before regresion start, is stopped by
+    post_stop_openvim_service after test regresion ends.
+
+    :param request: Users argument --config=<test yaml>
+    :param pre_init_db: initialize the openvim db
+    :param pre_create_host: Create hosts
+    :param pre_create_tenant: Create tenant
+    :param pre_create_net: Create a mgmt net
+    :param pre_create_image: Create an image
+    :param pre_create_flavor: Create a flavor
+    :param post_delete_image: Delete the image declare by the test
+    :param post_delete_flavor: Delete the flavor declare by the test
+    :param post_delete_net: Delete the mgmt net declare by the test
+    :param post_delete_host: remove the host attached by the test
+    :return:
+    """
+
+    net_id = os.environ['OPENVIM_TEST_MGMT_NET']
+    status = get_net_status(net_id)
+    if 'ACTIVE' in status:
+        service_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'scripts', 'service-openvim')
+        execute_local('{} restart'.format(service_path))
+    else:
+        # force test fail if net status is not ACTIVE
+        assert None
+
+    status = get_net_status(net_id)
+    if 'ACTIVE' not in status:
+        assert None
+
+
+def create_vm_per_host(config, host_number=0):
+    """
+    Create a vm in an specific compute.
+    :param config: test config
+    :param host_number: compute number to be depolyed. 
+    :return: 
+    """
+    # get env var for server descriptor
+    tenant_id = os.environ['OPENVIM_TENANT']
+    image_id = os.environ['OPENVIM_TEST_IMAGE']
+    flavor_id = os.environ['OPENVIM_TEST_FLAVOR']
+    net_id = os.environ['OPENVIM_TEST_MGMT_NET']
+
+    values = {'OPENVIM_TENANT': tenant_id,
+              'OPENVIM_TEST_IMAGE': image_id,
+              'OPENVIM_TEST_FLAVOR': flavor_id,
+              'OPENVIM_TEST_MGMT_NET': net_id,
+              'HOST_ID': os.environ['OPENVIM_TEST_HOST_' + str(host_number)]
+              }
+
+    descriptor = template_substitute(config['server'], values)
+    save_tmp_yaml('tmp.yaml', descriptor)
+    # create vm
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    vm_id = execute_local("{} vm-create {}".format(openvim_path, 'tmp.yaml'))
+    vm_id = parse_uuid(vm_id)
+    delete_tmp_yaml('tmp.yaml')
+    return vm_id
diff --git a/tox.ini b/tox.ini
new file mode 100644 (file)
index 0000000..025f4a0
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,22 @@
+[tox]
+#envlist = py27,py3
+envlist = py27
+toxworkdir={homedir}/.tox
+
+[testenv]
+deps=nose
+     mock
+commands=nosetests
+
+[testenv:flake8]
+basepython = python
+deps = flake8
+commands =
+    flake8 setup.py
+
+[testenv:build]
+basepython = python
+deps = stdeb
+       setuptools-version-command
+commands = python setup.py --command-packages=stdeb.command bdist_deb
+