Fix support of multi-segment networks in Openstack; added created_items in all vimconns 16/7216/4
authorgarciadeblas <gerardo.garciadeblas@telefonica.com>
Thu, 31 Jan 2019 16:01:31 +0000 (16:01 +0000)
committergarciadeblas <gerardo.garciadeblas@telefonica.com>
Thu, 14 Mar 2019 16:34:50 +0000 (17:34 +0100)
Change-Id: I641a337e58138d693970616360dfb9aab021fd2a
Signed-off-by: garciadeblas <gerardo.garciadeblas@telefonica.com>
14 files changed:
Dockerfile
docker/Dockerfile-local
osm_ro/nfvo.py
osm_ro/vim_thread.py
osm_ro/vimconn.py
osm_ro/vimconn_aws.py
osm_ro/vimconn_opennebula.py
osm_ro/vimconn_openstack.py
osm_ro/vimconn_openvim.py
osm_ro/vimconn_vmware.py
requirements.txt
scripts/install-openmano.sh
setup.py
stdeb.cfg

index 6f1f208..68a6890 100644 (file)
@@ -31,7 +31,7 @@ RUN  apt-get update && \
   DEBIAN_FRONTEND=noninteractive apt-get -y install software-properties-common && \
   DEBIAN_FRONTEND=noninteractive add-apt-repository -y cloud-archive:queens && \
   DEBIAN_FRONTEND=noninteractive apt-get update && \
   DEBIAN_FRONTEND=noninteractive apt-get -y install software-properties-common && \
   DEBIAN_FRONTEND=noninteractive add-apt-repository -y cloud-archive:queens && \
   DEBIAN_FRONTEND=noninteractive apt-get update && \
-  DEBIAN_FRONTEND=noninteractive apt-get -y install python-novaclient python-keystoneclient python-glanceclient python-cinderclient python-neutronclient && \
+  DEBIAN_FRONTEND=noninteractive apt-get -y install python-novaclient python-keystoneclient python-glanceclient python-cinderclient python-neutronclient python-networking-l2gw && \
   DEBIAN_FRONTEND=noninteractive pip install -U progressbar pyvmomi pyvcloud==19.1.1 && \
   DEBIAN_FRONTEND=noninteractive apt-get -y install python-argcomplete python-bottle python-cffi python-packaging python-paramiko python-pkgconfig libmysqlclient-dev libssl-dev libffi-dev python-mysqldb && \
   DEBIAN_FRONTEND=noninteractive apt-get -y install python-logutils python-openstackclient python-openstacksdk && \
   DEBIAN_FRONTEND=noninteractive pip install -U progressbar pyvmomi pyvcloud==19.1.1 && \
   DEBIAN_FRONTEND=noninteractive apt-get -y install python-argcomplete python-bottle python-cffi python-packaging python-paramiko python-pkgconfig libmysqlclient-dev libssl-dev libffi-dev python-mysqldb && \
   DEBIAN_FRONTEND=noninteractive apt-get -y install python-logutils python-openstackclient python-openstacksdk && \
index 8ec5e33..86bc994 100644 (file)
@@ -10,7 +10,7 @@ RUN apt-get update && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install wget tox && \
     DEBIAN_FRONTEND=noninteractive pip2 install pip==9.0.3 && \
     DEBIAN_FRONTEND=noninteractive pip2 install -U progressbar pyvmomi pyvcloud==19.1.1 && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install wget tox && \
     DEBIAN_FRONTEND=noninteractive pip2 install pip==9.0.3 && \
     DEBIAN_FRONTEND=noninteractive pip2 install -U progressbar pyvmomi pyvcloud==19.1.1 && \
-    DEBIAN_FRONTEND=noninteractive apt-get -y install python-novaclient python-keystoneclient python-glanceclient python-cinderclient python-neutronclient && \
+    DEBIAN_FRONTEND=noninteractive apt-get -y install python-novaclient python-keystoneclient python-glanceclient python-cinderclient python-neutronclient python-networking-l2gw && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install python-cffi libmysqlclient-dev libssl-dev libffi-dev python-mysqldb && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install python-openstacksdk python-openstackclient && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install python-networkx && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install python-cffi libmysqlclient-dev libssl-dev libffi-dev python-mysqldb && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install python-openstacksdk python-openstackclient && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install python-networkx && \
index ebb3097..677c7c6 100644 (file)
@@ -2408,7 +2408,7 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                                                 str(iface.get("vnfd-id-ref"))[:255]),
                                             httperrors.Bad_Request)
                     interface_uuid = existing_ifaces[0]["uuid"]
                                                 str(iface.get("vnfd-id-ref"))[:255]),
                                             httperrors.Bad_Request)
                     interface_uuid = existing_ifaces[0]["uuid"]
-                    if existing_ifaces[0]["iface_type"] == "data" and not db_sce_net["type"]:
+                    if existing_ifaces[0]["iface_type"] == "data":
                         db_sce_net["type"] = "data"
                     sce_interface_uuid = str(uuid4())
                     uuid_list.append(sce_net_uuid)
                         db_sce_net["type"] = "data"
                     sce_interface_uuid = str(uuid4())
                     uuid_list.append(sce_net_uuid)
@@ -2634,7 +2634,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
             #We should use the dictionary as input parameter for new_network
             #print myNetDict
             if not sce_net["external"]:
             #We should use the dictionary as input parameter for new_network
             #print myNetDict
             if not sce_net["external"]:
-                network_id = myvim.new_network(myNetName, myNetType, myNetIPProfile)
+                network_id, _ = myvim.new_network(myNetName, myNetType, myNetIPProfile)
                 #print "New VIM network created for scenario %s. Network id:  %s" % (scenarioDict['name'],network_id)
                 sce_net['vim_id'] = network_id
                 auxNetDict['scenario'][sce_net['uuid']] = network_id
                 #print "New VIM network created for scenario %s. Network id:  %s" % (scenarioDict['name'],network_id)
                 sce_net['vim_id'] = network_id
                 auxNetDict['scenario'][sce_net['uuid']] = network_id
@@ -2667,7 +2667,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
                 #print myNetDict
                 #TODO:
                 #We should use the dictionary as input parameter for new_network
                 #print myNetDict
                 #TODO:
                 #We should use the dictionary as input parameter for new_network
-                network_id = myvim.new_network(myNetName, myNetType, myNetIPProfile)
+                network_id, _  = myvim.new_network(myNetName, myNetType, myNetIPProfile)
                 #print "VIM network id for scenario %s: %s" % (scenarioDict['name'],network_id)
                 net['vim_id'] = network_id
                 if sce_vnf['uuid'] not in auxNetDict:
                 #print "VIM network id for scenario %s: %s" % (scenarioDict['name'],network_id)
                 net['vim_id'] = network_id
                 if sce_vnf['uuid'] not in auxNetDict:
@@ -5450,7 +5450,7 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor):
             net_public = net.pop("shared", False)
             net_ipprofile = net.pop("ip_profile", None)
             net_vlan = net.pop("vlan", None)
             net_public = net.pop("shared", False)
             net_ipprofile = net.pop("ip_profile", None)
             net_vlan = net.pop("vlan", None)
-            content = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **net)
+            content, _ = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **net)
 
             #If the datacenter has a SDN controller defined and the network is of dataplane type, then create the sdn network
             if get_sdn_controller_id(mydb, datacenter) != None and (net_type == 'data' or net_type == 'ptp'):
 
             #If the datacenter has a SDN controller defined and the network is of dataplane type, then create the sdn network
             if get_sdn_controller_id(mydb, datacenter) != None and (net_type == 'data' or net_type == 'ptp'):
@@ -5464,7 +5464,7 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor):
                     sdn_network['type'] = net_type
                     sdn_network['name'] = net_name
                     sdn_network['region'] = datacenter_tenant_id
                     sdn_network['type'] = net_type
                     sdn_network['name'] = net_name
                     sdn_network['region'] = datacenter_tenant_id
-                    ovim_content = ovim.new_network(sdn_network)
+                    ovim_content  = ovim.new_network(sdn_network)
                 except ovimException as e:
                     logger.error("ovimException creating SDN network={} ".format(
                         sdn_network) + str(e), exc_info=True)
                 except ovimException as e:
                     logger.error("ovimException creating SDN network={} ".format(
                         sdn_network) + str(e), exc_info=True)
index d57ec47..02b3bcc 100644 (file)
@@ -990,7 +990,7 @@ class vim_thread(threading.Thread):
             # CREATE
             params = task["params"]
             action_text = "creating VIM"
             # CREATE
             params = task["params"]
             action_text = "creating VIM"
-            vim_net_id = self.vim.new_network(*params[0:3])
+            vim_net_id, created_items = self.vim.new_network(*params[0:3])
 
             net_name = params[0]
             net_type = params[1]
 
             net_name = params[0]
             net_type = params[1]
@@ -1039,6 +1039,7 @@ class vim_thread(threading.Thread):
             task["extra"]["vim_info"] = {}
             task["extra"]["sdn_net_id"] = sdn_net_id
             task["extra"]["created"] = True
             task["extra"]["vim_info"] = {}
             task["extra"]["sdn_net_id"] = sdn_net_id
             task["extra"]["created"] = True
+            task["extra"]["created_items"] = created_items
             task["error_msg"] = None
             task["vim_id"] = vim_net_id
             instance_element_update = {"vim_net_id": vim_net_id, "sdn_net_id": sdn_net_id, "status": "BUILD",
             task["error_msg"] = None
             task["vim_id"] = vim_net_id
             instance_element_update = {"vim_net_id": vim_net_id, "sdn_net_id": sdn_net_id, "status": "BUILD",
@@ -1068,7 +1069,7 @@ class vim_thread(threading.Thread):
                         self.ovim.delete_port(port['uuid'], idempotent=True)
                     self.ovim.delete_network(sdn_net_id, idempotent=True)
             if net_vim_id:
                         self.ovim.delete_port(port['uuid'], idempotent=True)
                     self.ovim.delete_network(sdn_net_id, idempotent=True)
             if net_vim_id:
-                self.vim.delete_network(net_vim_id)
+                self.vim.delete_network(net_vim_id, task["extra"].get("created_items"))
             task["status"] = "DONE"
             task["error_msg"] = None
             return True, None
             task["status"] = "DONE"
             task["error_msg"] = None
             return True, None
index 4a7cc09..5c94840 100644 (file)
@@ -331,7 +331,11 @@ class vimconnector():
                 'dhcp_count': number of IPs to grant.
             'shared': if this network can be seen/use by other tenants/organization
             'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
                 'dhcp_count': number of IPs to grant.
             'shared': if this network can be seen/use by other tenants/organization
             'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
-        Returns the network identifier on success or raises and exception on failure
+        Returns a tuple with the network identifier and created_items, or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_network. Can be used to store created segments, created l2gw connections, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
         """
         raise vimconnNotImplemented( "Should have implemented this" )
 
         """
         raise vimconnNotImplemented( "Should have implemented this" )
 
@@ -370,8 +374,11 @@ class vimconnector():
         """
         raise vimconnNotImplemented( "Should have implemented this" )
 
         """
         raise vimconnNotImplemented( "Should have implemented this" )
 
-    def delete_network(self, net_id):
-        """Deletes a tenant network from VIM
+    def delete_network(self, net_id, created_items=None):
+        """
+        Removes a tenant network from VIM and its associated elements
+        :param net_id: VIM identifier of the network, provided by method new_network
+        :param created_items: dictionary with extra items to be deleted. provided by method new_network
         Returns the network identifier or raises an exception upon error or when network is not found
         """
         raise vimconnNotImplemented( "Should have implemented this" )
         Returns the network identifier or raises an exception upon error or when network is not found
         """
         raise vimconnNotImplemented( "Should have implemented this" )
@@ -533,7 +540,7 @@ class vimconnector():
         
     def delete_vminstance(self, vm_id, created_items=None):
         """
         
     def delete_vminstance(self, vm_id, created_items=None):
         """
-        Removes a VM instance from VIM and each associate elements
+        Removes a VM instance from VIM and its associated elements
         :param vm_id: VIM identifier of the VM, provided by method new_vminstance
         :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method
             action_vminstance
         :param vm_id: VIM identifier of the VM, provided by method new_vminstance
         :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method
             action_vminstance
index 22103d0..bcd8cbc 100644 (file)
@@ -299,11 +299,16 @@ class vimconnector(vimconn.vimconnector):
                     'count': number of IPs to grant.
             'shared': if this network can be seen/use by other tenants/organization
             'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
                     'count': number of IPs to grant.
             'shared': if this network can be seen/use by other tenants/organization
             'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
-        Returns the network identifier on success or raises and exception on failure
+        Returns a tuple with the network identifier and created_items, or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_network. Can be used to store created segments, created l2gw connections, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
         """
 
         self.logger.debug("Adding a subnet to VPC")
         try:
         """
 
         self.logger.debug("Adding a subnet to VPC")
         try:
+            created_items = {}
             self._reload_connection()
             subnet = None
             vpc_id = self.vpc_id
             self._reload_connection()
             subnet = None
             vpc_id = self.vpc_id
@@ -314,7 +319,7 @@ class vimconnector(vimconn.vimconnector):
                 subnet_list = self.subnet_sizes(len(self.get_availability_zones_list()), vpc['cidr_block'])
                 cidr_block = list(set(subnet_list) - set(self.get_network_details({'tenant_id': vpc['id']}, detail='cidr_block')))[0]
             subnet = self.conn_vpc.create_subnet(vpc_id, cidr_block)
                 subnet_list = self.subnet_sizes(len(self.get_availability_zones_list()), vpc['cidr_block'])
                 cidr_block = list(set(subnet_list) - set(self.get_network_details({'tenant_id': vpc['id']}, detail='cidr_block')))[0]
             subnet = self.conn_vpc.create_subnet(vpc_id, cidr_block)
-            return subnet.id
+            return subnet.id, created_items
         except Exception as e:
             self.format_vimconn_exception(e)
 
         except Exception as e:
             self.format_vimconn_exception(e)
 
@@ -384,8 +389,11 @@ class vimconnector(vimconn.vimconnector):
         except Exception as e:
             self.format_vimconn_exception(e)
 
         except Exception as e:
             self.format_vimconn_exception(e)
 
-    def delete_network(self, net_id):
-        """Deletes a tenant network from VIM
+    def delete_network(self, net_id, created_items=None):
+        """
+        Removes a tenant network from VIM and its associated elements
+        :param net_id: VIM identifier of the network, provided by method new_network
+        :param created_items: dictionary with extra items to be deleted. provided by method new_network
         Returns the network identifier or raises an exception upon error or when network is not found
         """
 
         Returns the network identifier or raises an exception upon error or when network is not found
         """
 
index 5a0ea87..8f7fad8 100644 (file)
@@ -209,9 +209,33 @@ class vimconnector(vimconn.vimconnector):
     #      os.remove("manage_bridge_OSM")
 
     def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None):  # , **vim_specific):
     #      os.remove("manage_bridge_OSM")
 
     def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None):  # , **vim_specific):
-        """Returns the network identifier"""
+        """Adds a tenant network to VIM
+        Params:
+            'net_name': name of the network
+            'net_type': one of:
+                'bridge': overlay isolated network
+                'data':   underlay E-LAN network for Passthrough and SRIOV interfaces
+                'ptp':    underlay E-LINE network for Passthrough and SRIOV interfaces.
+            'ip_profile': is a dict containing the IP parameters of the network
+                'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
+                'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
+                'gateway_address': (Optional) ip_schema, that is X.X.X.X
+                'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
+                'dhcp_enabled': True or False
+                'dhcp_start_address': ip_schema, first IP to grant
+                'dhcp_count': number of IPs to grant.
+            'shared': if this network can be seen/use by other tenants/organization
+            'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
+        Returns a tuple with the network identifier and created_items, or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_network. Can be used to store created segments, created l2gw connections, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
+        """
+
         # oca library method cannot be used in this case (problem with cluster parameters)
         try:
         # oca library method cannot be used in this case (problem with cluster parameters)
         try:
+            created_items = {}
             # vlan = str(random.randint(self.config["vlan"]["start-range"], self.config["vlan"]["finish-range"]))
             # self.create_bridge_host(vlan)
             bridge_config = self.config["bridge_service"]
             # vlan = str(random.randint(self.config["vlan"]["start-range"], self.config["vlan"]["finish-range"]))
             # self.create_bridge_host(vlan)
             bridge_config = self.config["bridge_service"]
@@ -262,7 +286,7 @@ class vimconnector(vimconn.vimconnector):
             </methodCall>'.format(self.user, self.passwd, config, self.config["cluster"]["id"])
             r = requests.post(self.url, params)
             obj = untangle.parse(str(r.content))
             </methodCall>'.format(self.user, self.passwd, config, self.config["cluster"]["id"])
             r = requests.post(self.url, params)
             obj = untangle.parse(str(r.content))
-            return obj.methodResponse.params.param.value.array.data.value[1].i4.cdata.encode('utf-8')
+            return obj.methodResponse.params.param.value.array.data.value[1].i4.cdata.encode('utf-8'), created_items
         except Exception as e:
             self.logger.error("Create new network error: " + str(e))
             raise vimconn.vimconnException(e)
         except Exception as e:
             self.logger.error("Create new network error: " + str(e))
             raise vimconn.vimconnException(e)
@@ -328,9 +352,12 @@ class vimconnector(vimconn.vimconnector):
                 self.logger.error("Get network " + str(net_id) + " error): " + str(e))
                 raise vimconn.vimconnException(e)
 
                 self.logger.error("Get network " + str(net_id) + " error): " + str(e))
                 raise vimconn.vimconnException(e)
 
-    def delete_network(self, net_id):
-        """Deletes a tenant network from VIM
-            Returns the network identifier
+    def delete_network(self, net_id, created_items=None):
+        """
+        Removes a tenant network from VIM and its associated elements
+        :param net_id: VIM identifier of the network, provided by method new_network
+        :param created_items: dictionary with extra items to be deleted. provided by method new_network
+        Returns the network identifier or raises an exception upon error or when network is not found
         """
         try:
             # self.delete_bridge_host()
         """
         try:
             # self.delete_bridge_host()
index b698eda..cfeeae0 100644 (file)
@@ -109,7 +109,11 @@ class vimconnector(vimconn.vimconnector):
 
         if config.get('dataplane_net_vlan_range') is not None:
             #validate vlan ranges provided by user
 
         if config.get('dataplane_net_vlan_range') is not None:
             #validate vlan ranges provided by user
-            self._validate_vlan_ranges(config.get('dataplane_net_vlan_range'))
+            self._validate_vlan_ranges(config.get('dataplane_net_vlan_range'), 'dataplane_net_vlan_range')
+
+        if config.get('multisegment_vlan_range') is not None:
+            #validate vlan ranges provided by user
+            self._validate_vlan_ranges(config.get('multisegment_vlan_range'), 'multisegment_vlan_range')
 
         vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
                                       config)
 
         vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
                                       config)
@@ -488,20 +492,63 @@ class vimconnector(vimconn.vimconnector):
             self._format_exception(e)
 
     def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None):
             self._format_exception(e)
 
     def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None):
-        '''Adds a tenant network to VIM. Returns the network identifier'''
+        """Adds a tenant network to VIM
+        Params:
+            'net_name': name of the network
+            'net_type': one of:
+                'bridge': overlay isolated network
+                'data':   underlay E-LAN network for Passthrough and SRIOV interfaces
+                'ptp':    underlay E-LINE network for Passthrough and SRIOV interfaces.
+            'ip_profile': is a dict containing the IP parameters of the network
+                'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
+                'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
+                'gateway_address': (Optional) ip_schema, that is X.X.X.X
+                'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
+                'dhcp_enabled': True or False
+                'dhcp_start_address': ip_schema, first IP to grant
+                'dhcp_count': number of IPs to grant.
+            'shared': if this network can be seen/use by other tenants/organization
+            'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
+        Returns a tuple with the network identifier and created_items, or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_network. Can be used to store created segments, created l2gw connections, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
+        """
         self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type)
         self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type)
-        #self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
+        # self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
         try:
             new_net = None
         try:
             new_net = None
+            created_items = {}
             self._reload_connection()
             network_dict = {'name': net_name, 'admin_state_up': True}
             if net_type=="data" or net_type=="ptp":
                 if self.config.get('dataplane_physical_net') == None:
                     raise vimconn.vimconnConflictException("You must provide a 'dataplane_physical_net' at config value before creating sriov network")
             self._reload_connection()
             network_dict = {'name': net_name, 'admin_state_up': True}
             if net_type=="data" or net_type=="ptp":
                 if self.config.get('dataplane_physical_net') == None:
                     raise vimconn.vimconnConflictException("You must provide a 'dataplane_physical_net' at config value before creating sriov network")
-                network_dict["provider:physical_network"] = self.config['dataplane_physical_net'] #"physnet_sriov" #TODO physical
-                network_dict["provider:network_type"]     = "vlan"
-                if vlan!=None:
-                    network_dict["provider:network_type"] = vlan
+                if not self.config.get('multisegment_support'):
+                    network_dict["provider:physical_network"] = self.config[
+                        'dataplane_physical_net']  # "physnet_sriov" #TODO physical
+                    network_dict["provider:network_type"] = "vlan"
+                    if vlan!=None:
+                        network_dict["provider:network_type"] = vlan
+                else:
+                    ###### Multi-segment case ######
+                    segment_list = []
+                    segment1_dict = {}
+                    segment1_dict["provider:physical_network"] = ''
+                    segment1_dict["provider:network_type"]     = 'vxlan'
+                    segment_list.append(segment1_dict)
+                    segment2_dict = {}
+                    segment2_dict["provider:physical_network"] = self.config['dataplane_physical_net']
+                    segment2_dict["provider:network_type"]     = "vlan"
+                    if self.config.get('multisegment_vlan_range'):
+                        vlanID = self._generate_multisegment_vlanID()
+                        segment2_dict["provider:segmentation_id"] = vlanID
+                    # else
+                    #     raise vimconn.vimconnConflictException(
+                    #         "You must provide 'multisegment_vlan_range' at config dict before creating a multisegment network")
+                    segment_list.append(segment2_dict)
+                    network_dict["segments"] = segment_list
 
                 ####### VIO Specific Changes #########
                 if self.vim_type == "VIO":
 
                 ####### VIO Specific Changes #########
                 if self.vim_type == "VIO":
@@ -513,12 +560,12 @@ class vimconnector(vimconn.vimconnector):
                                 "'dataplane_net_vlan_range' in format [start_ID - end_ID]"\
                                 "at config value before creating sriov network with vlan tag")
 
                                 "'dataplane_net_vlan_range' in format [start_ID - end_ID]"\
                                 "at config value before creating sriov network with vlan tag")
 
-                        network_dict["provider:segmentation_id"] = self._genrate_vlanID()
+                        network_dict["provider:segmentation_id"] = self._generate_vlanID()
 
 
-            network_dict["shared"]=shared
-            new_net=self.neutron.create_network({'network':network_dict})
-            #print new_net
-            #create subnetwork, even if there is no profile
+            network_dict["shared"] = shared
+            new_net = self.neutron.create_network({'network':network_dict})
+            # print new_net
+            # create subnetwork, even if there is no profile
             if not ip_profile:
                 ip_profile = {}
             if not ip_profile.get('subnet_address'):
             if not ip_profile:
                 ip_profile = {}
             if not ip_profile.get('subnet_address'):
@@ -527,7 +574,7 @@ class vimconnector(vimconn.vimconnector):
                 ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand)
             if 'ip_version' not in ip_profile:
                 ip_profile['ip_version'] = "IPv4"
                 ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand)
             if 'ip_version' not in ip_profile:
                 ip_profile['ip_version'] = "IPv4"
-            subnet = {"name":net_name+"-subnet",
+            subnet = {"name": net_name+"-subnet",
                     "network_id": new_net["network"]["id"],
                     "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
                     "cidr": ip_profile['subnet_address']
                     "network_id": new_net["network"]["id"],
                     "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
                     "cidr": ip_profile['subnet_address']
@@ -555,8 +602,29 @@ class vimconnector(vimconn.vimconnector):
                 subnet['allocation_pools'][0]['end'] = ip_str
             #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
             self.neutron.create_subnet({"subnet": subnet} )
                 subnet['allocation_pools'][0]['end'] = ip_str
             #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
             self.neutron.create_subnet({"subnet": subnet} )
-            return new_net["network"]["id"]
+
+            if net_type == "data" and self.config.get('multisegment_support'):
+                if self.config.get('l2gw_support'):
+                    l2gw_list = self.neutron.list_l2_gateways().get("l2_gateways", ())
+                    for l2gw in l2gw_list:
+                        l2gw_conn = {}
+                        l2gw_conn["l2_gateway_id"] = l2gw["id"]
+                        l2gw_conn["network_id"] = new_net["network"]["id"]
+                        l2gw_conn["segmentation_id"] = str(vlanID)
+                        new_l2gw_conn = self.neutron.create_l2_gateway_connection({"l2_gateway_connection": l2gw_conn})
+                        created_items["l2gwconn:" + str(new_l2gw_conn["l2_gateway_connection"]["id"])] = True
+            return new_net["network"]["id"], created_items
         except Exception as e:
         except Exception as e:
+            #delete l2gw connections (if any) before deleting the network
+            for k, v in created_items.items():
+                if not v:  # skip already deleted
+                    continue
+                try:
+                    k_item, _, k_id = k.partition(":")
+                    if k_item == "l2gwconn":
+                        self.neutron.delete_l2_gateway_connection(k_id)
+                except Exception as e2:
+                    self.logger.error("Error deleting l2 gateway connection: {}: {}".format(type(e2).__name__, e2))
             if new_net:
                 self.neutron.delete_network(new_net['network']['id'])
             self._format_exception(e)
             if new_net:
                 self.neutron.delete_network(new_net['network']['id'])
             self._format_exception(e)
@@ -611,11 +679,28 @@ class vimconnector(vimconn.vimconnector):
         net["encapsulation_id"] = net.get('provider:segmentation_id')
         return net
 
         net["encapsulation_id"] = net.get('provider:segmentation_id')
         return net
 
-    def delete_network(self, net_id):
-        '''Deletes a tenant network from VIM. Returns the old network identifier'''
+    def delete_network(self, net_id, created_items=None):
+        """
+        Removes a tenant network from VIM and its associated elements
+        :param net_id: VIM identifier of the network, provided by method new_network
+        :param created_items: dictionary with extra items to be deleted. provided by method new_network
+        Returns the network identifier or raises an exception upon error or when network is not found
+        """
         self.logger.debug("Deleting network '%s' from VIM", net_id)
         self.logger.debug("Deleting network '%s' from VIM", net_id)
+        if created_items == None:
+            created_items = {}
         try:
             self._reload_connection()
         try:
             self._reload_connection()
+            #delete l2gw connections (if any) before deleting the network
+            for k, v in created_items.items():
+                if not v:  # skip already deleted
+                    continue
+                try:
+                    k_item, _, k_id = k.partition(":")
+                    if k_item == "l2gwconn":
+                        self.neutron.delete_l2_gateway_connection(k_id)
+                except Exception as e:
+                    self.logger.error("Error deleting l2 gateway connection: {}: {}".format(type(e).__name__, e))
             #delete VM ports attached to this networks before the network
             ports = self.neutron.list_ports(network_id=net_id)
             for p in ports['ports']:
             #delete VM ports attached to this networks before the network
             ports = self.neutron.list_ports(network_id=net_id)
             for p in ports['ports']:
@@ -1625,7 +1710,7 @@ class vimconnector(vimconn.vimconnector):
         #TODO insert exception vimconn.HTTP_Unauthorized
 
     ####### VIO Specific Changes #########
         #TODO insert exception vimconn.HTTP_Unauthorized
 
     ####### VIO Specific Changes #########
-    def _genrate_vlanID(self):
+    def _generate_vlanID(self):
         """
          Method to get unused vlanID
             Args:
         """
          Method to get unused vlanID
             Args:
@@ -1655,35 +1740,69 @@ class vimconnector(vimconn.vimconnector):
                 " All given Vlan IDs {} are in use.".format(self.config.get('dataplane_net_vlan_range')))
 
 
                 " All given Vlan IDs {} are in use.".format(self.config.get('dataplane_net_vlan_range')))
 
 
-    def _validate_vlan_ranges(self, dataplane_net_vlan_range):
+    def _generate_multisegment_vlanID(self):
+        """
+         Method to get unused vlanID
+            Args:
+                None
+            Returns:
+                vlanID
+        """
+        #Get used VLAN IDs
+        usedVlanIDs = []
+        networks = self.get_network_list()
+        for net in networks:
+            if net.get('provider:network_type') == "vlan" and net.get('provider:segmentation_id'):
+                usedVlanIDs.append(net.get('provider:segmentation_id'))
+            elif net.get('segments'):
+                for segment in net.get('segments'):
+                    if segment.get('provider:network_type') == "vlan" and segment.get('provider:segmentation_id'):
+                        usedVlanIDs.append(segment.get('provider:segmentation_id'))
+        used_vlanIDs = set(usedVlanIDs)
+
+        #find unused VLAN ID
+        for vlanID_range in self.config.get('multisegment_vlan_range'):
+            try:
+                start_vlanid , end_vlanid = map(int, vlanID_range.replace(" ", "").split("-"))
+                for vlanID in xrange(start_vlanid, end_vlanid + 1):
+                    if vlanID not in used_vlanIDs:
+                        return vlanID
+            except Exception as exp:
+                raise vimconn.vimconnException("Exception {} occurred while generating VLAN ID.".format(exp))
+        else:
+            raise vimconn.vimconnConflictException("Unable to create the VLAN segment."\
+                " All VLAN IDs {} are in use.".format(self.config.get('multisegment_vlan_range')))
+
+
+    def _validate_vlan_ranges(self, input_vlan_range, text_vlan_range):
         """
         Method to validate user given vlanID ranges
             Args:  None
             Returns: None
         """
         """
         Method to validate user given vlanID ranges
             Args:  None
             Returns: None
         """
-        for vlanID_range in dataplane_net_vlan_range:
+        for vlanID_range in input_vlan_range:
             vlan_range = vlanID_range.replace(" ", "")
             #validate format
             vlanID_pattern = r'(\d)*-(\d)*$'
             match_obj = re.match(vlanID_pattern, vlan_range)
             if not match_obj:
             vlan_range = vlanID_range.replace(" ", "")
             #validate format
             vlanID_pattern = r'(\d)*-(\d)*$'
             match_obj = re.match(vlanID_pattern, vlan_range)
             if not match_obj:
-                raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}.You must provide "\
-                "'dataplane_net_vlan_range' in format [start_ID - end_ID].".format(vlanID_range))
+                raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}.You must provide "\
+                "'{}' in format [start_ID - end_ID].".format(text_vlan_range, vlanID_range, text_vlan_range))
 
             start_vlanid , end_vlanid = map(int,vlan_range.split("-"))
             if start_vlanid <= 0 :
 
             start_vlanid , end_vlanid = map(int,vlan_range.split("-"))
             if start_vlanid <= 0 :
-                raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}."\
+                raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\
                 "Start ID can not be zero. For VLAN "\
                 "Start ID can not be zero. For VLAN "\
-                "networks valid IDs are 1 to 4094 ".format(vlanID_range))
+                "networks valid IDs are 1 to 4094 ".format(text_vlan_range, vlanID_range))
             if end_vlanid > 4094 :
             if end_vlanid > 4094 :
-                raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}."\
+                raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\
                 "End VLAN ID can not be greater than 4094. For VLAN "\
                 "End VLAN ID can not be greater than 4094. For VLAN "\
-                "networks valid IDs are 1 to 4094 ".format(vlanID_range))
+                "networks valid IDs are 1 to 4094 ".format(text_vlan_range, vlanID_range))
 
             if start_vlanid > end_vlanid:
 
             if start_vlanid > end_vlanid:
-                raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}."\
-                    "You must provide a 'dataplane_net_vlan_range' in format start_ID - end_ID and "\
-                    "start_ID < end_ID ".format(vlanID_range))
+                raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\
+                    "You must provide '{}' in format start_ID - end_ID and "\
+                    "start_ID < end_ID ".format(text_vlan_range, vlanID_range, text_vlan_range))
 
 #NOT USED FUNCTIONS
 
 
 #NOT USED FUNCTIONS
 
index f6d2237..6f584c5 100644 (file)
@@ -485,9 +485,31 @@ class vimconnector(vimconn.vimconnector):
             self._format_request_exception(e)
 
     def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None): #, **vim_specific):
             self._format_request_exception(e)
 
     def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None): #, **vim_specific):
-        '''Adds a tenant network to VIM'''
-        '''Returns the network identifier'''
+        """Adds a tenant network to VIM
+        Params:
+            'net_name': name of the network
+            'net_type': one of:
+                'bridge': overlay isolated network
+                'data':   underlay E-LAN network for Passthrough and SRIOV interfaces
+                'ptp':    underlay E-LINE network for Passthrough and SRIOV interfaces.
+            'ip_profile': is a dict containing the IP parameters of the network
+                'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
+                'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
+                'gateway_address': (Optional) ip_schema, that is X.X.X.X
+                'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
+                'dhcp_enabled': True or False
+                'dhcp_start_address': ip_schema, first IP to grant
+                'dhcp_count': number of IPs to grant.
+            'shared': if this network can be seen/use by other tenants/organization
+            'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
+        Returns a tuple with the network identifier and created_items, or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_network. Can be used to store created segments, created l2gw connections, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
+        """
         try:
         try:
+            created_items = {}
             self._get_my_tenant()
             if net_type=="bridge":
                 net_type="bridge_data"
             self._get_my_tenant()
             if net_type=="bridge":
                 net_type="bridge_data"
@@ -507,7 +529,7 @@ class vimconnector(vimconn.vimconnector):
             #if r is not None: 
             #    self.logger.warn("Warning: remove extra items %s", str(r))
             network_id = response['network']['id']
             #if r is not None: 
             #    self.logger.warn("Warning: remove extra items %s", str(r))
             network_id = response['network']['id']
-            return network_id
+            return network_id, created_items
         except (requests.exceptions.RequestException, js_e.ValidationError) as e:
             self._format_request_exception(e)
         
         except (requests.exceptions.RequestException, js_e.ValidationError) as e:
             self._format_request_exception(e)
         
@@ -558,9 +580,13 @@ class vimconnector(vimconn.vimconnector):
         except (requests.exceptions.RequestException, js_e.ValidationError) as e:
             self._format_request_exception(e)
             
         except (requests.exceptions.RequestException, js_e.ValidationError) as e:
             self._format_request_exception(e)
             
-    def delete_network(self, net_id):
-        '''Deletes a tenant network from VIM'''
-        '''Returns the network identifier'''
+    def delete_network(self, net_id, created_items=None):
+        """
+        Removes a tenant network from VIM and its associated elements
+        :param net_id: VIM identifier of the network, provided by method new_network
+        :param created_items: dictionary with extra items to be deleted. provided by method new_network
+        Returns the network identifier or raises an exception upon error or when network is not found
+        """
         try:
             self._get_my_tenant()
             url = self.url+'/networks/'+net_id
         try:
             self._get_my_tenant()
             url = self.url+'/networks/'+net_id
index 4a5ab97..2910e92 100644 (file)
@@ -500,17 +500,35 @@ class vimconnector(vimconn.vimconnector):
 
         return vdclist
 
 
         return vdclist
 
-    def new_network(self, net_name, net_type, ip_profile=None, shared=False):
+    def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None):
         """Adds a tenant network to VIM
         """Adds a tenant network to VIM
-            net_name is the name
-            net_type can be 'bridge','data'.'ptp'.
-            ip_profile is a dict containing the IP parameters of the network
-            shared is a boolean
-        Returns the network identifier"""
+        Params:
+            'net_name': name of the network
+            'net_type': one of:
+                'bridge': overlay isolated network
+                'data':   underlay E-LAN network for Passthrough and SRIOV interfaces
+                'ptp':    underlay E-LINE network for Passthrough and SRIOV interfaces.
+            'ip_profile': is a dict containing the IP parameters of the network
+                'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
+                'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
+                'gateway_address': (Optional) ip_schema, that is X.X.X.X
+                'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
+                'dhcp_enabled': True or False
+                'dhcp_start_address': ip_schema, first IP to grant
+                'dhcp_count': number of IPs to grant.
+            'shared': if this network can be seen/use by other tenants/organization
+            'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
+        Returns a tuple with the network identifier and created_items, or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_network. Can be used to store created segments, created l2gw connections, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
+        """
 
         self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
                           .format(net_name, net_type, ip_profile, shared))
 
 
         self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
                           .format(net_name, net_type, ip_profile, shared))
 
+        created_items = {}
         isshared = 'false'
         if shared:
             isshared = 'true'
         isshared = 'false'
         if shared:
             isshared = 'true'
@@ -524,7 +542,7 @@ class vimconnector(vimconn.vimconnector):
         network_uuid = self.create_network(network_name=net_name, net_type=net_type,
                                            ip_profile=ip_profile, isshared=isshared)
         if network_uuid is not None:
         network_uuid = self.create_network(network_name=net_name, net_type=net_type,
                                            ip_profile=ip_profile, isshared=isshared)
         if network_uuid is not None:
-            return network_uuid
+            return network_uuid, created_items
         else:
             raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
 
         else:
             raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
 
@@ -771,11 +789,12 @@ class vimconnector(vimconn.vimconnector):
 
         return filter_dict
 
 
         return filter_dict
 
-    def delete_network(self, net_id):
+    def delete_network(self, net_id, created_items=None):
         """
         """
-            Method Deletes a tenant network from VIM, provide the network id.
-
-            Returns the network identifier or raise an exception
+        Removes a tenant network from VIM and its associated elements
+        :param net_id: VIM identifier of the network, provided by method new_network
+        :param created_items: dictionary with extra items to be deleted. provided by method new_network
+        Returns the network identifier or raises an exception upon error or when network is not found
         """
 
         # ############# Stub code for SRIOV #################
         """
 
         # ############# Stub code for SRIOV #################
index 6e1068a..05d96bb 100644 (file)
@@ -11,6 +11,7 @@ python-novaclient
 python-keystoneclient
 python-glanceclient
 python-neutronclient
 python-keystoneclient
 python-glanceclient
 python-neutronclient
+networking-l2gw
 python-cinderclient
 pyvcloud==19.1.1
 pyvmomi
 python-cinderclient
 pyvcloud==19.1.1
 pyvmomi
index 3789992..e4554ae 100755 (executable)
@@ -270,10 +270,11 @@ then
 
     # install openstack client needed for using openstack as a VIM
     [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-novaclient python-keystoneclient python-glanceclient "\
 
     # install openstack client needed for using openstack as a VIM
     [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-novaclient python-keystoneclient python-glanceclient "\
-                                                   "python-neutronclient python-cinderclient python-openstackclient"
+                                                   "python-neutronclient python-cinderclient python-openstackclient "\
+                                                   "python-networking-l2gw"
     [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "python-devel" && easy_install \
         python-novaclient python-keystoneclient python-glanceclient python-neutronclient python-cinderclient \
     [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "python-devel" && easy_install \
         python-novaclient python-keystoneclient python-glanceclient python-neutronclient python-cinderclient \
-        python-openstackclient #TODO revise if gcc python-pip is needed
+        python-openstackclient python-networking-l2gw #TODO revise if gcc python-pip is needed
 fi  # [[ -z "$NO_PACKAGES" ]]
 
 if [[ -z $NOCLONE ]]; then
 fi  # [[ -z "$NO_PACKAGES" ]]
 
 if [[ -z $NOCLONE ]]; then
index d5cbe79..5d78ec3 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -31,6 +31,7 @@ _requirements = [
     "python-glanceclient",
     "python-neutronclient",
     "python-cinderclient",
     "python-glanceclient",
     "python-neutronclient",
     "python-cinderclient",
+    "networking-l2gw",
     #"pyvcloud",
     #"progressbar",
     "prettytable",
     #"pyvcloud",
     #"progressbar",
     "prettytable",
index 53abd48..f0424fb 100644 (file)
--- a/stdeb.cfg
+++ b/stdeb.cfg
@@ -2,5 +2,5 @@
 Suite: xenial
 XS-Python-Version: >= 2.7
 Maintainer: Gerardo Garcia <gerardo.garciadeblas@telefonica.com>
 Suite: xenial
 XS-Python-Version: >= 2.7
 Maintainer: Gerardo Garcia <gerardo.garciadeblas@telefonica.com>
-Depends: python-pip, libmysqlclient-dev, libssl-dev, libffi-dev, python-argcomplete, python-boto, python-bottle, python-jsonschema, python-logutils, python-cinderclient, python-glanceclient, python-keystoneclient, python-neutronclient, python-novaclient, python-openstackclient, python-mysqldb, python-lib-osm-openvim, python-osm-im, python-networkx
+Depends: python-pip, libmysqlclient-dev, libssl-dev, libffi-dev, python-argcomplete, python-boto, python-bottle, python-jsonschema, python-logutils, python-cinderclient, python-glanceclient, python-keystoneclient, python-neutronclient, python-networking-l2gw, python-novaclient, python-openstackclient, python-mysqldb, python-lib-osm-openvim, python-osm-im, python-networkx