Merge branch 'v1.1' 80/1080/2
authortierno <alfonso.tiernosepulveda@telefonica.com>
Wed, 8 Feb 2017 13:21:28 +0000 (14:21 +0100)
committertierno <alfonso.tiernosepulveda@telefonica.com>
Wed, 8 Feb 2017 14:58:37 +0000 (15:58 +0100)
Change-Id: I0c27f01914959623bf25905786af9e7dabc141b6
Signed-off-by: tierno <alfonso.tiernosepulveda@telefonica.com>
17 files changed:
database_utils/migrate_vim_db.sh
host_thread.py
httpserver.py
onos.py [new file with mode: 0644]
openflow
openvimd.cfg
openvimd.py
scripts/configure-compute-node-RHEL7.2.sh
scripts/configure-compute-node-UBUNTU16.0.4.sh
scripts/configure-compute-node-develop-UBUNTU16.04.sh
scripts/configure-dhcp-server-UBUNTU16.0.4.sh [new file with mode: 0755]
scripts/initopenvim.sh
templates/network.yaml
test/networks/net-example4.yaml [new file with mode: 0644]
test/test_openflow.sh
vim_db.py
vim_schema.py

index 727a553..362edd3 100755 (executable)
@@ -175,8 +175,9 @@ DATABASE_TARGET_VER_NUM=0
 [ $OPENVIM_VER_NUM -ge 4001 ] && DATABASE_TARGET_VER_NUM=5   #0.4.1   =>  5
 [ $OPENVIM_VER_NUM -ge 4002 ] && DATABASE_TARGET_VER_NUM=6   #0.4.2   =>  6
 [ $OPENVIM_VER_NUM -ge 4005 ] && DATABASE_TARGET_VER_NUM=7   #0.4.5   =>  7
-[ $OPENVIM_VER_NUM -ge 4010 ] && DATABASE_TARGET_VER_NUM=8   #0.4.10   =>  8
+[ $OPENVIM_VER_NUM -ge 4010 ] && DATABASE_TARGET_VER_NUM=8   #0.4.10  =>  8
 [ $OPENVIM_VER_NUM -ge 5001 ] && DATABASE_TARGET_VER_NUM=9   #0.5.1   =>  9
+[ $OPENVIM_VER_NUM -ge 5002 ] && DATABASE_TARGET_VER_NUM=10  #0.5.2   => 10
 #TODO ... put next versions here
 
 
@@ -468,6 +469,18 @@ function downgrade_from_9(){
     echo "DELETE FROM schema_version WHERE version_int = '9';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
 }
 
+function upgrade_to_10(){
+    echo "    upgrade database from version 0.9 to version 0.10"
+    echo "     change types at 'ports'"
+    echo "ALTER TABLE ports CHANGE COLUMN type type ENUM('instance:bridge','instance:data','external','instance:ovs','controller:ovs') NOT NULL DEFAULT 'instance:bridge' AFTER status;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+    echo "INSERT INTO schema_version (version_int, version, openvim_ver, comments, date) VALUES (10, '0.10', '0.5.2', 'change ports type, adding instance:ovs', '2017-02-01');"| $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+}
+function downgrade_from_10(){
+    echo "    downgrade database from version 0.10 to version 0.9"
+    echo "     change back types at 'ports'"
+    echo "ALTER TABLE ports CHANGE COLUMN type type ENUM('instance:bridge','instance:data','external') NOT NULL DEFAULT 'instance:bridge' AFTER status;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+    echo "DELETE FROM schema_version WHERE version_int = '10';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+}
 #TODO ... put funtions here
 
 
index b9778af..4da5328 100644 (file)
@@ -25,9 +25,8 @@
 This is thread that interact with the host and the libvirt to manage VM
 One thread will be launched per host 
 '''
-__author__="Pablo Montes, Alfonso Tierno"
-__date__ ="$10-jul-2014 12:07:15$"
-
+__author__ = "Pablo Montes, Alfonso Tierno, Leonardo Mirabal"
+__date__ = "$10-jul-2014 12:07:15$"
 
 import json
 import yaml
@@ -40,17 +39,23 @@ from jsonschema import validate as js_v, exceptions as js_e
 import imp
 from vim_schema import localinfo_schema, hostinfo_schema
 import random
-#from logging import Logger
-#import auxiliary_functions as af
+import os
 
 #TODO: insert a logging system
 
+# from logging import Logger
+# import auxiliary_functions as af
+
+# TODO: insert a logging system
+
 
 class host_thread(threading.Thread):
-    lvirt_module = None  # libvirt module is charged only if not in test mode
-    def __init__(self, name, host, user, db, db_lock, test, image_path, host_id, version, develop_mode, develop_bridge_iface):
+    lvirt_module = None
+
+    def __init__(self, name, host, user, db, db_lock, test, image_path, host_id, version, develop_mode,
+                 develop_bridge_iface):
         '''Init a thread.
-        Arguments: 
+        Arguments:
             'id' number of thead
             'name' name of thread
             'host','user':  host ip or name to manage and user
@@ -63,7 +68,8 @@ class host_thread(threading.Thread):
         self.db = db
         self.db_lock = db_lock
         self.test = test
-        if not test and host_thread.lvirt_module == None:
+
+        if not test and not host_thread.lvirt_module:
             try:
                 module_info = imp.find_module("libvirt")
                 host_thread.lvirt_module = imp.load_module("libvirt", *module_info)
@@ -88,7 +94,8 @@ class host_thread(threading.Thread):
         
         self.queueLock = threading.Lock()
         self.taskQueue = Queue.Queue(2000)
-        
+        self.ssh_conn = None
+
     def ssh_connect(self):
         try:
             #Connect SSH
@@ -331,6 +338,27 @@ class host_thread(threading.Thread):
                 elif task[0] == 'restore-iface':
                     print self.name, ": processing task restore-iface %s mac=%s" % (task[1], task[2])
                     self.restore_iface(task[1], task[2])
+                elif task[0] == 'new-ovsbridge':
+                    print self.name, ": Creating compute OVS bridge"
+                    self.create_ovs_bridge()
+                    break
+                elif task[0] == 'new-vxlan':
+                    print self.name, ": Creating vxlan tunnel=" + task[1] + ", remote ip=" + task[2]
+                    self.create_ovs_vxlan_tunnel(task[1], task[2])
+                    break
+                elif task[0] == 'del-ovsbridge':
+                    print self.name, ": Deleting OVS bridge"
+                    self.delete_ovs_bridge()
+                    break
+                elif task[0] == 'del-vxlan':
+                    print self.name, ": Deleting vxlan " + task[1] + " tunnel"
+                    self.delete_ovs_vxlan_tunnel(task[1])
+                    break
+                elif task[0] == 'create-ovs-bridge-port':
+                    print self.name, ": Adding port ovim-" + task[1] + " to OVS bridge"
+                    self.create_ovs_bridge_port(task[1])
+                elif task[0] == 'del-ovs-port':
+                    self.delete_bridge_port_attached_to_ovs(task[1], task[2])
                 else:
                     print self.name, ": unknown task", task
                 
@@ -589,6 +617,10 @@ class host_thread(threading.Thread):
                         self.tab() + "<alias name='net" + str(net_nb)+ "'/>"
                 elif model==None:
                     model = "virtio"
+            elif content[0]['provider'][0:3] == "OVS":
+                vlan = content[0]['provider'].replace('OVS:', '')
+                text += self.tab() + "<interface type='bridge'>" + \
+                        self.inc_tab() + "<source bridge='ovim-" + vlan + "'/>"
             else:
                 return -1, 'Unknown Bridge net provider ' + content[0]['provider']
             if model!=None:
@@ -677,7 +709,517 @@ class host_thread(threading.Thread):
         """Decrement and return indentation according to xml_level"""
         self.xml_level -= 1
         return self.tab()
-    
+
+    def create_ovs_bridge(self):
+        """
+        Create a bridge in compute OVS to allocate VMs
+        :return: True if success
+        """
+        if self.test:
+            return
+        command = 'sudo ovs-vsctl --may-exist add-br br-int -- set Bridge br-int stp_enable=true'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_port_to_ovs_bridge(self, vlan, net_uuid):
+        """
+        Delete linux bridge port attched to a OVS bridge, if port is not free the port is not removed
+        :param vlan: vlan port id
+        :param net_uuid: network id
+        :return:
+        """
+
+        if self.test:
+            return
+
+        port_name = 'ovim-' + vlan
+        command = 'sudo ovs-vsctl del-port br-int ' + port_name
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_dhcp_server(self, vlan, net_uuid, dhcp_path):
+        """
+        Delete dhcp server process lining in namespace
+        :param vlan: segmentation id
+        :param net_uuid: network uuid
+        :param dhcp_path: conf fiel path that live in namespace side
+        :return:
+        """
+        if self.test:
+            return
+        if not self.is_dhcp_port_free(vlan, net_uuid):
+            return True
+
+        net_namespace = 'ovim-' + vlan
+        dhcp_path = os.path.join(dhcp_path, net_namespace)
+        pid_file = os.path.join(dhcp_path, 'dnsmasq.pid')
+
+        command = 'sudo ip netns exec ' + net_namespace + ' cat ' + pid_file
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo ip netns exec ' + net_namespace + ' kill -9 ' + content
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # if len(content) == 0:
+        #     return True
+        # else:
+        #     return False
+
+    def is_dhcp_port_free(self, host_id, net_uuid):
+        """
+        Check if any port attached to the a net in a vxlan mesh across computes nodes
+        :param host_id: host id
+        :param net_uuid: network id
+        :return: True if is not free
+        """
+        self.db_lock.acquire()
+        result, content = self.db.get_table(
+            FROM='ports',
+            WHERE={'p.type': 'instance:ovs', 'p.net_id': net_uuid}
+        )
+        self.db_lock.release()
+
+        if len(content) > 0:
+            return False
+        else:
+            return True
+
+    def is_port_free(self, host_id, net_uuid):
+        """
+        Check if there not ovs ports of a network in a compute host.
+        :param host_id:  host id
+        :param net_uuid: network id
+        :return: True if is not free
+        """
+
+        self.db_lock.acquire()
+        result, content = self.db.get_table(
+            FROM='ports as p join instances as i on p.instance_id=i.uuid',
+            WHERE={"i.host_id": self.host_id, 'p.type': 'instance:ovs', 'p.net_id': net_uuid}
+        )
+        self.db_lock.release()
+
+        if len(content) > 0:
+            return False
+        else:
+            return True
+
+    def add_port_to_ovs_bridge(self, vlan):
+        """
+        Add a bridge linux as a port to a OVS bridge and set a vlan for an specific linux bridge
+        :param vlan: vlan port id
+        :return: True if success
+        """
+
+        if self.test:
+            return
+
+        port_name = 'ovim-' + vlan
+        command = 'sudo ovs-vsctl add-port br-int ' + port_name + ' tag=' + vlan
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_dhcp_port(self, vlan, net_uuid):
+        """
+        Delete from an existing OVS bridge a linux bridge port attached and the linux bridge itself.
+        :param vlan: segmentation id
+        :param net_uuid: network id
+        :return: True if success
+        """
+
+        if self.test:
+            return
+
+        if not self.is_dhcp_port_free(vlan, net_uuid):
+            return True
+        self.delete_dhcp_interfaces(vlan)
+        return True
+
+    def delete_bridge_port_attached_to_ovs(self, vlan, net_uuid):
+        """
+        Delete from an existing OVS bridge a linux bridge port attached and the linux bridge itself.
+        :param vlan:
+        :param net_uuid:
+        :return: True if success
+        """
+        if self.test:
+            return
+
+        if not self.is_port_free(vlan, net_uuid):
+            return True
+        self.delete_port_to_ovs_bridge(vlan, net_uuid)
+        self.delete_linux_bridge(vlan)
+        return True
+
+    def delete_linux_bridge(self, vlan):
+        """
+        Delete a linux bridge in a scpecific compute.
+        :param vlan: vlan port id
+        :return: True if success
+        """
+
+        if self.test:
+            return
+
+        port_name = 'ovim-' + vlan
+        command = 'sudo ip link set dev veth0-' + vlan + ' down'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        #
+        # if len(content) != 0:
+        #     return False
+
+        command = 'sudo ifconfig ' + port_name + ' down &&  sudo brctl delbr ' + port_name
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def create_ovs_bridge_port(self, vlan):
+        """
+        Generate a linux bridge and attache the port to a OVS bridge
+        :param vlan: vlan port id
+        :return:
+        """
+        if self.test:
+            return
+        self.create_linux_bridge(vlan)
+        self.add_port_to_ovs_bridge(vlan)
+
+    def create_linux_bridge(self, vlan):
+        """
+        Create a linux bridge with STP active
+        :param vlan: netowrk vlan id
+        :return:
+        """
+
+        if self.test:
+            return
+
+        port_name = 'ovim-' + vlan
+        command = 'sudo brctl show | grep ' + port_name
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # if exist nothing to create
+        # if len(content) == 0:
+        #     return False
+
+        command = 'sudo brctl addbr ' + port_name
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # if len(content) == 0:
+        #     return True
+        # else:
+        #     return False
+
+        command = 'sudo brctl stp ' + port_name + ' on'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # if len(content) == 0:
+        #     return True
+        # else:
+        #     return False
+        command = 'sudo ip link set dev ' + port_name + ' up'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def set_mac_dhcp_server(self, ip, mac, vlan, netmask, dhcp_path):
+        """
+        Write into dhcp conf file a rule to assigned a fixed ip given to an specific MAC address
+        :param ip: IP address asigned to a VM
+        :param mac: VM vnic mac to be macthed with the IP received
+        :param vlan: Segmentation id
+        :param netmask: netmask value
+        :param path: dhcp conf file path that live in namespace side
+        :return: True if success
+        """
+
+        if self.test:
+            return
+
+        net_namespace = 'ovim-' + vlan
+        dhcp_path = os.path.join(dhcp_path, net_namespace)
+        dhcp_hostsdir = os.path.join(dhcp_path, net_namespace)
+
+        if not ip:
+            return False
+
+        ip_data = mac.upper() + ',' + ip
+
+        command = 'sudo  ip netns exec ' + net_namespace + ' touch ' + dhcp_hostsdir
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo  ip netns exec ' + net_namespace + ' sudo bash -ec "echo ' + ip_data + ' >> ' + dhcp_hostsdir + '"'
+
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_mac_dhcp_server(self, ip, mac, vlan, dhcp_path):
+        """
+        Delete into dhcp conf file the ip  assigned to a specific MAC address
+
+        :param ip: IP address asigned to a VM
+        :param mac:  VM vnic mac to be macthed with the IP received
+        :param vlan:  Segmentation id
+        :param dhcp_path: dhcp conf file path that live in namespace side
+        :return:
+        """
+
+        if self.test:
+            return
+
+        net_namespace = 'ovim-' + vlan
+        dhcp_path = os.path.join(dhcp_path, net_namespace)
+        dhcp_hostsdir = os.path.join(dhcp_path, net_namespace)
+
+        if not ip:
+            return False
+
+        ip_data = mac.upper() + ',' + ip
+
+        command = 'sudo  ip netns exec ' + net_namespace + ' sudo sed -i \'/' + ip_data + '/d\' ' + dhcp_hostsdir
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def launch_dhcp_server(self, vlan, ip_range, netmask, dhcp_path):
+        """
+        Generate a linux bridge and attache the port to a OVS bridge
+        :param self:
+        :param vlan: Segmentation id
+        :param ip_range: IP dhcp range
+        :param netmask: network netmask
+        :param dhcp_path: dhcp conf file path that live in namespace side
+        :return: True if success
+        """
+
+        if self.test:
+            return
+
+        interface = 'tap-' + vlan
+        net_namespace = 'ovim-' + vlan
+        dhcp_path = os.path.join(dhcp_path, net_namespace)
+        leases_path = os.path.join(dhcp_path, "dnsmasq.leases")
+        pid_file = os.path.join(dhcp_path, 'dnsmasq.pid')
+
+        dhcp_range = ip_range[0] + ',' + ip_range[1] + ',' + netmask
+
+        command = 'sudo ip netns exec ' + net_namespace + ' mkdir -p ' + dhcp_path
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        pid_path = os.path.join(dhcp_path, 'dnsmasq.pid')
+        command = 'sudo  ip netns exec ' + net_namespace + ' cat ' + pid_path
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        # check if pid is runing
+        pid_status_path = content
+        if content:
+            command = "ps aux | awk '{print $2 }' | grep " + pid_status_path
+            print self.name, ': command:', command
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+        if not content:
+            command = 'sudo  ip netns exec ' + net_namespace + ' /usr/sbin/dnsmasq --strict-order --except-interface=lo ' \
+              '--interface=' + interface + ' --bind-interfaces --dhcp-hostsdir=' + dhcp_path + \
+              ' --dhcp-range ' + dhcp_range + ' --pid-file=' + pid_file + ' --dhcp-leasefile=' + leases_path + '  --listen-address ' + ip_range[0]
+
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.readline()
+
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_dhcp_interfaces(self, vlan):
+        """
+        Create a linux bridge with STP active
+        :param vlan: netowrk vlan id
+        :return:
+        """
+
+        if self.test:
+            return
+
+        net_namespace = 'ovim-' + vlan
+        command = 'sudo ovs-vsctl del-port br-int ovs-tap-' + vlan
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo ip netns exec ' + net_namespace + ' ip link set dev tap-' + vlan + ' down'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo ip link set dev ovs-tap-' + vlan + ' down'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+    def create_dhcp_interfaces(self, vlan, ip, netmask):
+        """
+        Create a linux bridge with STP active
+        :param vlan: segmentation id
+        :param ip: Ip included in the dhcp range for the tap interface living in namesapce side
+        :param netmask: dhcp net CIDR
+        :return: True if success
+        """
+
+        if self.test:
+            return
+
+        net_namespace = 'ovim-' + vlan
+        namespace_interface = 'tap-' + vlan
+
+        command = 'sudo ip netns add ' + net_namespace
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo ip link add tap-' + vlan + ' type veth peer name ovs-tap-' + vlan
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo ovs-vsctl add-port br-int ovs-tap-' + vlan + ' tag=' + vlan
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo ip link set tap-' + vlan + ' netns ' + net_namespace
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo ip netns exec ' + net_namespace + ' ip link set dev tap-' + vlan + ' up'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo ip link set dev ovs-tap-' + vlan + ' up'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo  ip netns exec ' + net_namespace + ' ' + ' ifconfig  ' + namespace_interface \
+                  + ' ' + ip + ' netmask ' + netmask
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def create_ovs_vxlan_tunnel(self, vxlan_interface, remote_ip):
+        """
+        Create a vlxn tunnel between to computes with an OVS installed. STP is also active at port level
+        :param vxlan_interface: vlxan inteface name.
+        :param remote_ip: tunnel endpoint remote compute ip.
+        :return:
+        """
+        if self.test:
+            return
+        command = 'sudo ovs-vsctl add-port br-int ' + vxlan_interface + \
+                  ' -- set Interface ' + vxlan_interface + '  type=vxlan options:remote_ip=' + remote_ip + \
+                  ' -- set Port ' + vxlan_interface + ' other_config:stp-path-cost=10'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        print content
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_ovs_vxlan_tunnel(self, vxlan_interface):
+        """
+        Delete a vlxan tunnel  port from a OVS brdige.
+        :param vxlan_interface: vlxan name to be delete it.
+        :return: True if success.
+        """
+        if self.test:
+            return
+        command = 'sudo ovs-vsctl del-port br-int ' + vxlan_interface
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        print content
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_ovs_bridge(self):
+        """
+        Delete a OVS bridge from  a compute.
+        :return: True if success
+        """
+        if self.test:
+            return
+        command = 'sudo ovs-vsctl del-br br-int'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
     def get_file_info(self, path):
         command = 'ls -lL --time-style=+%Y-%m-%dT%H:%M:%S ' + path
         print self.name, ': command:', command
@@ -895,7 +1437,8 @@ class host_thread(threading.Thread):
                     continue
                 
                 self.db_lock.acquire()
-                result, content = self.db.get_table(FROM='images', SELECT=('path','metadata'),WHERE={'uuid':dev['image_id']} )
+                result, content = self.db.get_table(FROM='images', SELECT=('path', 'metadata'),
+                                                    WHERE={'uuid': dev['image_id']})
                 self.db_lock.release()
                 if result <= 0:
                     error_text = "ERROR", result, content, "when getting image", dev['image_id']
@@ -1000,7 +1543,7 @@ class host_thread(threading.Thread):
                 else:
                     new_status = None
                 domain_dict[uuid] = new_status
-            conn.close
+            conn.close()
         except host_thread.lvirt_module.libvirtError as e:
             print self.name, ": get_state() Exception '", e.get_error_message()
             return
@@ -1152,8 +1695,8 @@ class host_thread(threading.Thread):
                     else:
                         new_status = 'ACTIVE'
                 elif 'start' in req['action']:
-                    #La instancia está sólo en la base de datos pero no en la libvirt. es necesario crearla
-                    rebuild = True if req['action']['start']=='rebuild'  else False
+                    # The instance is only create in DB but not yet at libvirt domain, needs to be create
+                    rebuild = True if req['action']['start'] == 'rebuild'  else False
                     r = self.launch_server(conn, req, rebuild, dom)
                     if r[0] <0:
                         new_status = 'ERROR'
@@ -1197,7 +1740,7 @@ class host_thread(threading.Thread):
         
                 conn.close()    
             except host_thread.lvirt_module.libvirtError as e:
-                if conn is not None: conn.close
+                if conn is not None: conn.close()
                 text = e.get_error_message()
                 new_status = "ERROR"
                 last_error = text
@@ -1270,7 +1813,7 @@ class host_thread(threading.Thread):
             ret=-1
         finally:
             if lib_conn is None and conn is not None:
-                conn.close
+                conn.close()
         return ret, error_text
 
         
@@ -1374,9 +1917,9 @@ class host_thread(threading.Thread):
                 print self.name, ": edit_iface(",port["instance_id"],") libvirt exception:", text 
                 
             finally:
-                if conn is not None: conn.close
-                            
-                              
+                if conn is not None: conn.close()
+
+
 def create_server(server, db, db_lock, only_of_ports):
     #print "server"
     #print "server"
@@ -1643,7 +2186,10 @@ def create_server(server, db, db_lock, only_of_ports):
         control_iface['net_id']=control_iface.pop('uuid')
         #Get the brifge name
         db_lock.acquire()
-        result, content = db.get_table(FROM='nets', SELECT=('name','type', 'vlan'),WHERE={'uuid':control_iface['net_id']} )
+        result, content = db.get_table(FROM='nets',
+                                       SELECT=('name', 'type', 'vlan', 'provider', 'enable_dhcp',
+                                                 'dhcp_first_ip', 'dhcp_last_ip', 'cidr'),
+                                       WHERE={'uuid': control_iface['net_id']})
         db_lock.release()
         if result < 0: 
             pass
@@ -1655,6 +2201,18 @@ def create_server(server, db, db_lock, only_of_ports):
                 if network['type']!='bridge_data' and network['type']!='bridge_man':
                     return -1, "Error at field netwoks: network uuid %s for control interface is not of type bridge_man or bridge_data" % control_iface['net_id']
                 resources['bridged-ifaces'].append(control_iface)
+                if network.get("provider") and network["provider"][0:3] == "OVS":
+                    control_iface["type"] = "instance:ovs"
+                else:
+                    control_iface["type"] = "instance:bridge"
+                if network.get("vlan"):
+                    control_iface["vlan"] = network["vlan"]
+
+                if network.get("enable_dhcp") == 'true':
+                    control_iface["enable_dhcp"] = network.get("enable_dhcp")
+                    control_iface["dhcp_first_ip"] = network["dhcp_first_ip"]
+                    control_iface["dhcp_last_ip"] = network["dhcp_last_ip"]
+                    control_iface["cidr"] = network["cidr"]
             else:
                 if network['type']!='data' and network['type']!='ptp':
                     return -1, "Error at field netwoks: network uuid %s for dataplane interface is not of type data or ptp" % control_iface['net_id']
index 08ea403..1237fb4 100644 (file)
@@ -26,7 +26,7 @@ This is the thread for the http server North API.
 Two thread will be launched, with normal and administrative permissions.
 '''
 
-__author__="Alfonso Tierno, Gerardo Garcia"
+__author__="Alfonso Tierno, Gerardo Garcia, Leonardo Mirabal"
 __date__ ="$10-jul-2014 12:07:15$"
 
 import bottle
@@ -38,6 +38,7 @@ import datetime
 import hashlib
 import os
 import imp
+from netaddr import IPNetwork, IPAddress, all_matching_cidrs
 #import only if needed because not needed in test mode. To allow an easier installation   import RADclass
 from jsonschema import validate as js_v, exceptions as js_e
 import host_thread as ht
@@ -497,8 +498,12 @@ def enable_cors():
 
 @bottle.route(url_base + '/hosts', method='GET')
 def http_get_hosts():
-    select_,where_,limit_ = filter_query_string(bottle.request.query, http2db_host,
-            ('id','name','description','status','admin_state_up') )
+    return format_out(get_hosts())
+
+
+def get_hosts():
+    select_, where_, limit_ = filter_query_string(bottle.request.query, http2db_host,
+                                                  ('id', 'name', 'description', 'status', 'admin_state_up', 'ip_name'))
     
     myself = config_dic['http_threads'][ threading.current_thread().name ]
     result, content = myself.db.get_table(FROM='hosts', SELECT=select_, WHERE=where_, LIMIT=limit_)
@@ -511,7 +516,7 @@ def http_get_hosts():
         for row in content:
             row['links'] = ( {'href': myself.url_preffix + '/hosts/' + str(row['id']), 'rel': 'bookmark'}, )
         data={'hosts' : content}
-        return format_out(data)
+        return data
 
 @bottle.route(url_base + '/hosts/<host_id>', method='GET')
 def http_get_host_id(host_id):
@@ -633,6 +638,13 @@ def http_post_hosts():
             thread.start()
             config_dic['host_threads'][ content['uuid'] ] = thread
 
+            if config_dic['network_type'] == 'ovs':
+                # create bridge
+                create_dhcp_ovs_bridge()
+                config_dic['host_threads'][content['uuid']].insert_task("new-ovsbridge")
+                # check if more host exist
+                create_vxlan_mesh(content['uuid'])
+
         #return host data
         change_keys_http2db(content, http2db_host, reverse=True)
         if len(warning_text)>0:
@@ -643,6 +655,180 @@ def http_post_hosts():
         bottle.abort(HTTP_Bad_Request, content)
         return
 
+
+def get_dhcp_controller():
+    """
+    Create an host_thread object for manage openvim controller and not create a thread for itself
+    :return: dhcp_host openvim controller object
+    """
+
+    if 'openvim_controller' in config_dic['host_threads']:
+        return config_dic['host_threads']['openvim_controller']
+
+    bridge_ifaces = []
+    controller_ip = config_dic['ovs_controller_ip']
+    ovs_controller_user = config_dic['ovs_controller_user']
+
+    host_test_mode = True if config_dic['mode'] == 'test' or config_dic['mode'] == "OF only" else False
+    host_develop_mode = True if config_dic['mode'] == 'development' else False
+
+    dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip, db=config_dic['db'],
+                               db_lock=config_dic['db_lock'], test=host_test_mode,
+                               image_path=config_dic['image_path'], version=config_dic['version'],
+                               host_id='openvim_controller', develop_mode=host_develop_mode,
+                               develop_bridge_iface=bridge_ifaces)
+
+    config_dic['host_threads']['openvim_controller'] = dhcp_host
+    dhcp_host.ssh_connect()
+    return dhcp_host
+
+
+def delete_dhcp_ovs_bridge(vlan, net_uuid):
+    """
+    Delete bridges and port created during dhcp launching at openvim controller
+    :param vlan: net vlan id
+    :param net_uuid: network identifier
+    :return:
+    """
+    dhcp_path = config_dic['ovs_controller_file_path']
+
+    controller_host = get_dhcp_controller()
+    controller_host.delete_dhcp_port(vlan, net_uuid)
+    controller_host.delete_dhcp_server(vlan, net_uuid, dhcp_path)
+
+
+def create_dhcp_ovs_bridge():
+    """
+    Initialize bridge to allocate the dhcp server at openvim controller
+    :return:
+    """
+    controller_host = get_dhcp_controller()
+    controller_host.create_ovs_bridge()
+
+
+def set_mac_dhcp(vm_ip, vlan, first_ip, last_ip, cidr, mac):
+    """"
+    Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
+    :param vm_ip: IP address asigned to a VM
+    :param vlan: Segmentation id
+    :param first_ip: First dhcp range ip
+    :param last_ip: Last dhcp range ip
+    :param cidr: net cidr
+    :param mac: VM vnic mac to be macthed with the IP received
+    """
+    if not vm_ip:
+        return
+    ip_tools = IPNetwork(cidr)
+    cidr_len = ip_tools.prefixlen
+    dhcp_netmask = str(ip_tools.netmask)
+    dhcp_path = config_dic['ovs_controller_file_path']
+
+    new_cidr = [first_ip + '/' + str(cidr_len)]
+    if not len(all_matching_cidrs(vm_ip, new_cidr)):
+        vm_ip = None
+
+    controller_host = get_dhcp_controller()
+    controller_host.set_mac_dhcp_server(vm_ip, mac, vlan, dhcp_netmask, dhcp_path)
+
+
+def delete_mac_dhcp(vm_ip, vlan, mac):
+    """
+    Delete into dhcp conf file the ip  assigned to a specific MAC address
+    :param vm_ip: IP address asigned to a VM
+    :param vlan: Segmentation id
+    :param mac:  VM vnic mac to be macthed with the IP received
+    :return:
+    """
+
+    dhcp_path = config_dic['ovs_controller_file_path']
+
+    controller_host = get_dhcp_controller()
+    controller_host.delete_mac_dhcp_server(vm_ip, mac, vlan, dhcp_path)
+
+
+def launch_dhcp_server(vlan, first_ip, last_ip, cidr):
+    """
+    Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
+    :param vlan: vlan identifier
+    :param first_ip: First dhcp range ip
+    :param last_ip: Last dhcp range ip
+    :param cidr: net cidr
+    :return:
+    """
+    ip_tools = IPNetwork(cidr)
+    dhcp_netmask = str(ip_tools.netmask)
+    ip_range = [first_ip, last_ip]
+    dhcp_path = config_dic['ovs_controller_file_path']
+
+    controller_host = get_dhcp_controller()
+    controller_host.create_linux_bridge(vlan)
+    controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
+    controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path)
+
+
+def create_vxlan_mesh(host_id):
+    """
+    Create vxlan mesh across all openvimc controller and computes.
+    :param host_id: host identifier
+    :param host_id: host identifier
+    :return:
+    """
+    dhcp_compute_name = get_vxlan_interface("dhcp")
+    existing_hosts = get_hosts()
+    if len(existing_hosts['hosts']) > 0:
+        # vlxan mesh creation between openvim controller and computes
+        computes_available = existing_hosts['hosts']
+        controller_host = get_dhcp_controller()
+        for compute in computes_available:
+            vxlan_interface_name = get_vxlan_interface(compute['id'][:8])
+            config_dic['host_threads'][compute['id']].insert_task("new-vxlan", dhcp_compute_name, controller_host.host)
+            controller_host.create_ovs_vxlan_tunnel(vxlan_interface_name, compute['ip_name'])
+
+        # vlxan mesh creation between openvim computes
+        for count, compute_owner in enumerate(computes_available):
+            for compute in computes_available:
+                if compute_owner['id'] == compute['id']:
+                    pass
+                else:
+                    vxlan_interface_name = get_vxlan_interface(compute_owner['id'][:8])
+                    controller_host.create_ovs_vxlan_tunnel(vxlan_interface_name, compute_owner['ip_name'])
+                    config_dic['host_threads'][compute['id']].insert_task("new-vxlan",
+                                                                          vxlan_interface_name,
+                                                                          compute_owner['ip_name'])
+
+
+def delete_vxlan_mesh(host_id):
+    """
+    Create a task for remove a specific compute of the vlxan mesh
+    :param host_id: host id to be deleted.
+    """
+    existing_hosts = get_hosts()
+    computes_available = existing_hosts['hosts']
+    #
+    vxlan_interface_name = get_vxlan_interface(host_id[:8])
+    controller_host = get_dhcp_controller()
+    controller_host.delete_ovs_vxlan_tunnel(vxlan_interface_name)
+    # remove bridge from openvim controller if no more computes exist
+    if len(existing_hosts):
+        controller_host.delete_ovs_bridge()
+    # Remove vxlan mesh
+    for compute in computes_available:
+        if host_id == compute['id']:
+            pass
+        else:
+            controller_host.delete_ovs_vxlan_tunnel(vxlan_interface_name)
+            config_dic['host_threads'][compute['id']].insert_task("del-vxlan", vxlan_interface_name)
+
+
+def get_vxlan_interface(local_uuid):
+    """
+    Genearte a vxlan interface name
+    :param local_uuid: host id
+    :return: vlxan-8digits
+    """
+    return 'vxlan-' + local_uuid[:8]
+
+
 @bottle.route(url_base + '/hosts/<host_id>', method='PUT')
 def http_put_host_id(host_id):
     '''modify a host into the database. All resources are got and inserted'''
@@ -664,12 +850,21 @@ def http_put_host_id(host_id):
         change_keys_http2db(content, http2db_host, reverse=True)
         data={'host' : content}
 
+        if config_dic['network_type'] == 'ovs':
+            delete_vxlan_mesh(host_id)
+            config_dic['host_threads'][host_id].insert_task("del-ovsbridge")
+
         #reload thread
         config_dic['host_threads'][host_id].name = content.get('name',content['ip_name'])
         config_dic['host_threads'][host_id].user = content['user']
         config_dic['host_threads'][host_id].host = content['ip_name']
         config_dic['host_threads'][host_id].insert_task("reload")
 
+        if config_dic['network_type'] == 'ovs':
+            # create mesh with new host data
+            config_dic['host_threads'][host_id].insert_task("new-ovsbridge")
+            create_vxlan_mesh(host_id)
+
         #print data
         return format_out(data)
     else:
@@ -687,9 +882,13 @@ def http_delete_host_id(host_id):
     result, content = my.db.delete_row('hosts', host_id)
     if result == 0:
         bottle.abort(HTTP_Not_Found, content)
-    elif result >0:
-        #terminate thread
+    elif result > 0:
+        if config_dic['network_type'] == 'ovs':
+            delete_vxlan_mesh(host_id)
+        # terminate thread
         if host_id in config_dic['host_threads']:
+            if config_dic['network_type'] == 'ovs':
+                config_dic['host_threads'][host_id].insert_task("del-ovsbridge")
             config_dic['host_threads'][host_id].insert_task("exit")
         #return data
         data={'result' : content}
@@ -1411,6 +1610,11 @@ def http_post_server_id(tenant_id):
         print
         if server_start == 'no':
             content['status'] = 'INACTIVE'
+        dhcp_nets_id = []
+        for net in http_content['server']['networks']:
+            if net['type'] == 'instance:ovs':
+                dhcp_nets_id.append(get_network_id(net['net_id']))
+
         ports_to_free=[]
         new_instance_result, new_instance = my.db.new_instance(content, nets, ports_to_free)
         if new_instance_result < 0:
@@ -1431,23 +1635,37 @@ def http_post_server_id(tenant_id):
                 print ':http_post_servers ERROR UPDATING NETS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c
 
             
-            
-        #look for dhcp ip address 
-        r2, c2 = my.db.get_table(FROM="ports", SELECT=["mac", "net_id"], WHERE={"instance_id": new_instance})
-        if r2 >0 and config_dic.get("dhcp_server"):
+        #look for dhcp ip address
+        r2, c2 = my.db.get_table(FROM="ports", SELECT=["mac", "ip_address", "net_id"], WHERE={"instance_id": new_instance})
+        if r2 >0:
             for iface in c2:
-                if iface["net_id"] in config_dic["dhcp_nets"]:
+                if config_dic.get("dhcp_server") and iface["net_id"] in config_dic["dhcp_nets"]:
                     #print "dhcp insert add task"
                     r,c = config_dic['dhcp_thread'].insert_task("add", iface["mac"])
                     if r < 0:
-                        print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c 
-        
-    #Start server
-        
+                        print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c
+
+                #ensure compute contain the bridge for ovs networks:
+                server_net = get_network_id(iface['net_id'])
+                if server_net["network"].get('provider:physical', "")[:3] == 'OVS':
+                    vlan = str(server_net['network']['provider:vlan'])
+                    dhcp_enable = bool(server_net['network']['enable_dhcp'])
+                    if dhcp_enable:
+                        dhcp_firt_ip = str(server_net['network']['dhcp_first_ip'])
+                        dhcp_last_ip = str(server_net['network']['dhcp_last_ip'])
+                        dhcp_cidr = str(server_net['network']['cidr'])
+                        vm_dhcp_ip = c2[0]["ip_address"]
+                        config_dic['host_threads'][server['host_id']].insert_task("create-ovs-bridge-port", vlan)
+
+                        set_mac_dhcp(vm_dhcp_ip, vlan, dhcp_firt_ip, dhcp_last_ip, dhcp_cidr, c2[0]['mac'])
+                        launch_dhcp_server(vlan, dhcp_firt_ip, dhcp_last_ip, dhcp_cidr)
+
+        #Start server
         server['uuid'] = new_instance
-        #server_start = server.get('start', 'yes')
+        server_start = server.get('start', 'yes')
+
         if server_start != 'no':
-            server['paused'] = True if server_start == 'paused' else False 
+            server['paused'] = True if server_start == 'paused' else False
             server['action'] = {"start":None}
             server['status'] = "CREATING"
             #Program task
@@ -1585,9 +1803,11 @@ def http_server_action(server_id, tenant_id, action):
     if new_status != None and new_status == 'DELETING':
         nets=[]
         ports_to_free=[]
-        #look for dhcp ip address 
+
+        net_ovs_list = []
+        #look for dhcp ip address
         r2, c2 = my.db.get_table(FROM="ports", SELECT=["mac", "net_id"], WHERE={"instance_id": server_id})
-        r,c = my.db.delete_instance(server_id, tenant_id, nets, ports_to_free, "requested by http")
+        r, c = my.db.delete_instance(server_id, tenant_id, nets, ports_to_free, net_ovs_list, "requested by http")
         for port in ports_to_free:
             r1,c1 = config_dic['host_threads'][ server['host_id'] ].insert_task( 'restore-iface',*port )
             if r1 < 0:
@@ -1606,7 +1826,15 @@ def http_server_action(server_id, tenant_id, action):
                     #print "dhcp insert del task"
                     if r < 0:
                         print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c 
-
+        # delete ovs-port and linux bridge, contains a list of tuple (net_id,vlan)
+        for net in net_ovs_list:
+            mac = str(net[3])
+            vm_ip = str(net[2])
+            vlan = str(net[1])
+            net_id = net[0]
+            delete_dhcp_ovs_bridge(vlan, net_id)
+            delete_mac_dhcp(vm_ip, vlan, mac)
+            config_dic['host_threads'][server['host_id']].insert_task('del-ovs-port', vlan, net_id)
     return format_out(data)
 
 
@@ -1659,7 +1887,7 @@ def http_get_networks():
         print "http_get_networks error %d %s" % (result, content)
         bottle.abort(-result, content)
     else:
-        convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp') )
+        convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
         delete_nulls(content)      
         change_keys_http2db(content, http2db_network, reverse=True)  
         data={'networks' : content}
@@ -1667,8 +1895,12 @@ def http_get_networks():
 
 @bottle.route(url_base + '/networks/<network_id>', method='GET')
 def http_get_network_id(network_id):
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    #obtain data
+        data = get_network_id(network_id)
+        return format_out(data)
+
+def get_network_id(network_id):
+    my = config_dic['http_threads'][threading.current_thread().name]
+    # obtain data
     where_ = bottle.request.query
     where_['uuid'] = network_id
     result, content = my.db.get_table(FROM='nets', WHERE=where_, LIMIT=100)
@@ -1680,7 +1912,7 @@ def http_get_network_id(network_id):
         print "http_get_networks_id network '%s' not found" % network_id
         bottle.abort(HTTP_Not_Found, 'network %s not found' % network_id)
     else:
-        convert_boolean(content, ('shared', 'admin_state_up', 'enale_dhcp') )
+        convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
         change_keys_http2db(content, http2db_network, reverse=True)        
         #get ports
         result, ports = my.db.get_table(FROM='ports', SELECT=('uuid as port_id',), 
@@ -1689,7 +1921,7 @@ def http_get_network_id(network_id):
             content[0]['ports'] = ports
         delete_nulls(content[0])      
         data={'network' : content[0]}
-        return format_out(data)
+        return data
 
 @bottle.route(url_base + '/networks', method='POST')
 def http_post_networks():
@@ -1712,6 +1944,10 @@ def http_post_networks():
     #check valid params
     net_provider = network.get('provider')
     net_type =     network.get('type')
+    net_enable_dhcp = network.get('enable_dhcp')
+    if net_enable_dhcp:
+        net_cidr = network.get('cidr')
+
     net_vlan =     network.get("vlan")
     net_bind_net = network.get("bind_net")
     net_bind_type= network.get("bind_type")
@@ -1786,7 +2022,7 @@ def http_post_networks():
 #            if bridge_net==None:     
 #                bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
 #                return
-    elif net_type=='bridge_data' or net_type=='bridge_man':
+    elif config_dic['network_type'] == 'bridge' and ( net_type =='bridge_data' or net_type ==  'bridge_man' ):
         #look for a free precreated nets
         for brnet in config_dic['bridge_nets']:
             if brnet[3]==None: # free
@@ -1805,22 +2041,29 @@ def http_post_networks():
             print "using net", bridge_net
             net_provider = "bridge:"+bridge_net[0]
             net_vlan = bridge_net[1]
-    if net_vlan==None and (net_type=="data" or net_type=="ptp"):
+    elif net_type == 'bridge_data' or net_type == 'bridge_man' and config_dic['network_type'] == 'ovs':
+        net_provider = 'OVS'
+    if not net_vlan and (net_type == "data" or net_type == "ptp" or net_provider == "OVS"):
         net_vlan = my.db.get_free_net_vlan()
         if net_vlan < 0:
             bottle.abort(HTTP_Internal_Server_Error, "Error getting an available vlan")
             return
-    
+    if net_provider == 'OVS':
+        net_provider = 'OVS' + ":" + str(net_vlan)
+
     network['provider'] = net_provider
     network['type']     = net_type
     network['vlan']     = net_vlan
+
+    if 'enable_dhcp' in network and network['enable_dhcp']:
+        check_dhcp_data_integrity(network)
+
     result, content = my.db.new_row('nets', network, True, True)
     
     if result >= 0:
         if bridge_net!=None:
             bridge_net[3] = content
-        
-        if config_dic.get("dhcp_server"):
+        if config_dic.get("dhcp_server") and config_dic['network_type'] == 'bridge':
             if network["name"] in config_dic["dhcp_server"].get("nets", () ):
                 config_dic["dhcp_nets"].append(content)
                 print "dhcp_server: add new net", content
@@ -1834,6 +2077,24 @@ def http_post_networks():
         return
 
 
+def check_dhcp_data_integrity(network):
+    """
+    Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
+    :param network: list with user nets paramters
+    :return:
+    """
+    control_iface = []
+
+    if "cidr" in network:
+        cidr = network["cidr"]
+
+        ips = IPNetwork(cidr)
+        if "dhcp_first_ip" not in network:
+            network["dhcp_first_ip"] = str(ips[2])
+        if "dhcp_last_ip" not in network:
+            network["dhcp_last_ip"] = str(ips[-2])
+
+
 @bottle.route(url_base + '/networks/<network_id>', method='PUT')
 def http_put_network_id(network_id):
     '''update a network_id into the database.'''
@@ -2152,7 +2413,7 @@ def http_put_port_id(port_id):
             
             if new_net is not None: nets.append(new_net) #put first the new net, so that new openflow rules are created before removing the old ones 
             if old_net is not None: nets.append(old_net)
-            if port['type'] == 'instance:bridge':
+            if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
                 bottle.abort(HTTP_Forbidden, "bridge interfaces cannot be attached to a different net")
                 return
             elif port['type'] == 'external':
diff --git a/onos.py b/onos.py
new file mode 100644 (file)
index 0000000..53def9d
--- /dev/null
+++ b/onos.py
@@ -0,0 +1,452 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+##
+# Copyright 2016, I2T Research Group (UPV/EHU)
+# 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: alaitz.mendiola@ehu.eus or alaitz.mendiola@gmail.com
+##
+
+'''
+ImplementS the pluging for the Open Network Operating System (ONOS) openflow
+controller. It creates the class OF_conn to create dataplane connections
+with static rules based on packet destination MAC address
+'''
+
+__author__="Alaitz Mendiola"
+__date__ ="$22-nov-2016$"
+
+
+import json
+import requests
+import base64
+import logging
+
+class OF_conn():
+    '''ONOS connector. No MAC learning is used'''
+    def __init__(self, params):
+        ''' Constructor. 
+            Params: dictionary with the following keys:
+                of_dpid:     DPID to use for this controller ?? Does a controller have a dpid?
+                of_ip:       controller IP address
+                of_port:     controller TCP port
+                of_user:     user credentials, can be missing or None
+                of_password: password credentials
+                of_debug:    debug level for logging. Default to ERROR
+                other keys are ignored
+            Raise an exception if same parameter is missing or wrong
+        '''
+        #check params
+
+        if "of_ip" not in params or params["of_ip"]==None or "of_port" not in params or params["of_port"]==None:
+            raise ValueError("IP address and port must be provided")
+        #internal variables
+        self.name = "onos"
+        self.headers = {'content-type':'application/json',
+                        'accept':'application/json',
+        }
+
+        self.auth="None"
+        self.pp2ofi={}  # From Physical Port to OpenFlow Index
+        self.ofi2pp={}  # From OpenFlow Index to Physical Port
+
+        self.dpid = str(params["of_dpid"])
+        self.id = 'of:'+str(self.dpid.replace(':', ''))
+        self.url = "http://%s:%s/onos/v1/" %( str(params["of_ip"]), str(params["of_port"] ) )
+
+        # TODO This may not be straightforward
+        if "of_user" in params and params["of_user"]!=None:
+            if not params.get("of_password"):
+                of_password=""
+            else:
+                of_password=str(params["of_password"])
+            self.auth = base64.b64encode(str(params["of_user"])+":"+of_password)
+            self.headers['authorization'] = 'Basic ' + self.auth
+
+
+        self.logger = logging.getLogger('vim.OF.onos')
+        self.logger.setLevel( getattr(logging, params.get("of_debug", "ERROR")) )
+
+    def get_of_switches(self):
+        ''' Obtain a a list of switches or DPID detected by this controller
+            Return
+                >=0, list:      list length, and a list where each element a tuple pair (DPID, IP address)
+                <0, text_error: if fails
+        '''
+        try:
+            self.headers['content-type'] = 'text/plain'
+            of_response = requests.get(self.url + "devices", headers=self.headers)
+            error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
+            if of_response.status_code != 200:
+                self.logger.warning("get_of_switches " + error_text)
+                return -1, error_text
+
+            self.logger.debug("get_of_switches " + error_text)
+            info = of_response.json()
+
+            if type(info) != dict:
+                self.logger.error("get_of_switches. Unexpected response, not a dict: %s", str(info))
+                return -1, "Unexpected response, not a dict. Wrong version?"
+
+            node_list = info.get('devices')
+
+            if type(node_list) is not list:
+                self.logger.error(
+                    "get_of_switches. Unexpected response, at 'devices', not found or not a list: %s",
+                    str(type(node_list)))
+                return -1, "Unexpected response, at 'devices', not found or not a list. Wrong version?"
+
+            switch_list = []
+            for node in node_list:
+                node_id = node.get('id')
+                if node_id is None:
+                    self.logger.error("get_of_switches. Unexpected response at 'device':'id', not found: %s",
+                                      str(node))
+                    return -1, "Unexpected response at 'device':'id', not found . Wrong version?"
+
+                node_ip_address = node.get('annotations').get('managementAddress')
+                if node_ip_address is None:
+                    self.logger.error(
+                        "get_of_switches. Unexpected response at 'device':'managementAddress', not found: %s",
+                        str(node))
+                    return -1, "Unexpected response at 'device':'managementAddress', not found. Wrong version?"
+
+                node_id_hex = hex(int(node_id.split(':')[1])).split('x')[1].zfill(16)
+
+                switch_list.append(
+                    (':'.join(a + b for a, b in zip(node_id_hex[::2], node_id_hex[1::2])), node_ip_address))
+
+            return len(switch_list), switch_list
+
+        except (requests.exceptions.RequestException, ValueError) as e:
+            # ValueError in the case that JSON can not be decoded
+            error_text = type(e).__name__ + ": " + str(e)
+            self.logger.error("get_of_switches " + error_text)
+            return -1, error_text
+
+
+        
+    def obtain_port_correspondence(self):
+        '''Obtain the correspondence between physical and openflow port names
+        return:
+             0, dictionary: with physical name as key, openflow name as value
+            -1, error_text: if fails
+        '''
+        try:
+            self.headers['content-type'] = 'text/plain'
+            of_response = requests.get(self.url + "devices/" + self.id + "/ports", headers=self.headers)
+            error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
+            if of_response.status_code != 200:
+                self.logger.warning("obtain_port_correspondence " + error_text)
+                return -1, error_text
+
+            self.logger.debug("obtain_port_correspondence " + error_text)
+            info = of_response.json()
+
+            node_connector_list = info.get('ports')
+            if type(node_connector_list) is not list:
+                self.logger.error(
+                    "obtain_port_correspondence. Unexpected response at 'ports', not found or not a list: %s",
+                    str(node_connector_list))
+                return -1, "Unexpected response at 'ports', not found  or not a list. Wrong version?"
+
+            for node_connector in node_connector_list:
+                if (node_connector['port'] != "local"):
+                    self.pp2ofi[str(node_connector['annotations']['portName'])] = str(node_connector['port'])
+                    self.ofi2pp[str(node_connector['port'])] = str(node_connector['annotations']['portName'])
+
+            node_ip_address = info['annotations']['managementAddress']
+            if node_ip_address is None:
+                self.logger.error(
+                    "obtain_port_correspondence. Unexpected response at 'managementAddress', not found: %s",
+                    str(self.id))
+                return -1, "Unexpected response at 'managementAddress', not found. Wrong version?"
+            self.ip_address = node_ip_address
+
+            # print self.name, ": obtain_port_correspondence ports:", self.pp2ofi
+            return 0, self.pp2ofi
+
+        except (requests.exceptions.RequestException, ValueError) as e:
+            # ValueError in the case that JSON can not be decoded
+            error_text = type(e).__name__ + ": " + str(e)
+            self.logger.error("obtain_port_correspondence " + error_text)
+            return -1, error_text
+        
+    def get_of_rules(self, translate_of_ports=True):
+        ''' Obtain the rules inserted at openflow controller
+            Params:
+                translate_of_ports: if True it translates ports from openflow index to physical switch name
+            Return:
+                0, dict if ok: with the rule name as key and value is another dictionary with the following content:
+                    priority: rule priority
+                    name:         rule name (present also as the master dict key)
+                    ingress_port: match input port of the rule
+                    dst_mac:      match destination mac address of the rule, can be missing or None if not apply
+                    vlan_id:      match vlan tag of the rule, can be missing or None if not apply
+                    actions:      list of actions, composed by a pair tuples:
+                        (vlan, None/int): for stripping/setting a vlan tag
+                        (out, port):      send to this port 
+                    switch:       DPID, all 
+                -1, text_error if fails
+        '''
+
+
+        if len(self.ofi2pp) == 0:
+            r, c = self.obtain_port_correspondence()
+            if r < 0:
+                return r, c
+        # get rules
+        try:
+            of_response = requests.get(self.url + "flows/" + self.id, headers=self.headers)
+            error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
+
+            # The configured page does not exist if there are no rules installed. In that case we return an empty dict
+            if of_response.status_code == 404:
+                return 0, {}
+
+            elif of_response.status_code != 200:
+                self.logger.warning("get_of_rules " + error_text)
+                return -1, error_text
+            self.logger.debug("get_of_rules " + error_text)
+
+            info = of_response.json()
+
+            if type(info) != dict:
+                self.logger.error("get_of_rules. Unexpected response, not a dict: %s", str(info))
+                return -1, "Unexpected openflow response, not a dict. Wrong version?"
+
+            flow_list = info.get('flows')
+
+            if flow_list is None:
+                return 0, {}
+
+            if type(flow_list) is not list:
+                self.logger.error(
+                    "get_of_rules. Unexpected response at 'flows', not a list: %s",
+                    str(type(flow_list)))
+                return -1, "Unexpected response at 'flows', not a list. Wrong version?"
+
+            rules = dict() # Response dictionary
+
+            for flow in flow_list:
+                if not ('id' in flow and 'selector' in flow and 'treatment' in flow and \
+                                    'instructions' in flow['treatment'] and 'criteria' in \
+                                    flow['selector']):
+                    return -1, "unexpected openflow response, one or more elements are missing. Wrong version?"
+
+                rule = dict()
+                rule['switch'] = self.dpid
+                rule['priority'] = flow.get('priority')
+                rule['name'] = flow['id']
+
+                for criteria in flow['selector']['criteria']:
+                    if criteria['type'] == 'IN_PORT':
+                        in_port = str(criteria['port'])
+                        if in_port != "CONTROLLER":
+                            if not in_port in self.ofi2pp:
+                                return -1, "Error: Ingress port " + in_port + " is not in switch port list"
+                            if translate_of_ports:
+                                in_port = self.ofi2pp[in_port]
+                        rule['ingress_port'] = in_port
+
+                    elif criteria['type'] == 'VLAN_VID':
+                        rule['vlan_id'] = criteria['vlanId']
+
+                    elif criteria['type'] == 'ETH_DST':
+                        rule['dst_mac'] = str(criteria['mac']).lower()
+
+                actions = []
+                for instruction in flow['treatment']['instructions']:
+                    if instruction['type'] == "OUTPUT":
+                        out_port = str(instruction['port'])
+                        if out_port != "CONTROLLER":
+                            if not out_port in self.ofi2pp:
+                                return -1, "Error: Output port " + out_port + " is not in switch port list"
+
+                            if translate_of_ports:
+                                out_port = self.ofi2pp[out_port]
+
+                        actions.append( ('out', out_port) )
+
+                    if instruction['type'] == "L2MODIFICATION" and instruction['subtype'] == "VLAN_POP":
+                        actions.append( ('vlan', 'None') )
+                    if instruction['type'] == "L2MODIFICATION" and instruction['subtype'] == "VLAN_ID":
+                        actions.append( ('vlan', instruction['vlanId']) )
+
+                rule['actions'] = actions
+                rules[flow['id']] = dict(rule)
+
+            return 0, rules
+
+        except (requests.exceptions.RequestException, ValueError) as e:
+            # ValueError in the case that JSON can not be decoded
+            error_text = type(e).__name__ + ": " + str(e)
+            self.logger.error("get_of_rules " + error_text)
+            return -1, error_text
+
+    def del_flow(self, flow_name):
+        ''' Delete an existing rule
+            Params: flow_name, this is the rule name
+            Return
+                0, None if ok
+                -1, text_error if fails
+        '''
+
+        try:
+            self.headers['content-type'] = None
+            of_response = requests.delete(self.url + "flows/" + self.id + "/" + flow_name, headers=self.headers)
+            error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
+
+            if of_response.status_code != 204:
+                self.logger.warning("del_flow " + error_text)
+                return -1 , error_text
+            self.logger.debug("del_flow OK " + error_text)
+            return 0, None
+
+        except requests.exceptions.RequestException as e:
+            error_text = type(e).__name__ + ": " + str(e)
+            self.logger.error("del_flow " + error_text)
+            return -1, error_text
+
+    def new_flow(self, data):
+        ''' Insert a new static rule
+            Params: data: dictionary with the following content:
+                priority:     rule priority
+                name:         rule name
+                ingress_port: match input port of the rule
+                dst_mac:      match destination mac address of the rule, missing or None if not apply
+                vlan_id:      match vlan tag of the rule, missing or None if not apply
+                actions:      list of actions, composed by a pair tuples with these posibilities:
+                    ('vlan', None/int): for stripping/setting a vlan tag
+                    ('out', port):      send to this port
+            Return
+                0, None if ok
+                -1, text_error if fails
+        '''
+
+        if len(self.pp2ofi) == 0:
+            r,c = self.obtain_port_correspondence()
+            if r<0:
+                return r,c
+        try:
+            # Build the dictionary with the flow rule information for ONOS
+            flow = dict()
+            #flow['id'] = data['name']
+            flow['tableId'] = 0
+            flow['priority'] = data.get('priority')
+            flow['timeout'] = 0
+            flow['isPermanent'] = "true"
+            flow['appId'] = 10 # FIXME We should create an appId for OSM
+            flow['selector'] = dict()
+            flow['selector']['criteria'] = list()
+
+            # Flow rule matching criteria
+            if not data['ingress_port'] in self.pp2ofi:
+                error_text = 'Error. Port ' + data['ingress_port'] + ' is not present in the switch'
+                self.logger.warning("new_flow " + error_text)
+                return -1, error_text
+
+            ingress_port_criteria = dict()
+            ingress_port_criteria['type'] = "IN_PORT"
+            ingress_port_criteria['port'] = self.pp2ofi[data['ingress_port']]
+            flow['selector']['criteria'].append(ingress_port_criteria)
+
+            if 'dst_mac' in data:
+                dst_mac_criteria = dict()
+                dst_mac_criteria["type"] = "ETH_DST"
+                dst_mac_criteria["mac"] = data['dst_mac']
+                flow['selector']['criteria'].append(dst_mac_criteria)
+
+            if data.get('vlan_id'):
+                vlan_criteria = dict()
+                vlan_criteria["type"] = "VLAN_VID"
+                vlan_criteria["vlanId"] = int(data['vlan_id'])
+                flow['selector']['criteria'].append(vlan_criteria)
+
+            # Flow rule treatment
+            flow['treatment'] = dict()
+            flow['treatment']['instructions'] = list()
+            flow['treatment']['deferred'] = list()
+
+            for action in data['actions']:
+                new_action = dict()
+                if  action[0] == "vlan":
+                    new_action['type'] = "L2MODIFICATION"
+                    if action[1] == None:
+                        new_action['subtype'] = "VLAN_POP"
+                    else:
+                        new_action['subtype'] = "VLAN_ID"
+                        new_action['vlanId'] = int(action[1])
+                elif action[0] == 'out':
+                    new_action['type'] = "OUTPUT"
+                    if not action[1] in self.pp2ofi:
+                        error_msj = 'Port '+ action[1] + ' is not present in the switch'
+                        return -1, error_msj
+                    new_action['port'] = self.pp2ofi[action[1]]
+                else:
+                    error_msj = "Unknown item '%s' in action list" % action[0]
+                    self.logger.error("new_flow " + error_msj)
+                    return -1, error_msj
+
+                flow['treatment']['instructions'].append(new_action)
+
+            self.headers['content-type'] = 'application/json'
+            path = self.url + "flows/" + self.id
+            of_response = requests.post(path, headers=self.headers, data=json.dumps(flow) )
+
+            error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
+            if of_response.status_code != 201:
+                self.logger.warning("new_flow " + error_text)
+                return -1 , error_text
+
+
+            flowId = of_response.headers['location'][path.__len__() + 1:]
+
+            data['name'] = flowId
+
+            self.logger.debug("new_flow OK " + error_text)
+            return 0, None
+
+        except requests.exceptions.RequestException as e:
+            error_text = type(e).__name__ + ": " + str(e)
+            self.logger.error("new_flow " + error_text)
+            return -1, error_text
+
+    def clear_all_flows(self):
+        ''' Delete all existing rules
+            Return:
+                0, None if ok
+                -1, text_error if fails
+        '''           
+        try:
+            c, rules = self.get_of_rules(True)
+            if c < 0:
+                return -1, "Error retrieving the flows"
+
+            for rule in rules:
+                self.del_flow(rule)
+
+            self.logger.debug("clear_all_flows OK ")
+            return 0, None
+
+        except requests.exceptions.RequestException as e:
+            error_text = type(e).__name__ + ": " + str(e)
+            self.logger.error("clear_all_flows " + error_text)
+            return -1, error_text
+
+
index 7ea59c4..80cf624 100755 (executable)
--- a/openflow
+++ b/openflow
@@ -205,6 +205,8 @@ def of_add(args):
     if r<0:
         print c
         return -1
+    if args.print_id:
+        print rule["name"]
     return 0
 
 def of_delete(args):
@@ -280,6 +282,7 @@ if __name__=="__main__":
     add_parser.add_argument("--setvlan", action="append", dest="act", type=int, help="alternative to --actions. Use before --out to set vlan")
     add_parser.add_argument("--out", action="append", dest="act", type=str, help="alternative to --actions. out=<egress-port> can be used several times")
     add_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
+    add_parser.add_argument('--print-id', action='store_true', help="print the flow id after added")
     add_parser.set_defaults(func=of_add)
     
     delete_parser = subparsers.add_parser('delete', help="delete an openflow rule")
index bb852e5..148e992 100644 (file)
@@ -72,8 +72,18 @@ tenant_id: fc7b43b6-6bfa-11e4-84d2-5254006d6777   # Default tenant identifier fo
 network_vlan_range_start: 3000
 network_vlan_range_end:   4000
 
+# Overlay network implementation. Options are:
+# - ovs :   (by default) Use a vlxan mesh between computes to handle the network overlay.
+# - bridge: Use pre-populated linux bridges with L2 conectivity at compte nodes.
+network_type : ovs
+ovs_controller_ip   :   localhost                   # dhcp controller IP address, must be change in order to
+ovs_controller_user :   "osm_dhcp"                  # User for the dchp controller for OVS networks
+ovs_controller_file_path  :   "/var/lib/openvim"    # Path for dhcp daemon configuration, by default '/var/lib/openvim'
+
+
 #host bridge interfaces for networks
-# Openvim cannot create bridge networks automatically, in the same way as other CMS do.
+# Apply only for 'network_type: bridge'
+# Indicates the bridges at compute nodes to be used for the overlay networks
 # Bridge networks need to be pre-provisioned on each host and Openvim uses those pre-provisioned bridge networks.
 # Openvim assumes that the following bridge interfaces have been created on each host, appropriately associated to a physical port.
 # The following information needs to be provided:
@@ -82,21 +92,21 @@ network_vlan_range_end:   4000
 #    - The speed of the physical port in Gbps, where that bridge interface was created
 # For instance, next example assumes that 10 bridges have been created on each host
 # using vlans 2001 to 2010, associated to a 1Gbps physical port 
-bridge_ifaces:
-   #name:      [vlan, speed in Gbps]
-   virbrMan1:  [2001, 1]
-   virbrMan2:  [2002, 1]
-   virbrMan3:  [2003, 1]
-   virbrMan4:  [2004, 1]
-   virbrMan5:  [2005, 1]
-   virbrMan6:  [2006, 1]
-   virbrMan7:  [2007, 1]
-   virbrMan8:  [2008, 1]
-   virbrMan9:  [2009, 1]
-   virbrMan10: [2010, 1]
+#bridge_ifaces:
+#   #name:      [vlan, speed in Gbps]
+#   virbrMan1:  [2001, 1]
+#   virbrMan2:  [2002, 1]
+#   virbrMan3:  [2003, 1]
+#   virbrMan4:  [2004, 1]
+#   virbrMan5:  [2005, 1]
+#   virbrMan6:  [2006, 1]
+#   virbrMan7:  [2007, 1]
+#   virbrMan8:  [2008, 1]
+#   virbrMan9:  [2009, 1]
+#   virbrMan10: [2010, 1]
 
 #Used only when 'mode' is at development'. Indicates which 'bridge_ifaces' is used for dataplane networks
-development_bridge: virbrMan10
+#development_bridge: virbrMan10
 
 #DHCP SERVER PARAMETERS. 
 #In case some of the previous 'bridge_ifaces' are connected to an EXTERNAL dhcp server, provide 
@@ -126,3 +136,5 @@ dhcp_server:
 log_level:       ERROR
 log_level_db:    DEBUG
 log_level_of:    DEBUG
+
+
index 9cd321c..7c260b9 100755 (executable)
@@ -30,9 +30,9 @@ and host controllers
 
 __author__="Alfonso Tierno"
 __date__ ="$10-jul-2014 12:07:15$"
-__version__="0.5.2-r516"
+__version__="0.5.2-r519"
 version_date="Jan 2017"
-database_version="0.9"      #expected database schema version
+database_version="0.10"      #expected database schema version
 
 import httpserver
 import auxiliary_functions as af
@@ -69,6 +69,10 @@ def load_configuration(configuration_file):
                      'log_level': "DEBUG",
                      'log_level_db': "ERROR",
                      'log_level_of': 'ERROR',
+                     'bridge_ifaces': {},
+                     'network_type': 'ovs',
+                     'ovs_controller_user': 'osm_dhcp',
+                     'ovs_controller_file_path': '/var/lib/',
             }
     try:
         #First load configuration from configuration file
@@ -214,10 +218,11 @@ if __name__=="__main__":
             #allow backward compatibility of test_mode option
             if 'test_mode' in config_dic and config_dic['test_mode']==True:
                 config_dic['mode'] = 'test' 
-        if config_dic['mode'] == 'development' and ( 'development_bridge' not in config_dic or config_dic['development_bridge'] not in config_dic.get("bridge_ifaces",None) ):
+        if config_dic['mode'] == 'development' and config_dic['network_type'] == 'bridge' and \
+                ( 'development_bridge' not in config_dic or config_dic['development_bridge'] not in config_dic.get("bridge_ifaces",None) ):
             logger.error("'%s' is not a valid 'development_bridge', not one of the 'bridge_ifaces'", config_file)
             exit(-1)
-            
+
         if config_dic['mode'] != 'normal':
             print '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
             print "!! Warning, openvimd in TEST mode '%s'" % config_dic['mode']
index b8e06a4..09f5fb2 100644 (file)
 # Changed the vlan tag used by virbrVIM from 2000 to 1100
 
 function usage(){
-    echo -e "Usage: sudo $0 [-y] <user-name>  [ <iface-name>  [<ip-address>|dhcp] ]"
+    echo -e "Usage: sudo $0 [-f] <user-name> [ <iface-for-overlay-bridges>  [<ip-address>|dhcp] ]"
     echo -e "  Configure compute host for VIM usage. (version 0.4). Params:"
-    echo -e "     -y  do not prompt for confirmation. If a new user is created, the user name is set as password"
+    echo -e "     -f  do not prompt for confirmation. If a new user is created, the user name is set as password"
     echo -e "     <user-name> Create if not exist and configure this user for openvim to connect"
-    echo -e "     <iface-name> if suplied creates bridge interfaces on this interface, needed for openvim"
+    echo -e "     <iface-for-overlay-bridges> if suplied creates bridge interfaces on this interface, needed for older openvim versions"
     echo -e "     ip or dhcp if suplied, configure the interface with this ip address (/24) or 'dhcp' "
+
 }
 
+function _install_openvswitch(){
+
+    echo "Installing openvswitch"
+    curl -O  http://openvswitch.org/releases/openvswitch-2.5.1.tar.gz
+    mkdir -p ~/rpmbuild/SOURCES
+    cp  openvswitch-2.5.1.tar.gz  ~/rpmbuild/SOURCES/
+    tar -zxvf openvswitch-2.5.1.tar.gz
+    cp -r openvswitch-2.5.1 ~/rpmbuild/SOURCES/
+    rpmbuild -bb --without check ~/rpmbuild/SOURCES/openvswitch-2.5.1/rhel/openvswitch.spec
+    yum -y localinstall /root/rpmbuild/RPMS/x86_64/openvswitch-2.5.1-1.x86_64.rpm
+    systemctl start openvswitch.service
+}
 
 #1 CHECK input parameters
 #1.1 root privileges
@@ -61,9 +74,9 @@ function usage(){
 
 #1.2 input parameters
 FORCE=""
-while getopts "y" o; do
+while getopts "f" o; do
     case "${o}" in
-        y)
+        f)
             FORCE="yes"
             ;;
         *)
@@ -85,6 +98,7 @@ user_name=$1
 interface=$2
 ip_iface=$3
 
+
 if [ -n "$interface" ] && ! ifconfig $interface &> /dev/null
 then
   echo "Error: interface '$interface' is not present in the system"
@@ -92,6 +106,7 @@ then
   exit 1
 fi
 
+
 echo '
 #################################################################
 #####       INSTALL NEEDED PACKETS                          #####
@@ -101,7 +116,12 @@ echo '
 yum repolist
 yum check-update
 yum update -y
-yum install -y screen virt-manager ethtool gcc gcc-c++ xorg-x11-xauth xorg-x11-xinit xorg-x11-deprecated-libs libXtst guestfish hwloc libhugetlbfs-utils libguestfs-tools numactl
+yum install -y screen virt-manager ethtool gcc gcc-c++ xorg-x11-xauth xorg-x11-xinit xorg-x11-deprecated-libs libXtst \
+                      guestfish hwloc libhugetlbfs-utils libguestfs-tools numactl
+
+
+# gcc make python-devel openssl-devel kernel-devel graphviz \ kernel-debug-devel autoconf automake rpm-build redhat-rpm-config \ libtool
+
 # Selinux management
 yum install -y policycoreutils-python
 
@@ -389,6 +409,8 @@ BOOTPROTO=none
 IPV6INIT=no" >> $interface.tmp
     mv $interface.tmp  ifcfg-$interface
 
+if  [ -z $interface ]
+then
   # Management interfaces
 #  integrated_interfaces=""
 #  nb_ifaces=0
@@ -458,7 +480,7 @@ BOOTPROTO=none
 MTU=9000
 BRIDGE=virbrMan$i" > ifcfg-${interface}.20$i2digits
   done
-
+fi
   iface=$interface
   if [ -n "$ip_iface" ]
   then
@@ -549,8 +571,9 @@ dracut --force
 #  chmod +x /etc/rc.d/rc.local
 
 #fi
+_install_openvswitch
 
-echo 
+echo
 echo "Do not forget to create a shared (NFS, Samba, ...) where original virtual machine images are allocated"
 echo
 echo "Do not forget to copy the public ssh key of openvim user into /home/${user_name}/.ssh/authorized_keys for authomatic login from openvim controller"
index 9d82c19..ff049db 100755 (executable)
@@ -21,7 +21,7 @@
 # contact with: nfvlabs@tid.es
 ##
 
-# Authors: Antonio Lopez, Pablo Montes, Alfonso Tierno
+# Authors: Antonio Lopez, Pablo Montes, Alfonso Tierno, Leonardo Mirabal
 # June 2015
 
 # Personalize RHEL7.1 on compute nodes
 # @base, @core, @development, @network-file-system-client, @virtualization-hypervisor, @virtualization-platform, @virtualization-tools
 
 
+
 function usage(){
-    echo -e "Usage: sudo $0 [-y] <user-name>  [ <iface-name>  [<ip-address>|dhcp] ]"
+    echo -e "Usage: sudo $0 [-f] <user-name> [<iface-for-overlay-bridges>]"
     echo -e "  Configure compute host for VIM usage. (version 0.4). Params:"
-    echo -e "     -y  do not prompt for confirmation. If a new user is created, the user name is set as password"
+    echo -e "     -f  do not prompt for confirmation. If a new user is created, the user name is set as password"
     echo -e "     <user-name> Create if not exist and configure this user for openvim to connect"
-    echo -e "     <iface-name> if suplied creates bridge interfaces on this interface, needed for openvim"
-    echo -e "     ip or dhcp if suplied, configure the interface with this ip address (/24) or 'dhcp' "
+    echo -e "     [<iface-for-overlay-bridges>] Only needed for old openvim versions. Iface to create pre-provioned bridges for network conectivity"
+
 }
 
 
@@ -57,9 +58,9 @@ function usage(){
 
 #1.2 input parameters
 FORCE=""
-while getopts "y" o; do
+while getopts "f" o; do
     case "${o}" in
-        y)
+        f)
             FORCE="yes"
             ;;
         *)
@@ -81,9 +82,9 @@ fi
 
 user_name=$1
 interface=$2
-ip_iface=$3
 
-if [ -n "$interface" ] && ! ifconfig $interface &> /dev/null
+
+if [ -z $interface ]  && ! ifconfig $interface &> /dev/null
 then
   echo "Error: interface '$interface' is not present in the system"
   usage
@@ -96,17 +97,14 @@ echo '
 #################################################################'
 
 # Required packages
-apt-get -y update
-#apt-get -y install grub-common screen virt-manager ethtool build-essential x11-common x11-utils x11-apps libguestfs-tools hwloc libguestfs-tools numactl vlan nfs-common nfs-kernel-server
-apt-get -y install grub-common screen virt-manager ethtool build-essential x11-common x11-utils libguestfs-tools hwloc libguestfs-tools numactl vlan nfs-common nfs-kernel-server
-
+apt-get -y  update
+apt-get -y  install grub-common screen virt-manager ethtool build-essential x11-common x11-utils \
+            libguestfs-tools hwloc libguestfs-tools numactl vlan nfs-common nfs-kernel-server openvswitch-switch
 echo "Remove unneeded packages....."
 apt-get -y autoremove
 # Selinux management
 #yum install -y policycoreutils-python
 
-
-
 echo '
 #################################################################
 #####       INSTALL USER                                    #####
@@ -141,24 +139,19 @@ else
   fi
 fi
 
-## Allow admin users to access without password
-#if ! grep -q "#openmano" /etc/sudoers
-#then
-#    cat >> /home/${user_name}/script_visudo.sh << EOL
-##!/bin/bash
-#cat \$1 | awk '(\$0~"requiretty"){print "#"\$0}(\$0!~"requiretty"){print \$0}' > tmp
-#cat tmp > \$1
-#rm tmp
-#echo "" >> \$1
-#echo "#openmano allow to group admin to grant root privileges without password" >> \$1
-#echo "%admin ALL=(ALL) NOPASSWD: ALL" >> \$1
-#EOL
-#    chmod +x /home/${user_name}/script_visudo.sh
-#    echo "allowing admin user to get root privileges withut password"
-#    export EDITOR=/home/${user_name}/script_visudo.sh && sudo -E visudo
-#    rm -f /home/${user_name}/script_visudo.sh
-#fi
-
+# Allow admin users to access without password
+if ! grep -q "#openmano" /etc/sudoers
+then
+    cat >> /home/${user_name}/script_visudo.sh << EOL
+#!/bin/bash
+echo "#openmano allow to group admin to grant root privileges without password" >> \$1
+echo "${user_name} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
+EOL
+    chmod +x /home/${user_name}/script_visudo.sh
+    echo "allowing admin user to get root privileges withut password"
+    export EDITOR=/home/${user_name}/script_visudo.sh && sudo -E visudo
+    rm -f /home/${user_name}/script_visudo.sh
+fi
 
 echo '
 #################################################################
@@ -365,107 +358,88 @@ echo "Interface ==> $interface"
 if [ -n "$interface" ]
 then
 
-
-  # For management and data interfaces
-  rm -f /etc/udev/rules.d/pci_config.rules # it will be created to define VFs
+    # For management and data interfaces
+    rm -f /etc/udev/rules.d/pci_config.rules # it will be created to define VFs
 
 
-  # Set ONBOOT=on and MTU=9000 on the interface used for the bridges
-  echo "configuring iface $interface"
+    # Set ONBOOT=on and MTU=9000 on the interface used for the bridges
+    echo "configuring iface $interface"
 
-#MTU for interfaces and bridges
-MTU=9000
+    #MTU for interfaces and bridges
+    MTU=9000
 
-cp /etc/network/interfaces interfaces.tmp
-
-
-  #Create infrastructure bridge, normally used for connecting to compute nodes, openflow controller, ...
+    cp /etc/network/interfaces interfaces.tmp
 
 
+    #Create infrastructure bridge, normally used for connecting to compute nodes, openflow controller, ...
     #Create VLAN for infrastructure bridge
 
- echo "
+echo "
 ######### CUTLINE #########
 
 auto ${interface}
 iface ${interface} inet manual
-    post-up ip link set dev ${interface} mtu ${MTU}
+post-up ip link set dev ${interface} mtu ${MTU}
 
 auto ${interface}.1001
 iface ${interface}.1001 inet manual
-    vlan-raw-device ${interface}
-    post-up ip link set mtu $MTU dev ${interface}.1001
+vlan-raw-device ${interface}
+post-up ip link set mtu $MTU dev ${interface}.1001
 " >> interfaces.tmp
 
-# echo "ifconfig ${interface} mtu $MTU
-# ifconfig ${interface} up
-#" > mtu.tmp
+        # echo "ifconfig ${interface} mtu $MTU
+        # ifconfig ${interface} up
+        #" > mtu.tmp
 
 
-  #Create bridge interfaces
-  echo "Creating bridge ifaces: "
-  for ((i=1;i<=20;i++))
-  do
-    i2digits=$i
-    [ $i -lt 10 ] && i2digits="0$i"
-    echo "    virbrMan$i  vlan 20$i2digits"
+    #Create bridge interfaces
+    echo "Creating bridge ifaces: "
+    for ((i=1;i<=20;i++))
+    do
+        i2digits=$i
+        [ $i -lt 10 ] && i2digits="0$i"
+        echo "    virbrMan$i  vlan 20$i2digits"
 
-    j=$i
+        j=$i
 
- echo "
+echo "
 auto ${interface}.20$i2digits
 iface ${interface}.20$i2digits inet manual
-    post-up ip link set mtu $MTU dev ${interface}.20$i2digits
+vlan-raw-device ${interface}
+post-up ip link set mtu $MTU dev ${interface}.20$i2digits
 
 auto virbrMan$j
 iface virbrMan$j inet manual
-       bridge_ports ${interface}.20$i2digits
-       post-up ip link set dev virbrMan$j && ip link set mtu 9000 dev virbrMan$j
+bridge_ports ${interface}.20$i2digits
+post-up ip link set dev virbrMan$j && ip link set mtu $MTU dev virbrMan$j
 " >> interfaces.tmp
 
-# echo "ifconfig ${interface}.20$i2digits mtu $MTU
-#ifconfig virbrMan$j mtu $MTU
-#ifconfig virbrMan$j up
-#" >> mtu.tmp
+    done
 
-  done
+    # echo "ifconfig em2.1001 mtu $MTU
+    #ifconfig virbrInf mtu $MTU
+    #ifconfig virbrInf up
+    #" >> mtu.tmp
 
- echo "
-auto em2.1001
-iface em2.1001 inet manual
-    post-up ip link set dev em2.1001 mtu 9000
-
-auto virbrInf
-iface virbrInf inet manual
-       bridge_ports em2.1001
-       post-up ip link set dev virbrInf && ip link set mtu 9000 dev virbrInf
-" >> interfaces.tmp
-
-# echo "ifconfig em2.1001 mtu $MTU
-#ifconfig virbrInf mtu $MTU
-#ifconfig virbrInf up
-#" >> mtu.tmp
-
-if ! grep -q "#### CUTLINE ####" /etc/network/interfaces
-then
-       echo "====== Copying interfaces.tmp to /etc/network/interfaces"
-       cp interfaces.tmp /etc/network/interfaces
-fi
+    if ! grep -q "#### CUTLINE ####" /etc/network/interfaces
+    then
+        echo "====== Copying interfaces.tmp to /etc/network/interfaces"
+        cp interfaces.tmp /etc/network/interfaces
+    fi
 
 
   #popd
 fi
 
-
-# Activate 8 Virtual Functions per PF on Niantic cards (ixgbe driver)
+################### Activate 8 Virtual Functions per PF on Niantic cards (ixgbe driver)
 if [[ `lsmod | cut -d" " -f1 | grep "ixgbe" | grep -v vf` ]]
-then
-       if ! grep -q "ixgbe" /etc/modprobe.d/ixgbe.conf
-       then
-       echo "options ixgbe max_vfs=8" >> /etc/modprobe.d/ixgbe.conf
-       fi
+    then
+        if ! grep -q "ixgbe" /etc/modprobe.d/ixgbe.conf
+        then
+        echo "options ixgbe max_vfs=8" >> /etc/modprobe.d/ixgbe.conf
+        fi
 
-fi
+    fi
 
 echo "#!/bin/bash" > /etc/activate-vfs.sh
 chmod +x /etc/activate-vfs.sh
@@ -524,7 +498,7 @@ exit 0" >> /etc/rc.local
 #fi
 
 chmod a+rwx /var/lib/libvirt/images
-mkdir /usr/libexec/
+mkdir -p /usr/libexec/
 pushd /usr/libexec/
 ln -s /usr/bin/qemu-system-x86_64 qemu-kvm
 popd
index 6024a62..2f6d72e 100755 (executable)
@@ -21,7 +21,7 @@
 # contact with: nfvlabs@tid.es
 ##
 
-# Authors: Antonio Lopez, Pablo Montes, Alfonso Tierno
+# Authors: Antonio Lopez, Pablo Montes, Alfonso Tierno, Leonardo Mirabal
 # June 2015
 
 # Personalize RHEL7.1 on compute nodes
@@ -46,42 +46,31 @@ set_mtu_path='/etc/'
 VLAN_INDEX=20
 
 function _usage(){
-    echo -e "Usage: sudo $0 [-y] <user-name>  <iface-name>"
+    echo -e "Usage: sudo $0 [-f] --user=<user-name> --overlay=ovs --iface-name=<iface-name>"
     echo -e "  Configure compute host for VIM usage. (version 0.4). OPTIONS:"
     echo -e "     -h --help    this help"
     echo -e "     -f --force:  do not prompt for confirmation. If a new user is created, the user name is set as password"
     echo -e "     -u --user:   Create if not exist and configure this user for openvim to connect"
-    echo -e "     --in --iface-name:  creates bridge interfaces on this interface, needed for openvim overlay networks"
-    exit 1
-}
-
-function _interface_cfg_generator(){
-    #$1 interface name | $2 MTU | $3 type
+    echo -e "     -o --overlay: ovs, bridge and bridge-and-ovs. Specify the networking overlay used by openvim, by default ovs is used"
+    echo -e "     -in --iface-name:  creates bridge interfaces on this interface, needed for openvim overlay networks"
 
-echo "
-auto ${1}
-iface ${1} inet ${3}
-        mtu ${2}
-        ${bridge_ports}
-" >> ${interfaced_path}${1}."cfg"
+    exit 1
 }
 
-
 function _interface_cfg_generator(){
     #$1 interface name | $2 vlan  | $3 virbrMan | $4  MTU
 
 echo "
 auto ${1}.${2}
 iface ${1}.${2} inet manual
-        mtu ${4}
-        post-up vconfig add ${1} ${2}
-        post-down vconfig rem ${1}.${2}
+        vlan-raw-device ${1}
+        post-up ip link set mtu $MTU dev ${1}.${2}
 
 auto ${3}
 iface ${3} inet manual
         bridge_ports ${1}.${2}
-        mtu ${4}
-        vlan-raw-device $1
+           post-up ip link set dev ${3} && ip link set mtu $MTU dev ${3}
+
 " >> ${interfaced_path}${1}.${2}."cfg"
 }
 
@@ -89,14 +78,14 @@ function _install_user() {
     # create user given by the user and add to groups need it.
     # Add required groups
     groupadd -f admin
-    groupadd -f libvirt   #for other operating systems may be libvirtd
+    groupadd -f libvirtd   #for other operating systems may be libvirtd
 
     # Adds user, default password same as name
     if grep -q "^${option_user}:" /etc/passwd
     then
         #user exist, add to group
         echo "adding user ${option_user} to groups libvirt,admin"
-        usermod -a -G libvirt,admin -g admin ${option_user}
+        usermod -a -G libvirtd,admin -g admin ${option_user}
     else
         #create user if it does not exist
         [ -z "$FORCE" ] && read -p "user '${option_user}' does not exist, create (Y/n)" kk
@@ -105,7 +94,7 @@ function _install_user() {
             exit
         fi
         echo "creating and configuring user ${option_user}"
-        useradd -m -G libvirt,admin -g admin ${option_user}
+        useradd -m -G libvirtd,admin -g admin ${option_user}
         #Password
         if [ -z "$FORCE" ]
             then
@@ -139,13 +128,13 @@ function _openmano_img_2_libvirt_img(){
     fi
 }
 
-function _install_pacckags_dependences()
+function _install_packages_dependencies()
 {
     # Required packages by openvim
     apt-get -y update
     apt-get -y install  grub-common screen virt-manager ethtool build-essential \
                         x11-common x11-utils libguestfs-tools hwloc libguestfs-tools \
-                        numactl vlan nfs-common nfs-kernel-server
+                        numactl vlan nfs-common nfs-kernel-server  openvswitch-switch
     echo "Remove unneeded packages....."
     apt-get -y autoremove
 }
@@ -154,8 +143,6 @@ function _network_configuration(){
     # adding vlan support
     grep -q '8021q' '/etc/modules'; [ $? -eq 1 ] && sudo su -c 'echo "8021q" >> /etc/modules'
 
-    #grep -q ${interface} '/etc/network/interfaces.d/50-cloud-init.cfg'; [ $? -eq 0 ] && sed -e '/'${interface}'/ s/^#*/#/' -i  '/etc/network/interfaces.d/50-cloud-init.cfg'
-
     # Network interfaces static configuration
     echo "Interface ==> $interface"
     if [ -n "$interface" ]
@@ -164,24 +151,25 @@ function _network_configuration(){
         rm -f /etc/udev/rules.d/pci_config.rules # it will be created to define VFs
         # Set ONBOOT=on and MTU=9000 on the interface used for the bridges
         echo "configuring iface $interface"
+    if [ "$option_overlay" == "bridge" ] || [ "$option_overlay" == "bridge-and-ovs" ]
+        then
+            # Static network interface configuration and MTU
+            MTU=9000
+            virbrMan_interface_number=20
 
-    # Static network interface configuration and MTU
-    MTU=9000
-    virbrMan_interface_number=20
-
-    #Create bridge interfaces
-    echo "Creating bridge ifaces: "
-    for ((i =1; i <= ${virbrMan_interface_number}; i++))
-        do
-            i2digits=${i}
-            [ ${i} -lt 10 ] && i2digits="0${i}"
-            echo "    ${interface} ${VLAN_INDEX}${i2digits}"
-            echo "    virbrMan${i}  vlan ${VLAN_INDEX}${i2digits}"
-            j=${i}
-            #$1 interface name | $2 vlan | $3 MTU | $3 virbrMan | $4 bridge_ports
-            _interface_cfg_generator  ${interface} ${VLAN_INDEX}${i2digits} 'virbrMan'${i} ${MTU}
-    done
-
+            #Create bridge interfaces
+            echo "Creating bridge ifaces: "
+            for ((i =1; i <= ${virbrMan_interface_number}; i++))
+                do
+                    i2digits=${i}
+                    [ ${i} -lt 10 ] && i2digits="0${i}"
+                    echo "    ${interface} ${VLAN_INDEX}${i2digits}"
+                    echo "    virbrMan${i}  vlan ${VLAN_INDEX}${i2digits}"
+                    j=${i}
+                    #$1 interface name | $2 vlan | $3 MTU | $3 virbrMan | $4 bridge_ports
+                    _interface_cfg_generator  ${interface} ${VLAN_INDEX}${i2digits} 'virbrMan'${i} ${MTU}
+            done
+        fi
     fi
 }
 
@@ -234,8 +222,30 @@ function _hostinfo_config()
     echo "#if compute node contain a different name it must be indicated in this file" >> /opt/VNF/images/hostinfo.yaml
     echo "#with the format extandard-name: compute-name" >> /opt/VNF/images/hostinfo.yaml
     chmod o+r /opt/VNF/images/hostinfo.yaml
+    if [ "$interface" != "" -a "$interface" != "em1" ]
+    then
+      echo "iface_names:"  >> /opt/VNF/images/hostinfo.yaml
+      echo "  em1: ${interface}" >> /opt/VNF/images/hostinfo.yaml
+    fi
 }
 
+function _add_user_to_visudo()
+{
+# Allow admin users to access without password
+if ! grep -q "#openmano" /etc/sudoers
+then
+    cat >> /home/${option_user}/script_visudo.sh << EOL
+#!/bin/bash
+echo "#openmano allow to group admin to grant root privileges without password" >> \$1
+echo "${option_user} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
+EOL
+    chmod +x /home/${option_user}/script_visudo.sh
+    echo "allowing admin user to get root privileges withut password"
+    export EDITOR=/home/${option_user}/script_visudo.sh && sudo -E visudo
+    rm -f /home/${option_user}/script_visudo.sh
+fi
+
+}
 function _get_opts()
 {
     options="$1"
@@ -370,15 +380,19 @@ function _parse_opts()
     [ -n "$option_force" ] && FORCE="yes"
 
     [ -z "$option_user" ] && echo -e "ERROR: User argument is mandatory, --user=<user>\n" >&2 && _usage
-    #echo "user_name = "$option_user
 
     [ -z "$option_iface_name" ] && echo -e "ERROR: iface-name argument is mandatory, --iface-name=<interface>\n" && _usage
     interface=$option_iface_name
 
+    if [ "$option_overlay" != "bridge" ] && [ "$option_overlay" != "ovs" ] && [ "$option_overlay" != "bridge-and-ovs" ];
+    then
+        option_overlay='ovs'
+        echo 'ERROR: overlay argument must be "ovs", "bridge", "bridge-and-ovs"' && _usage
+    fi
 }
 
 #Parse opts
-_get_opts "help:h force:f user:u= iface-name:in= "  $*  || exit 1
+_get_opts "help:h force:f user:u= overlay:o= iface-name:in= "  $*  || exit 1
 _parse_opts
 
 #check root privileges
@@ -393,12 +407,12 @@ echo '
 #####       INSTALL USER                                    #####
 #################################################################'
 _install_user
-
+_add_user_to_visudo
 echo '
 #################################################################
 #####       INSTALL NEEDED PACKETS                          #####
 #################################################################'
-_install_pacckags_dependences
+_install_packages_dependencies
 
 echo '
 #################################################################
@@ -412,7 +426,10 @@ echo '
 #################################################################
 #####       NETWORK CONFIGURATION                           #####
 #################################################################'
+
 _network_configuration
+
+
 _disable_aaparmor
 _user_remainder_pront
 
diff --git a/scripts/configure-dhcp-server-UBUNTU16.0.4.sh b/scripts/configure-dhcp-server-UBUNTU16.0.4.sh
new file mode 100755 (executable)
index 0000000..9eb675e
--- /dev/null
@@ -0,0 +1,148 @@
+#!/bin/bash
+##
+# 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
+#
+# Authors: Leonardo Mirabal
+# February 2017
+
+
+
+function _usage(){
+    echo -e "Usage: sudo $0  <user-name>  "
+    echo -e "  Configure dhcp server for VIM usage. (version 1.0). Params:"
+    echo -e "     <user-name> Create if not exist and configure this user for openvim to connect"
+    echo -e "     -h --help    this help"
+    exit 1
+}
+
+function _install_packages_dependencies()
+{
+    # Required packages by openvim
+    apt-get -y update
+    apt-get -y install   ethtool build-essential dnsmasq openvswitch-switch
+    echo "Remove unneeded packages....."
+    apt-get -y autoremove
+}
+
+function _add_user_to_visudo()
+{
+# Allow admin users to access without password
+if ! grep -q "#openmano" /etc/sudoers
+then
+    cat >> /home/${option_user}/script_visudo.sh << EOL
+#!/bin/bash
+echo "#openmano allow to group admin to grant root privileges without password" >> \$1
+echo "${option_user} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
+EOL
+    chmod +x /home/${option_user}/script_visudo.sh
+    echo "allowing admin user to get root privileges withut password"
+    export EDITOR=/home/${option_user}/script_visudo.sh && sudo -E visudo
+    rm -f /home/${option_user}/script_visudo.sh
+fi
+
+}
+
+function _create_ovs_controller_config_path() {
+    mkdir -p '/var/lib/openvim'
+}
+
+function _install_user() {
+    # create user given by the user and add to groups need it.
+    # Add required groups
+    groupadd -f admin
+
+    # Adds user, default password same as name
+    if grep -q "^${option_user}:" /etc/passwd
+    then
+        #user exist, add to group
+        echo "adding user ${option_user} to group admin"
+        usermod -a -G admin -g admin ${option_user}
+    else
+        #create user if it does not exist
+        [ -z "$FORCE" ] && read -p "user '${option_user}' does not exist, create (Y/n)" kk
+        if ! [ -z "$kk" -o "$kk"="y" -o "$kk"="Y" ]
+        then
+            exit
+        fi
+        echo "creating and configuring user ${option_user}"
+        useradd -m -G admin -g admin ${option_user}
+        #Password
+        if [ -z "$FORCE" ]
+            then
+                echo "Provide a password for ${option_user}"
+                passwd ${option_user}
+            else
+                echo -e "$option_user\n$option_user" | passwd --stdin ${option_user}
+        fi
+    fi
+
+}
+
+
+
+
+#1.2 input parameters
+FORCE=""
+while getopts "h" o; do
+    case "${o}" in
+        h)
+            _usage
+            exit -1
+            ;;
+    esac
+done
+shift $((OPTIND-1))
+
+
+
+if [ $# -lt 1 ]
+then
+  usage
+  exit
+fi
+
+[ -z "$1" ] && echo -e "ERROR: User argument is mandatory, --user=<user>\n"  && _usage
+
+option_user=$1
+
+#check root privileges
+[ "${USER}" != "root" ] && echo "Needed root privileges" >&2 && exit 2
+
+
+echo '
+#################################################################
+#####       INSTALL USER                                    #####
+#################################################################'
+
+_install_user
+_add_user_to_visudo
+
+echo '
+#################################################################
+#####       INSTALL NEEDED PACKETS                          #####
+#################################################################'
+_install_packages_dependencies
+
+_create_ovs_controller_config_path
+
+echo
+echo "Do not forget to copy the public ssh key into /home/${option_user}/.ssh/authorized_keys for authomatic login from openvim controller"
+echo
+
+echo "Reboot the system to make the changes effective"
index 44fe9e2..ae839ba 100755 (executable)
@@ -195,7 +195,7 @@ then
     ${DIRvim}/openvim net-create $DIRvim/test/networks/net-example3.yaml || ! echo "fail" >&2 || $_exit 1
 
     printf "%-50s" "Creating openvim tenant 'TEST-admin': "
-    result=`openvim tenant-create '{"tenant": {"name":"TEST-admin", "description":"admin"}}'`
+    result=`${DIRvim}/openvim tenant-create '{"tenant": {"name":"TEST-admin", "description":"admin"}}'`
     vimtenant=`echo $result |gawk '{print $1}'`
     #check a valid uuid is obtained
     ! is_valid_uuid $vimtenant && echo "FAIL" && echo "    $result" && $_exit 1
index d49dd18..da62fae 100644 (file)
@@ -29,4 +29,6 @@ network:
                               #    default            : attached to the default host interface
                               #    null               : for data or ptp types. (To be changed in future versions)
     shared:              true # true, false: if shared it will consider by OPENVIM an EXTERNAL network available at OPENMANO
+    enable_dhcp : true # true, false to activate network dhcp over copmutes OVS mesh
+    cidr:  10.0.0.0/24        # Network CIDR from which to include or exclude addresses used for DHCP service lease offerings.
 
diff --git a/test/networks/net-example4.yaml b/test/networks/net-example4.yaml
new file mode 100644 (file)
index 0000000..1402de7
--- /dev/null
@@ -0,0 +1,28 @@
+##
+# 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
+##
+
+network:
+    name:               mgmt
+    type:               bridge_man
+    shared:             true
+    enable_dhcp:        true
+    cidr:               192.168.10.0/24
+
index 3f76771..2c9b504 100755 (executable)
@@ -146,7 +146,7 @@ TODELETE="restore"
 
 printf "%-50s" "new rule vlan,mac -> no vlan: "
 rule_name=fromVlanMac_to_NoVlan1
-openflow add $rule_name --priority 1000 --matchmac "aa:bb:cc:dd:ee:ff" --matchvlan 500 --inport $port0 --stripvlan --out $port1 $debug 
+rule_name=`openflow add $rule_name --priority 1000 --matchmac "aa:bb:cc:dd:ee:ff" --matchvlan 500 --inport $port0 --stripvlan --out $port1 $debug --print-id`
 [[ $? != 0 ]] && echo  "FAIL cannot insert new rule" && $_exit 1
 expected="$OF_CONTROLLER_DPID 1000 $rule_name $port0 aa:bb:cc:dd:ee:ff 500 vlan=None,out=$port1"
 result=`openflow list | grep $rule_name`
@@ -158,7 +158,7 @@ TODELETE="$rule_name $TODELETE"
 
 printf "%-50s" "new rule mac -> vlan: "
 rule_name=fromMac_to_Vlan2
-openflow add $rule_name --priority 1001 --matchmac "ff:ff:ff:ff:ff:ff" --inport $port1 --setvlan 501 --out $port2 --out $port3 $debug 
+rule_name=`openflow add $rule_name --priority 1001 --matchmac "ff:ff:ff:ff:ff:ff" --inport $port1 --setvlan 501 --out $port2 --out $port3 $debug  --print-id`
 [[ $? != 0 ]] && echo  "FAIL cannot insert new rule" && $_exit 1
 expected="$OF_CONTROLLER_DPID 1001 $rule_name $port1 ff:ff:ff:ff:ff:ff any vlan=501,out=$port2,out=$port3"
 result=`openflow list | grep $rule_name`
@@ -170,7 +170,7 @@ TODELETE="$rule_name $TODELETE"
 
 printf "%-50s" "new rule None -> None: "
 rule_name=fromNone_to_None
-openflow add $rule_name --priority 1002 --inport $port2 --out $port0 $debug 
+rule_name=`openflow add $rule_name --priority 1002 --inport $port2 --out $port0 $debug  --print-id`
 [[ $? != 0 ]] && echo  "FAIL cannot insert new rule" && $_exit 1
 expected="$OF_CONTROLLER_DPID 1002 $rule_name $port2 any any out=$port0"
 result=`openflow list | grep $rule_name`
@@ -182,7 +182,7 @@ TODELETE="$rule_name $TODELETE"
 
 printf "%-50s" "new rule vlan -> vlan: "
 rule_name=fromVlan_to_Vlan1
-openflow add $rule_name --priority 1003 --matchvlan 504 --inport $port3 --setvlan 505 --out $port0 $debug 
+rule_name=`openflow add $rule_name --priority 1003 --matchvlan 504 --inport $port3 --setvlan 505 --out $port0 $debug  --print-id`
 [[ $? != 0 ]] && echo  "FAIL cannot insert new rule" && $_exit 1
 expected="$OF_CONTROLLER_DPID 1003 $rule_name $port3 any 504 vlan=505,out=$port0"
 result=`openflow list | grep $rule_name`
@@ -198,7 +198,7 @@ then
 
   printf "%-50s" "new rule Vlan -> Vlan_Vlan: "
   rule_name=fromVlan_to_Vlan1Vlan1
-  openflow add $rule_name --priority 1005 --inport $port3 --matchvlan 505 --setvlan 510 --out $port0 --setvlan 511 --out $port1 --stripvlan --out=$port2 $debug 
+  rule_name=`openflow add $rule_name --priority 1005 --inport $port3 --matchvlan 505 --setvlan 510 --out $port0 --setvlan 511 --out $port1 --stripvlan --out=$port2 $debug --print-id`
   [[ $? != 0 ]] && echo  "FAIL cannot insert new rule" && $_exit 1
   expected="$OF_CONTROLLER_DPID 1005 $rule_name $port3 any 505 vlan=510,out=$port0,vlan=511,out=$port1,vlan=None,out=$port2"
   result=`openflow list | grep $rule_name`
index a458a8e..a024427 100644 (file)
--- a/vim_db.py
+++ b/vim_db.py
@@ -37,6 +37,7 @@ import uuid as myUuid
 import auxiliary_functions as af
 import json
 import logging
+from netaddr import IPNetwork, IPSet, IPRange, all_matching_cidrs
 
 HTTP_Bad_Request = 400
 HTTP_Unauthorized = 401 
@@ -173,7 +174,7 @@ class vim_db():
     def __get_used_net_vlan(self):
         #get used from database if needed
         try:
-            cmd = "SELECT vlan FROM nets WHERE vlan>='%s' and (type='ptp' or type='data') ORDER BY vlan LIMIT 25" % self.net_vlan_lastused
+            cmd = "SELECT vlan FROM nets WHERE vlan>='%s' ORDER BY vlan LIMIT 25" % self.net_vlan_lastused
             with self.con:
                 self.cur = self.con.cursor()
                 self.logger.debug(cmd)
@@ -1035,15 +1036,16 @@ class vim_db():
                 with self.con:
                     self.cur = self.con.cursor(mdb.cursors.DictCursor)
                     #get INSTANCE
-                    cmd = "SELECT uuid, name, description, progress, host_id, flavor_id, image_id, status, last_error, tenant_id, ram, vcpus, created_at \
-                        FROM instances WHERE uuid = '" +  str(instance_id) +"'"
+                    cmd = "SELECT uuid, name, description, progress, host_id, flavor_id, image_id, status, last_error, "\
+                        "tenant_id, ram, vcpus, created_at FROM instances WHERE uuid='{}'".format(instance_id)
                     self.logger.debug(cmd)
                     self.cur.execute(cmd)
                     if self.cur.rowcount == 0 : return 0, "instance '" + str(instance_id) +"'not found."
                     instance = self.cur.fetchone()
                     #get networks
-                    cmd = "SELECT uuid as iface_id, net_id, mac as mac_address, ip_address, name, Mbps as bandwidth, vpci, model \
-                        FROM ports WHERE type = 'instance:bridge' AND instance_id = '" + instance_id + "'"
+                    cmd = "SELECT uuid as iface_id, net_id, mac as mac_address, ip_address, name, Mbps as bandwidth, "\
+                        "vpci, model FROM ports WHERE (type='instance:bridge' or type='instance:ovs') AND "\
+                        "instance_id= '{}'".format(instance_id)
                     self.logger.debug(cmd)
                     self.cur.execute(cmd)
                     if self.cur.rowcount > 0 :
@@ -1113,8 +1115,9 @@ class vim_db():
                                 numa_dict['threads-source'] = thread_source
 
                         #get dedicated ports and SRIOV
-                        cmd = "SELECT port_id as iface_id, p.vlan as vlan, p.mac as mac_address, net_id, if(model='PF','yes',if(model='VF','no','yes:sriov')) as dedicated,\
-                            rp.Mbps as bandwidth, name, vpci, pci as source \
+                        cmd = "SELECT port_id as iface_id, p.vlan as vlan, p.mac as mac_address, net_id, if(model='PF',\
+                            'yes',if(model='VF','no','yes:sriov')) as dedicated, rp.Mbps as bandwidth, name, vpci, \
+                            pci as source \
                             FROM resources_port as rp join ports as p on port_id=uuid  WHERE p.instance_id = '%s' AND numa_id = '%s' and p.type='instance:data'" % (instance_id, numa_id) 
                         self.logger.debug(cmd)
                         self.cur.execute(cmd)
@@ -1395,15 +1398,28 @@ class vim_db():
                     #insert resources
                     nb_bridge_ifaces = nb_cores = nb_ifaces = nb_numas = 0
                     #insert bridged_ifaces
+
                     for iface in bridgedifaces:
                         #generate and insert a iface uuid
+                        if 'enable_dhcp' in iface and iface['enable_dhcp']:
+                            dhcp_first_ip = iface["dhcp_first_ip"]
+                            del iface["dhcp_first_ip"]
+                            dhcp_last_ip = iface["dhcp_last_ip"]
+                            del iface["dhcp_last_ip"]
+                            dhcp_cidr = iface["cidr"]
+                            del iface["cidr"]
+                            del iface["enable_dhcp"]
+                            used_dhcp_ips = self._get_dhcp_ip_used_list(iface["net_id"])
+                            iface["ip_address"] = self.get_free_ip_from_range(dhcp_first_ip, dhcp_last_ip,
+                                                                              dhcp_cidr, used_dhcp_ips)
+
                         iface['uuid'] = str(myUuid.uuid1()) # create_uuid
                         cmd = "INSERT INTO uuids (uuid, root_uuid, used_at) VALUES ('%s','%s', 'ports')" % (iface['uuid'], uuid)
                         self.logger.debug(cmd)
                         self.cur.execute(cmd)
                         #insert iface
                         iface['instance_id'] = uuid
-                        iface['type'] = 'instance:bridge'
+                        iface['type'] = 'instance:bridge'
                         if 'name' not in iface: iface['name']="br"+str(nb_bridge_ifaces)
                         iface['Mbps']=iface.pop('bandwidth', None)
                         if 'mac_address' not in iface:
@@ -1503,7 +1519,59 @@ class vim_db():
                 r,c = self.format_error(e, "new_instance", cmd)
                 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
 
-    def delete_instance(self, instance_id, tenant_id, net_list, ports_to_free, logcause="requested by http"):
+    def get_free_ip_from_range(self, first_ip, last_ip, cidr, ip_used_list):
+        """
+        Calculate a free IP from a range given
+        :param first_ip: First dhcp ip range
+        :param last_ip: Last dhcp ip range
+        :param cidr: net cidr
+        :param ip_used_list: contain all used ips to avoid ip collisions
+        :return:
+        """
+
+        ip_tools = IPNetwork(cidr)
+        cidr_len = ip_tools.prefixlen
+        ips = IPNetwork(first_ip + '/' + str(cidr_len))
+        ip_used_list.append(str(ips[0])) # first ip
+        ip_used_list.append(str(ips[1])) # gw ip
+        ip_used_list.append(str(ips[-1])) # broadcast ip
+        for vm_ip in ips:
+            if str(vm_ip) not in ip_used_list:
+                return vm_ip
+
+        return None
+
+    def _get_dhcp_ip_used_list(self, net_id):
+        """
+        REtreive from DB all ips already used by the dhcp server for a given net
+        :param net_id:
+        :return:
+        """
+        WHERE={'type': 'instance:ovs', 'net_id': net_id}
+        for retry_ in range(0, 2):
+            cmd = ""
+            self.cur = self.con.cursor(mdb.cursors.DictCursor)
+            select_ = "SELECT uuid, ip_address FROM ports "
+
+            if WHERE is None or len(WHERE) == 0:
+                where_ = ""
+            else:
+                where_ = "WHERE " + " AND ".join(
+                    map(lambda x: str(x) + (" is Null" if WHERE[x] is None else "='" + str(WHERE[x]) + "'"),
+                        WHERE.keys()))
+            limit_ = "LIMIT 100"
+            cmd = " ".join((select_, where_, limit_))
+            self.logger.debug(cmd)
+            self.cur.execute(cmd)
+            ports = self.cur.fetchall()
+            ip_address_list = []
+            for port in ports:
+                ip_address_list.append(port['ip_address'])
+
+            return ip_address_list
+
+
+    def delete_instance(self, instance_id, tenant_id, net_dataplane_list, ports_to_free, net_ovs_list, logcause="requested by http"):
         for retry_ in range(0,2):
             cmd=""
             try:
@@ -1523,7 +1591,14 @@ class vim_db():
                     self.cur.execute(cmd)
                     net_list__ = self.cur.fetchall()
                     for net in net_list__:
-                        net_list.append(net[0])
+                        net_dataplane_list.append(net[0])
+
+                    # get ovs manangement nets
+                    cmd = "SELECT DISTINCT net_id, vlan, ip_address, mac FROM ports WHERE instance_id='{}' AND net_id is not Null AND "\
+                            "type='instance:ovs'".format(instance_id)
+                    self.logger.debug(cmd)
+                    self.cur.execute(cmd)
+                    net_ovs_list += self.cur.fetchall()
 
                     #get dataplane interfaces releases by this VM; both PF and VF with no other VF 
                     cmd="SELECT source_name, mac FROM (SELECT root_id, count(instance_id) as used FROM resources_port WHERE instance_id='%s' GROUP BY root_id ) AS A" % instance_id \
@@ -1634,8 +1709,8 @@ class vim_db():
             if net['tenant_id']==tenant_id and net['shared']=='false':
                 return -1, "needed admin privileges to attach to the net %s" % net_id
         #check types
-        if (net['type'] in ('p2p','data') and 'port_type' == 'instance:bridge') or \
-            (net['type'] in ('bridge_data','bridge_man') and 'port_type' != 'instance:bridge') :
+        if (net['type'] in ('p2p','data') and port_type != 'instance:data') or \
+            (net['type'] in ('bridge_data','bridge_man') and port_type not in ('instance:bridge', 'instance:ovs')):
             return -1, "can not attach a port of type %s into a net of type %s" % (port_type, net['type'])
         if net['type'] == 'ptp':
             #look how many 
index 0a5929d..6d508a6 100644 (file)
@@ -117,12 +117,17 @@ config_schema = {
         "log_level": log_level_schema,
         "log_level_db": log_level_schema,
         "log_level_of": log_level_schema,
+        "network_type": {"type": "string", "enum": ["ovs", "bridge"]},
+        "ovs_controller_file_path": path_schema,
+        "ovs_controller_user": nameshort_schema,
+
+        "ovs_controller_ip": nameshort_schema
     },
     "patternProperties": {
         "of_*" : {"type": ["string", "integer", "boolean"]}
     },
     "required": ['db_host', 'db_user', 'db_passwd', 'db_name',
-            'of_controller_ip', 'of_controller_port', 'of_controller_dpid', 'bridge_ifaces', 'of_controller'],
+            'of_controller_ip', 'of_controller_port', 'of_controller_dpid', 'of_controller'],
     "additionalProperties": False
 }
 
@@ -625,8 +630,8 @@ network_update_schema = {
                 "provider:physical":net_bind_schema,
                 "cidr":cidr_schema,
                 "enable_dhcp": {"type":"boolean"},
-                "dhcp_first_ip": ip_schema,
-                "dhcp_last_ip": ip_schema,
+                "dhcp_first_ip": ip_schema,
+                "dhcp_last_ip": ip_schema,
                 "bind_net":name_schema, #can be name, or uuid
                 "bind_type":{"oneOf":[{"type":"null"},{"type":"string", "pattern":"^vlan:[0-9]{1,4}$"}]}
             },