Provider network and dnsmaq conf improvment 43/1943/7
authormirabal <leonardo.mirabal@altran.com>
Mon, 5 Jun 2017 14:19:26 +0000 (09:19 -0500)
committermirabal <leonardo.mirabal@altran.com>
Wed, 5 Jul 2017 09:10:49 +0000 (04:10 -0500)
When dhcp server is launch, now dns servers and default routes can be progated to a vm.
A qrouter is created inside a namespace allowing connections with a provider network and a openvim tenant network.

Change-Id: If2becc010b2886493396c9f6b363980a846a04da
Signed-off-by: mirabal <leonardo.mirabal@altran.com>
database_utils/migrate_vim_db.sh
osm_openvim/host_thread.py
osm_openvim/httpserver.py
osm_openvim/ovim.py
osm_openvim/vim_db.py
osm_openvim/vim_schema.py
test/networks/net-example5.yaml [new file with mode: 0644]

index aded5a0..1a1bf95 100755 (executable)
@@ -33,7 +33,7 @@ DBPORT="3306"
 DBNAME="vim_db"
 QUIET_MODE=""
 #TODO update it with the last database version
-LAST_DB_VERSION=20
+LAST_DB_VERSION=21
 
 # Detect paths
 MYSQL=$(which mysql)
@@ -187,7 +187,8 @@ fi
 #[ $OPENVIM_VER_NUM -ge 5010 ] && DATABASE_TARGET_VER_NUM=17  #0.5.10  => 17
 #[ $OPENVIM_VER_NUM -ge 5013 ] && DATABASE_TARGET_VER_NUM=18  #0.5.13  => 18
 #[ $OPENVIM_VER_NUM -ge 5015 ] && DATABASE_TARGET_VER_NUM=19  #0.5.15  => 19
-#[ $OPENVIM_VER_NUM -ge 5017 ] && DATABASE_TARGET_VER_NUM20   #0.5.17  => 20
+#[ $OPENVIM_VER_NUM -ge 5017 ] && DATABASE_TARGET_VER_NUM=20   #0.5.17  => 20
+#[ $OPENVIM_VER_NUM -ge 5018 ] && DATABASE_TARGET_VER_NUM=21   #0.5.18  => 21
 # TODO ... put next versions here
 
 function upgrade_to_1(){
@@ -703,6 +704,22 @@ function downgrade_from_20(){
     echo "DELETE FROM schema_version WHERE version_int = '20';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
 }
 
+function upgrade_to_21(){
+    echo "    Add 'routes', 'links' and 'dns' to 'nets'"
+    echo "ALTER TABLE nets ADD COLUMN dns VARCHAR(255) NULL AFTER gateway_ip,
+    ADD COLUMN links TEXT(2000)  NULL AFTER dns,
+    ADD COLUMN routes TEXT(2000)  NULL AFTER links;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+    echo "INSERT INTO schema_version (version_int, version, openvim_ver, comments, date) VALUES (21, '0.21', '0.5.18', 'Add routes, links and dns to inets', '2017-06-21');"\
+         | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+}
+
+function downgrade_from_21(){
+    echo "    Delete 'routes', 'links' and 'dns' to 'nets'"
+    echo "ALTER TABLE nets DROP COLUMN dns, DROP COLUMN links, DROP COLUMN routes;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+    echo "DELETE FROM schema_version WHERE version_int = '21';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+}
+
+
 #TODO ... put funtions here
 
 # echo "db version = "${DATABASE_VER_NUM}
index b8f051c..6fe8331 100644 (file)
@@ -813,16 +813,16 @@ class host_thread(threading.Thread):
         if not self.is_dhcp_port_free(vlan, net_uuid):
             return True
         try:
-            net_namespace = 'ovim-' + str(vlan)
-            dhcp_path = os.path.join(dhcp_path, net_namespace)
+            dhcp_namespace = str(vlan) + '-dnsmasq'
+            dhcp_path = os.path.join(dhcp_path, dhcp_namespace)
             pid_file = os.path.join(dhcp_path, 'dnsmasq.pid')
 
-            command = 'sudo ip netns exec ' + net_namespace + ' cat ' + pid_file
+            command = 'sudo ip netns exec ' + dhcp_namespace + ' cat ' + pid_file
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo ip netns exec ' + net_namespace + ' kill -9 ' + content
+            command = 'sudo ip netns exec ' + dhcp_namespace + ' kill -9 ' + content
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
@@ -944,7 +944,7 @@ class host_thread(threading.Thread):
             return True
         try:
             port_name = 'ovim-' + str(vlan)
-            command = 'sudo ip link set dev veth0-' + str(vlan) + ' down'
+            command = 'sudo ip link set dev ovim-' + str(vlan) + ' down'
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             # content = stdout.read()
@@ -965,6 +965,59 @@ class host_thread(threading.Thread):
                 self.ssh_connect()
             return False
 
+    def remove_link_bridge_to_ovs(self, vlan, link):
+        """
+        Delete a linux provider net connection to tenatn net
+        :param vlan: vlan port id
+        :param link: link name
+        :return: True if success
+        """
+
+        if self.test:
+            return True
+        try:
+            br_tap_name = str(vlan) + '-vethBO'
+            br_ovs_name = str(vlan) + '-vethOB'
+
+            # Delete ovs veth pair
+            command = 'sudo ip link set dev {} down'.format(br_ovs_name)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+            command = 'sudo ovs-vsctl del-port br-int {}'.format(br_ovs_name)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+            # Delete br veth pair
+            command = 'sudo ip link set dev {} down'.format(br_tap_name)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+            # Delete br veth interface form bridge
+            command = 'sudo brctl delif {} {}'.format(link, br_tap_name)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+            # Delete br veth pair
+            command = 'sudo ip link set dev {} down'.format(link)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+            if len(content) == 0:
+                return True
+            else:
+                return False
+        except paramiko.ssh_exception.SSHException as e:
+            self.logger.error("delete_linux_bridge ssh Exception: " + str(e))
+            if "SSH session not active" in str(e):
+                self.ssh_connect()
+            return False
+
     def create_ovs_bridge_port(self, vlan):
         """
         Generate a linux bridge and attache the port to a OVS bridge
@@ -1030,7 +1083,7 @@ class host_thread(threading.Thread):
                 self.ssh_connect()
             return False
 
-    def set_mac_dhcp_server(self, ip, mac, vlan, netmask, dhcp_path):
+    def set_mac_dhcp_server(self, ip, mac, vlan, netmask, first_ip, 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
@@ -1044,21 +1097,43 @@ class host_thread(threading.Thread):
         if self.test:
             return True
 
-        net_namespace = 'ovim-' + str(vlan)
-        dhcp_path = os.path.join(dhcp_path, net_namespace)
-        dhcp_hostsdir = os.path.join(dhcp_path, net_namespace)
+        dhcp_namespace = str(vlan) + '-dnsmasq'
+        dhcp_path = os.path.join(dhcp_path, dhcp_namespace)
+        dhcp_hostsdir = os.path.join(dhcp_path, dhcp_namespace)
 
         if not ip:
             return False
         try:
+
+            ns_interface = str(vlan) + '-vethDO'
+            command = 'sudo  ip netns exec ' + dhcp_namespace + ' cat /sys/class/net/{}/address'.format(ns_interface)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            iface_listen_mac = stdout.read()
+
+            if iface_listen_mac > 0:
+                command = 'sudo  ip netns exec ' + dhcp_namespace + ' cat {} | grep {}'.format(dhcp_hostsdir, dhcp_hostsdir)
+                self.logger.debug("command: " + command)
+                (_, stdout, _) = self.ssh_conn.exec_command(command)
+                content = stdout.read()
+                if content > 0:
+                    ip_data = iface_listen_mac.upper().replace('\n', '') + ',' + first_ip
+                    dhcp_hostsdir = os.path.join(dhcp_path, dhcp_namespace)
+
+                    command = 'sudo  ip netns exec ' + dhcp_namespace + ' sudo bash -ec "echo ' + ip_data + ' >> ' + dhcp_hostsdir + '"'
+                    self.logger.debug("command: " + command)
+                    (_, stdout, _) = self.ssh_conn.exec_command(command)
+                    content = stdout.read()
+
+
             ip_data = mac.upper() + ',' + ip
 
-            command = 'sudo  ip netns exec ' + net_namespace + ' touch ' + dhcp_hostsdir
+            command = 'sudo  ip netns exec ' + dhcp_namespace + ' touch ' + dhcp_hostsdir
             self.logger.debug("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 + '"'
+            command = 'sudo  ip netns exec ' + dhcp_namespace + ' sudo bash -ec "echo ' + ip_data + ' >> ' + dhcp_hostsdir + '"'
 
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
@@ -1088,16 +1163,16 @@ class host_thread(threading.Thread):
         if self.test:
             return False
         try:
-            net_namespace = 'ovim-' + str(vlan)
-            dhcp_path = os.path.join(dhcp_path, net_namespace)
-            dhcp_hostsdir = os.path.join(dhcp_path, net_namespace)
+            dhcp_namespace = str(vlan) + '-dnsmasq'
+            dhcp_path = os.path.join(dhcp_path, dhcp_namespace)
+            dhcp_hostsdir = os.path.join(dhcp_path, dhcp_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
+            command = 'sudo  ip netns exec ' + dhcp_namespace + ' sudo sed -i \'/' + ip_data + '/d\' ' + dhcp_hostsdir
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
@@ -1113,7 +1188,7 @@ class host_thread(threading.Thread):
                 self.ssh_connect()
             return False
 
-    def launch_dhcp_server(self, vlan, ip_range, netmask, dhcp_path, gateway):
+    def launch_dhcp_server(self, vlan, ip_range, netmask, dhcp_path, gateway, dns_list=None, routes=None):
         """
         Generate a linux bridge and attache the port to a OVS bridge
         :param self:
@@ -1122,30 +1197,34 @@ class host_thread(threading.Thread):
         :param netmask: network netmask
         :param dhcp_path: dhcp conf file path that live in namespace side
         :param gateway: Gateway address for dhcp net
+        :param dns_list: dns list for dhcp server
+        :param routes: routes list for dhcp server
         :return: True if success
         """
 
         if self.test:
             return True
         try:
-            interface = 'tap-' + str(vlan)
-            net_namespace = 'ovim-' + str(vlan)
-            dhcp_path = os.path.join(dhcp_path, net_namespace)
+            ns_interface = str(vlan) + '-vethDO'
+            dhcp_namespace = str(vlan) + '-dnsmasq'
+            dhcp_path = os.path.join(dhcp_path, dhcp_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
+            command = 'sudo ip netns exec ' + dhcp_namespace + ' mkdir -p ' + dhcp_path
             self.logger.debug("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
+            command = 'sudo  ip netns exec ' + dhcp_namespace + ' cat ' + pid_path
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
+
             # check if pid is runing
             pid_status_path = content
             if content:
@@ -1153,11 +1232,34 @@ class host_thread(threading.Thread):
                 self.logger.debug("command: " + command)
                 (_, stdout, _) = self.ssh_conn.exec_command(command)
                 content = stdout.read()
+
+            gateway_option = ' --dhcp-option=3,' + gateway
+
+            dhcp_route_option = ''
+            if routes:
+                dhcp_route_option = ' --dhcp-option=121'
+                for key, value in routes.iteritems():
+                        if 'default' == key:
+                            gateway_option = ' --dhcp-option=3,' + value
+                        else:
+                            dhcp_route_option += ',' + key + ',' + value
+            dns_data = ''
+            if dns_list:
+                dns_data = ' --dhcp-option=6'
+                for dns in dns_list:
+                    dns_data += ',' + dns
+
             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 ' + gateway
+                command = 'sudo  ip netns exec ' + dhcp_namespace + ' /usr/sbin/dnsmasq --strict-order --except-interface=lo ' \
+                          '--interface=' + ns_interface + \
+                          ' --bind-interfaces --dhcp-hostsdir=' + dhcp_path + \
+                          ' --dhcp-range ' + dhcp_range + \
+                          ' --pid-file=' + pid_file + \
+                          ' --dhcp-leasefile=' + leases_path + \
+                          ' --listen-address ' + ip_range[0] + \
+                          gateway_option + \
+                          dhcp_route_option + \
+                          dns_data
 
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
@@ -1183,21 +1285,35 @@ class host_thread(threading.Thread):
         if self.test:
             return True
         try:
-            net_namespace = 'ovim-' + str(vlan)
-            command = 'sudo ovs-vsctl del-port br-int ovs-tap-' + str(vlan)
+            br_veth_name = str(vlan) + '-vethDO'
+            ovs_veth_name = str(vlan) + '-vethOD'
+            dhcp_namespace = str(vlan) + '-dnsmasq'
+
+            command = 'sudo ovs-vsctl del-port br-int ' + ovs_veth_name
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo ip netns exec ' + net_namespace + ' ip link set dev tap-' + str(vlan) + ' down'
+            command = 'sudo ip netns exec ' + dhcp_namespace + ' ip link set dev ' + br_veth_name + ' down'
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo ip link set dev ovs-tap-' + str(vlan) + ' down'
+            command = 'sudo ip link set dev ' + dhcp_namespace + ' down'
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
+
+            command = 'sudo brctl delbr ' + dhcp_namespace
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+            command = 'sudo ip netns del ' + dhcp_namespace
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
         except paramiko.ssh_exception.SSHException as e:
             self.logger.error("delete_dhcp_interfaces ssh Exception: " + str(e))
             if "SSH session not active" in str(e):
@@ -1216,50 +1332,50 @@ class host_thread(threading.Thread):
         if self.test:
             return True
         try:
-            net_namespace = 'ovim-' + str(vlan)
-            namespace_interface = 'tap-' + str(vlan)
+            ovs_veth_name = str(vlan) + '-vethOD'
+            ns_veth = str(vlan) + '-vethDO'
+            dhcp_namespace = str(vlan) + '-dnsmasq'
 
-            command = 'sudo ip netns add ' + net_namespace
+            command = 'sudo ip netns add ' + dhcp_namespace
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo ip link add tap-' + str(vlan) + ' type veth peer name ovs-tap-' + str(vlan)
+            command = 'sudo ip link add ' + ns_veth + ' type veth peer name ' + ovs_veth_name
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo ovs-vsctl add-port br-int ovs-tap-' + str(vlan) + ' tag=' + str(vlan)
+            command = 'sudo ip link set ' + ns_veth + ' netns ' + dhcp_namespace
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo ip link set tap-' + str(vlan) + ' netns ' + net_namespace
+            command = 'sudo ip netns exec ' + dhcp_namespace + ' ip link set dev ' + ns_veth + ' up'
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo ip netns exec ' + net_namespace + ' ip link set dev tap-' + str(vlan) + ' up'
+            command = 'sudo ovs-vsctl add-port br-int ' + ovs_veth_name + ' tag=' + str(vlan)
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo ip link set dev ovs-tap-' + str(vlan) + ' up'
+            command = 'sudo ip link set dev ' + ovs_veth_name + ' up'
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo ip netns exec ' + net_namespace + ' ip link set dev lo up'
+            command = 'sudo ip netns exec ' + dhcp_namespace + ' ip link set dev lo up'
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
 
-            command = 'sudo  ip netns exec ' + net_namespace + ' ' + ' ifconfig  ' + namespace_interface \
+            command = 'sudo  ip netns exec ' + dhcp_namespace + ' ' + ' ifconfig  ' + ns_veth \
                       + ' ' + ip_listen_address + ' netmask ' + netmask
             self.logger.debug("command: " + command)
             (_, stdout, _) = self.ssh_conn.exec_command(command)
             content = stdout.read()
-
             if len(content) == 0:
                 return True
             else:
@@ -1270,6 +1386,266 @@ class host_thread(threading.Thread):
                 self.ssh_connect()
             return False
 
+    def delete_qrouter_connection(self, vlan, link):
+        """
+        Delete qrouter Namesapce with all veth interfaces need it
+        :param vlan: 
+        :param link: 
+        :return: 
+        """
+
+        ns_qouter = str(vlan) + '-qrouter'
+        qrouter_ovs_veth = str(vlan) + '-vethOQ'
+        qrouter_ns_veth = str(vlan) + '-vethQO'
+
+        qrouter_br_veth = str(vlan) + '-vethBQ'
+        qrouter_ns_router_veth = str(vlan) + '-vethQB'
+
+        # delete ovs veth to ovs br-int
+        command = 'sudo ovs-vsctl del-port br-int {}'.format(qrouter_ovs_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # down ns veth
+        command = 'sudo ip netns exec {} ip link set dev {} down'.format(ns_qouter, qrouter_ns_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # down ovs veth interface
+        command = 'sudo ip link set dev {} down'.format(qrouter_br_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # down br veth interface
+        command = 'sudo ip link set dev {} down'.format(qrouter_ovs_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # down br veth interface
+        command = 'sudo ip link set dev {} down'.format(qrouter_ns_router_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # down br veth interface
+        command = 'sudo brctl delif {} {}'.format(link, qrouter_br_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+
+        # delete NS
+        command = 'sudo ip netns del ' + ns_qouter
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+    def create_qrouter_ovs_connection(self, vlan, gateway, dhcp_cidr):
+        """
+        Create qrouter Namesapce with all veth interfaces need it between NS and OVS
+        :param vlan: 
+        :param gateway: 
+        :return: 
+        """
+
+        ns_qouter = str(vlan) + '-qrouter'
+        qrouter_ovs_veth = str(vlan) + '-vethOQ'
+        qrouter_ns_veth = str(vlan) + '-vethQO'
+
+        # Create NS
+        command = 'sudo ip netns add ' + ns_qouter
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # Create pait veth
+        command = 'sudo ip link add {} type veth peer name {}'.format(qrouter_ns_veth, qrouter_ovs_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # up ovs veth interface
+        command = 'sudo ip link set dev {} up'.format(qrouter_ovs_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # add ovs veth to ovs br-int
+        command = 'sudo ovs-vsctl add-port br-int {} tag={}'.format(qrouter_ovs_veth, vlan)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # add veth to ns
+        command = 'sudo ip link set {} netns {}'.format(qrouter_ns_veth, ns_qouter)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # up ns loopback
+        command = 'sudo ip netns exec {} ip link set dev lo up'.format(ns_qouter)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # up ns veth
+        command = 'sudo ip netns exec {} ip link set dev {} up'.format(ns_qouter, qrouter_ns_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        from netaddr import IPNetwork
+        ip_tools = IPNetwork(dhcp_cidr)
+        cidr_len = ip_tools.prefixlen
+
+        # set gw to ns veth
+        command = 'sudo ip netns exec {} ip address add {}/{} dev {}'.format(ns_qouter, gateway, cidr_len, qrouter_ns_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+    def add_ns_routes(self, vlan, routes):
+
+        for key, value in routes.iteritems():
+            ns_qouter = str(vlan) + '-qrouter'
+            qrouter_ns_router_veth = str(vlan) + '-vethQB'
+            # up ns veth
+            if key == 'default':
+                command = 'sudo ip netns exec {} ip route add {} via {} '.format(ns_qouter,  key, value)
+            else:
+                command = 'sudo ip netns exec {} ip route add {} via {} dev {}'.format(ns_qouter, key, value,
+                                                                                       qrouter_ns_router_veth)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+    def create_qrouter_br_connection(self, vlan, cidr, link):
+        """
+        Create veth interfaces between user bridge (link) and OVS
+        :param vlan: 
+        :param link: 
+        :return: 
+        """
+
+        ns_qouter = str(vlan) + '-qrouter'
+        qrouter_ns_router_veth = str(vlan) + '-vethQB'
+        qrouter_br_veth = str(vlan) + '-vethBQ'
+
+        # Create pait veth
+        command = 'sudo ip link add {} type veth peer name {}'.format(qrouter_br_veth, qrouter_ns_router_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # up ovs veth interface
+        command = 'sudo ip link set dev {} up'.format(qrouter_br_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # add veth to ns
+        command = 'sudo ip link set {} netns {}'.format(qrouter_ns_router_veth, ns_qouter)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        # up ns veth
+        command = 'sudo ip netns exec {} ip link set dev {} up'.format(ns_qouter, qrouter_ns_router_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo ip netns exec {} ip address add {} dev {}'.format(ns_qouter, link['nat'], qrouter_ns_router_veth)
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        command = 'sudo brctl show | grep {}'.format(link['iface'])
+        self.logger.debug("command: " + command)
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        if content > '':
+            # up ns veth
+            command = 'sudo brctl addif {} {}'.format(link['iface'], qrouter_br_veth)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+            # up ns veth
+            command = 'sudo ip netns exec {} iptables -t nat -A POSTROUTING -o {} -s {} -d {} -j MASQUERADE' \
+                .format(ns_qouter, qrouter_ns_router_veth, link['nat'], cidr)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+
+        else:
+            self.logger.error('Bridge {} given by user not exist'.format(qrouter_br_veth))
+
+
+
+    def create_link_bridge_to_ovs(self, vlan, link):
+        """
+        Create interfaces to connect a linux bridge with tenant net
+        :param vlan: segmentation id
+        :return: True if success
+        """
+        if self.test:
+            return True
+        try:
+
+            br_tap_name = str(vlan) + '-vethBO'
+            br_ovs_name = str(vlan) + '-vethOB'
+
+            # is a bridge or a interface
+            command = 'sudo brctl show | grep {}'.format(link)
+            self.logger.debug("command: " + command)
+            (_, stdout, _) = self.ssh_conn.exec_command(command)
+            content = stdout.read()
+
+            if content > '':
+                command = 'sudo ip link add {} type veth peer name {}'.format(br_tap_name, br_ovs_name)
+                self.logger.debug("command: " + command)
+                (_, stdout, _) = self.ssh_conn.exec_command(command)
+                content = stdout.read()
+
+                command = 'sudo ip link set dev {}  up'.format(br_tap_name)
+                self.logger.debug("command: " + command)
+                (_, stdout, _) = self.ssh_conn.exec_command(command)
+                content = stdout.read()
+
+                command = 'sudo ip link set dev {}  up'.format(br_ovs_name)
+                self.logger.debug("command: " + command)
+                (_, stdout, _) = self.ssh_conn.exec_command(command)
+                content = stdout.read()
+
+                command = 'sudo ovs-vsctl add-port br-int {} tag={}'.format(br_ovs_name, str(vlan))
+                self.logger.debug("command: " + command)
+                (_, stdout, _) = self.ssh_conn.exec_command(command)
+                content = stdout.read()
+
+                command = 'sudo brctl addif ' + link + ' {}'.format(br_tap_name)
+                self.logger.debug("command: " + command)
+                (_, stdout, _) = self.ssh_conn.exec_command(command)
+                content = stdout.read()
+
+                if len(content) == 0:
+                    return True
+                else:
+                    return False
+            else:
+                self.logger.error('Link is not present, please check {}'.format(link))
+                return False
+        except paramiko.ssh_exception.SSHException as e:
+            self.logger.error("create_dhcp_interfaces ssh Exception: " + str(e))
+            if "SSH session not active" in str(e):
+                self.ssh_connect()
+            return False
 
     def create_ovs_vxlan_tunnel(self, vxlan_interface, remote_ip):
         """
@@ -2344,8 +2720,8 @@ def create_server(server, db, db_lock, only_of_ports):
         #Get the brifge name
         db_lock.acquire()
         result, content = db.get_table(FROM='nets',
-                                       SELECT=('name', 'type', 'vlan', 'provider', 'enable_dhcp',
-                                                 'dhcp_first_ip', 'dhcp_last_ip', 'cidr'),
+                                       SELECT=('name', 'type', 'vlan', 'provider', 'enable_dhcp','dhcp_first_ip',
+                                               'dhcp_last_ip', 'cidr', 'gateway_ip', 'dns', 'links', 'routes'),
                                        WHERE={'uuid': control_iface['net_id']})
         db_lock.release()
         if result < 0: 
@@ -2370,6 +2746,13 @@ def create_server(server, db, db_lock, only_of_ports):
                     control_iface["dhcp_first_ip"] = network["dhcp_first_ip"]
                     control_iface["dhcp_last_ip"] = network["dhcp_last_ip"]
                     control_iface["cidr"] = network["cidr"]
+
+                if network.get("dns"):
+                    control_iface["dns"] = yaml.safe_load(network.get("dns"))
+                if network.get("links"):
+                    control_iface["links"] = yaml.safe_load(network.get("links"))
+                if network.get("routes"):
+                    control_iface["routes"] = yaml.safe_load(network.get("routes"))
             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 7126e21..e95c820 100644 (file)
@@ -157,15 +157,25 @@ http2db_network={'id':'uuid','provider:vlan':'vlan', 'provider:physical': 'provi
 http2db_ofc = {'id': 'uuid'}
 http2db_port={'id':'uuid', 'network_id':'net_id', 'mac_address':'mac', 'device_owner':'type','device_id':'instance_id','binding:switch_port':'switch_port','binding:vlan':'vlan', 'bandwidth':'Mbps'}
 
+
 def remove_extra_items(data, schema):
+    import re
+
     deleted=[]
     if type(data) is tuple or type(data) is list:
         for d in data:
             a= remove_extra_items(d, schema['items'])
             if a is not None: deleted.append(a)
     elif type(data) is dict:
+
         for k in data.keys():
-            if 'properties' not in schema or k not in schema['properties'].keys():
+            if 'patternProperties' in schema and k not in schema['properties'].keys():
+                reg_ex_list = schema['patternProperties'].keys()
+                for reg_ex in reg_ex_list:
+                    if not re.match(reg_ex, k):
+                        del data[k]
+                        deleted.append(k)
+            elif 'properties' not in schema or k not in schema['properties'].keys(): # or k not in schema['patternProperties'].keys():
                 del data[k]
                 deleted.append(k)
             else:
@@ -174,7 +184,8 @@ def remove_extra_items(data, schema):
     if len(deleted) == 0: return None
     elif len(deleted) == 1: return deleted[0]
     else: return deleted
-                
+
+
 def delete_nulls(var):
     if type(var) is dict:
         for k in var.keys():
@@ -648,7 +659,7 @@ def http_post_hosts():
                 # create bridge
                 create_dhcp_ovs_bridge()
                 config_dic['host_threads'][content['uuid']].insert_task("new-ovsbridge")
-                # check if more host exist
+                # create vlxan bwt OVS controller and computes
                 create_vxlan_mesh(content['uuid'])
 
         # return host data
@@ -674,8 +685,8 @@ def delete_dhcp_ovs_bridge(vlan, net_uuid):
     http_controller = config_dic['http_threads'][threading.current_thread().name]
     dhcp_controller = http_controller.ovim.get_dhcp_controller()
 
-    dhcp_controller.delete_dhcp_port(vlan, net_uuid)
     dhcp_controller.delete_dhcp_server(vlan, net_uuid, dhcp_path)
+    dhcp_controller.delete_dhcp_port(vlan, net_uuid)
 
 
 def create_dhcp_ovs_bridge():
@@ -713,7 +724,7 @@ def set_mac_dhcp(vm_ip, vlan, first_ip, last_ip, cidr, mac):
     http_controller = config_dic['http_threads'][threading.current_thread().name]
     dhcp_controller = http_controller.ovim.get_dhcp_controller()
 
-    dhcp_controller.set_mac_dhcp_server(vm_ip, mac, vlan, dhcp_netmask, dhcp_path)
+    dhcp_controller.set_mac_dhcp_server(vm_ip, mac, vlan, dhcp_netmask, first_ip, dhcp_path)
 
 
 def delete_mac_dhcp(vm_ip, vlan, mac):
@@ -1614,7 +1625,7 @@ def http_post_server_id(tenant_id):
             if net['type'] == 'instance:ovs':
                 dhcp_nets_id.append(get_network_id(net['net_id']))
 
-        ports_to_free=[]
+        ports_to_free = []
         new_instance_result, new_instance = my.db.new_instance(content, nets, ports_to_free)
         if new_instance_result < 0:
             print "Error http_post_servers() :", new_instance_result, new_instance
@@ -1623,6 +1634,8 @@ def http_post_server_id(tenant_id):
         print
         print "inserted at DB"
         print
+
+
         for port in ports_to_free:
             r,c = config_dic['host_threads'][ server['host_id'] ].insert_task( 'restore-iface',*port )
             if r < 0:
@@ -1652,14 +1665,24 @@ def http_post_server_id(tenant_id):
                         dhcp_enable = bool(server_net['network']['enable_dhcp'])
                         vm_dhcp_ip = c2[0]["ip_address"]
                         config_dic['host_threads'][server['host_id']].insert_task("create-ovs-bridge-port", vlan)
+
+                        dns = yaml.safe_load(server_net['network'].get("dns"))
+                        routes = yaml.safe_load(server_net['network'].get("routes"))
+                        links = yaml.safe_load(server_net['network'].get("links"))
                         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'])
                             gateway = str(server_net['network']['gateway_ip'])
-                            set_mac_dhcp(vm_dhcp_ip, vlan, dhcp_firt_ip, dhcp_last_ip, dhcp_cidr, c2[0]['mac'])
+
                             http_controller = config_dic['http_threads'][threading.current_thread().name]
-                            http_controller.ovim.launch_dhcp_server(vlan, dhcp_firt_ip, dhcp_last_ip, dhcp_cidr, gateway)
+                            http_controller.ovim.launch_dhcp_server(vlan, dhcp_firt_ip, dhcp_last_ip,
+                                                                    dhcp_cidr, gateway, dns, routes)
+                            set_mac_dhcp(vm_dhcp_ip, vlan, dhcp_firt_ip, dhcp_last_ip, dhcp_cidr, c2[0]['mac'])
+
+                        if links:
+                            http_controller.ovim.launch_link_bridge_to_ovs(vlan, gateway, dhcp_cidr, links, routes)
+
 
         #Start server
         server['uuid'] = new_instance
@@ -1679,6 +1702,7 @@ def http_post_server_id(tenant_id):
         bottle.abort(HTTP_Bad_Request, content)
         return
 
+
 def http_server_action(server_id, tenant_id, action):
     '''Perform actions over a server as resume, reboot, terminate, ...'''
     my = config_dic['http_threads'][ threading.current_thread().name ]
@@ -1830,14 +1854,22 @@ 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)
+        # delete ovs-port and linux bridge, contains a list of tuple (net_id,vlan, vm_ip, mac)
+
         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)
+
+            net_data = my.ovim.show_network(net_id)
+            if net_data.get('links'):
+                links = yaml.load(net_data.get('links'))
+                my.ovim.delete_link_bridge_to_ovs(vlan, links)
+
             config_dic['host_threads'][server['host_id']].insert_task('del-ovs-port', vlan, net_id)
     if warn_text:
         data["result"] += warn_text
@@ -1956,7 +1988,7 @@ def http_post_networks():
 
     try:
         # parse input data
-        http_content = format_in(network_new_schema )
+        http_content = format_in(network_new_schema)
         r = remove_extra_items(http_content, network_new_schema)
         if r is not None:
             print "http_post_networks: Warning: remove extra items ", r
index 6472cc1..2fca38f 100755 (executable)
@@ -28,6 +28,7 @@ Two thread will be launched, with normal and administrative permissions.
 """
 
 import threading
+import yaml
 import vim_db
 import logging
 # import imp
@@ -42,9 +43,9 @@ import openflow_conn
 
 __author__ = "Alfonso Tierno, Leonardo Mirabal"
 __date__ = "$06-Feb-2017 12:07:15$"
-__version__ = "0.5.17-r533"
+__version__ = "0.5.18-r534"
 version_date = "Jun 2017"
-database_version = 20      #needed database schema version
+database_version = 21      #needed database schema version
 
 HTTP_Bad_Request =          400
 HTTP_Unauthorized =         401
@@ -146,7 +147,7 @@ class ovim():
             if "dhcp_last_ip" not in network:
                 network["dhcp_last_ip"] = str(ips[-2])
             if "gateway_ip" not in network:
-                network["gateway_ip"] = str(ips[2])
+                network["gateway_ip"] = str(ips[1])
 
             return True
         else:
@@ -285,11 +286,16 @@ class ovim():
             if (net_type == 'bridge_data' or net_type == 'bridge_man') and \
                     net["provider"][:4] == 'OVS:' and net["enable_dhcp"] == "true":
                 try:
-                    self.launch_dhcp_server(net['vlan'],
-                                            net['dhcp_first_ip'],
-                                            net['dhcp_last_ip'],
-                                            net['cidr'],
-                                            net['gateway_ip'])
+                    routes = yaml.safe_load(net.get('routes'))
+                    dns = yaml.safe_load(net.get('dns'))
+                    self.launch_dhcp_server(net.get('vlan'),
+                                            net.get('dhcp_first_ip'),
+                                            net.get('dhcp_last_ip'),
+                                            net.get('cidr'),
+                                            net.get('gateway_ip'),
+                                            dns,
+                                            routes)
+                    self.launch_link_bridge_to_ovs(net['vlan'], net.get('links'), net.get('routes'))
                 except Exception as e:
                     self.logger.error("Fail at launching dhcp server for net_id='%s' net_name='%s': %s",
                                       net["uuid"], net["name"], str(e))
@@ -640,6 +646,13 @@ class ovim():
         dhcp_integrity = True
         if 'enable_dhcp' in network and network['enable_dhcp']:
             dhcp_integrity = self._check_dhcp_data_integrity(network)
+        
+        if network.get('links'):
+            network['links'] = yaml.safe_dump(network['links'], default_flow_style=True, width=256)
+        if network.get('dns'):
+            network['dns'] = yaml.safe_dump(network['dns'], default_flow_style=True, width=256)
+        if network.get('routes'):
+            network['routes'] = yaml.safe_dump(network['routes'], default_flow_style=True, width=256)
 
         result, content = self.db.new_row('nets', network, True, True)
 
@@ -1392,7 +1405,7 @@ class ovim():
 
         return dhcp_host
 
-    def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway):
+    def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway, dns, routes):
         """
         Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
         :param vlan: vlan identifier
@@ -1409,9 +1422,49 @@ class ovim():
         dhcp_path = self.config['ovs_controller_file_path']
 
         controller_host = self.get_dhcp_controller()
-        controller_host.create_linux_bridge(vlan)
-        controller_host.create_dhcp_interfaces(vlan, gateway, dhcp_netmask)
-        controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway)
+        # TODO leo check if is need ti to create an ovim-vlan bridge, looks like not
+        # controller_host.create_linux_bridge(vlan)
+        controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
+        dhcp_path = self.config['ovs_controller_file_path']
+        controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway, dns, routes)
+
+    def launch_link_bridge_to_ovs(self, vlan, gateway, dhcp_cidr, links=None, routes=None):
+        """
+        Launch creating of connections (veth) between user bridge (link) and OVS
+        :param vlan: 
+        :param gateway: 
+        :param links: 
+        :return: 
+        """
+
+        if links:
+            controller_host = self.get_dhcp_controller()
+            for link in links:
+                if 'iface' in link and 'nat' not in link:
+                        controller_host.create_link_bridge_to_ovs(vlan, link['iface'])
+                elif 'nat' in link:
+                    controller_host.create_qrouter_ovs_connection(vlan, gateway, dhcp_cidr)
+                    controller_host.create_qrouter_br_connection(vlan, dhcp_cidr, link)
+
+            if len(routes):
+                controller_host.add_ns_routes(vlan, routes)
+
+    def delete_link_bridge_to_ovs(self, vlan,  links=None):
+        """
+        Delete connections (veth) between user bridge (link) and OVS
+        :param vlan: 
+        :param links: 
+        :return: 
+        """
+        if links:
+            controller_host = self.get_dhcp_controller()
+
+            for link in links:
+                if 'iface' in link and 'nat' not in link:
+                    controller_host.remove_link_bridge_to_ovs(vlan, link['iface'])
+                elif 'nat' in link:
+                    controller_host.delete_qrouter_connection(vlan,  link['iface'])
+
 
 if __name__ == "__main__":
 
index 5fc7c91..0c2d9f1 100644 (file)
@@ -1428,6 +1428,9 @@ class vim_db():
                             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)
+                            del iface['links']
+                            del iface['dns']
+                            del iface['routes']
 
                         iface['uuid'] = str(myUuid.uuid1()) # create_uuid
                         cmd = "INSERT INTO uuids (uuid, root_uuid, used_at) VALUES ('%s','%s', 'ports')" % (iface['uuid'], uuid)
@@ -1443,6 +1446,7 @@ class vim_db():
                         else:
                             iface['mac'] = iface['mac_address']
                             del iface['mac_address']
+
                         #iface['mac']=iface.pop('mac_address', None)  #for leaving mac generation to libvirt
                         keys    = ",".join(iface.keys())
                         values  = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", iface.values() ) )
@@ -1552,6 +1556,7 @@ class vim_db():
 
         ip_used_list.append(str(ips[1]))  # gw ip
         ip_used_list.append(str(ips[-1]))  # broadcast ip
+        ip_used_list.append(first_ip)
 
         for vm_ip in ips:
             if str(vm_ip) not in ip_used_list and IPAddress(first_ip) <= IPAddress(vm_ip) <= IPAddress(last_ip):
index 1925f05..4689a89 100644 (file)
@@ -603,10 +603,26 @@ network_new_schema = {
                 "enable_dhcp": {"type": "boolean"},
                 "dhcp_first_ip": ip_schema,
                 "dhcp_last_ip": ip_schema,
+                "dns": {"type": "array", "items": [ip_schema]},
+                "links":    {"type": "array", "items": {"type": "object", "properties": {
+                                "nat": cidr_schema,
+                                "iface": name_schema,
+                                "vlan": vlan_schema},
+                                "required": ["iface"],
+                                "additionalProperties": False
+                                },
+
+                             },
+                "routes":   {"type": "object", "properties":  {"default": ip_schema}, "patternProperties": {
+                                                            "^([0-9]{1,3}.){3}[0-9]{1,3}/[0-9]{1,2}$": ip_schema,
+                                                            },
+                                                            "additionalProperties": False
+                             },
+
                 "bind_net": name_schema,   # can be name, or uuid
                 "bind_type": {"oneOf": [{"type": "null"}, {"type": "string", "pattern": "^vlan:[0-9]{1,4}$"}]}
             },
-            "required": ["name"]
+        "required": ["name"]
         }
     },
     "required": ["network"],
diff --git a/test/networks/net-example5.yaml b/test/networks/net-example5.yaml
new file mode 100644 (file)
index 0000000..8dc865c
--- /dev/null
@@ -0,0 +1,20 @@
+network:
+    name:  mgmt
+    type: bridge_man
+    shared: True
+    cidr: 192.168.11.0/24
+    enable_dhcp: True
+    dhcp_first_ip: 192.168.11.16
+    dhcp_last_ip: 192.168.11.200
+    dns:
+    -   8.8.8.8
+    -   8.8.4.4
+    links:
+    -  nat: 10.250.0.3/24
+       iface: bridge-osm            # Needs to be create in advance by the user
+    -  nat: 10.90.80.5/24
+       iface: bridge-internet       # Needs to be create in advance by the user
+    - iface: bridge-private         # Needs to be create in advance by the user
+    routes:
+      default: 192.168.11.1         # route will be propagate via dhcp server, GW can not be in the dhcp range
+      10.90.80.0/24: 192.168.11.20  # route will be propagate via dhcp server