Feature 10906: Support for Anti-Affinity groups
[osm/RO.git] / RO-VIM-openstack / osm_rovim_openstack / vimconn_openstack.py
index d3837ab..a15a53c 100644 (file)
@@ -30,32 +30,29 @@ to the VIM connector's SFC resources as follows:
 - Service Function Path (OSM) -> Port Chain (Neutron)
 """
 
-from osm_ro_plugin import vimconn
-
-# import json
+import copy
+from http.client import HTTPException
 import logging
-import netaddr
-import time
-import yaml
+from pprint import pformat
 import random
 import re
-import copy
-from pprint import pformat
-from novaclient import client as nClient, exceptions as nvExceptions
-from keystoneauth1.identity import v2, v3
+import time
+
+from cinderclient import client as cClient
+from glanceclient import client as glClient
+import glanceclient.exc as gl1Exceptions
 from keystoneauth1 import session
+from keystoneauth1.identity import v2, v3
 import keystoneclient.exceptions as ksExceptions
-import keystoneclient.v3.client as ksClient_v3
 import keystoneclient.v2_0.client as ksClient_v2
-from glanceclient import client as glClient
-import glanceclient.exc as gl1Exceptions
-from cinderclient import client as cClient
-
-# TODO py3 check that this base exception matches python2 httplib.HTTPException
-from http.client import HTTPException
-from neutronclient.neutron import client as neClient
+import keystoneclient.v3.client as ksClient_v3
+import netaddr
 from neutronclient.common import exceptions as neExceptions
+from neutronclient.neutron import client as neClient
+from novaclient import client as nClient, exceptions as nvExceptions
+from osm_ro_plugin import vimconn
 from requests.exceptions import ConnectionError
+import yaml
 
 __author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes, xFlow Research, Igor D.C., Eduardo Sousa"
 __date__ = "$22-sep-2017 23:59:59$"
@@ -1177,6 +1174,8 @@ class vimconnector(vimconn.VimConnector):
                 flavor_dict["ram"],
                 flavor_dict["vcpus"],
                 flavor_dict["disk"],
+                flavor_dict.get("ephemeral", 0),
+                flavor_dict.get("swap", 0),
             )
             # numa=None
             extended = flavor_dict.get("extended", {})
@@ -1196,7 +1195,13 @@ class vimconnector(vimconn.VimConnector):
                     continue
                     # TODO
 
-                flavor_data = (flavor.ram, flavor.vcpus, flavor.disk)
+                flavor_data = (
+                    flavor.ram,
+                    flavor.vcpus,
+                    flavor.disk,
+                    flavor.ephemeral,
+                    flavor.swap if isinstance(flavor.swap, int) else 0,
+                )
                 if flavor_data == flavor_target:
                     return flavor.id
                 elif (
@@ -1346,10 +1351,12 @@ class vimconnector(vimconn.VimConnector):
 
                     # create flavor
                     new_flavor = self.nova.flavors.create(
-                        name,
-                        ram,
-                        vcpus,
-                        flavor_data.get("disk", 0),
+                        name=name,
+                        ram=ram,
+                        vcpus=vcpus,
+                        disk=flavor_data.get("disk", 0),
+                        ephemeral=flavor_data.get("ephemeral", 0),
+                        swap=flavor_data.get("swap", 0),
                         is_public=flavor_data.get("is_public", True),
                     )
                     # add metadata
@@ -1674,6 +1681,7 @@ class vimconnector(vimconn.VimConnector):
         start,
         image_id,
         flavor_id,
+        affinity_group_list,
         net_list,
         cloud_config=None,
         disk_list=None,
@@ -1683,7 +1691,9 @@ class vimconnector(vimconn.VimConnector):
         """Adds a VM instance to VIM
         Params:
             start: indicates if VM must start or boot in pause mode. Ignored
-            image_id,flavor_id: iamge and flavor uuid
+            image_id,flavor_id: image and flavor uuid
+            affinity_group_list: list of affinity groups, each one is a dictionary.
+                Ignore if empty.
             net_list: list of interfaces, each one is a dictionary with:
                 name:
                 net_id: network uuid to connect
@@ -1908,10 +1918,19 @@ class vimconnector(vimconn.VimConnector):
                 availability_zone_index, availability_zone_list
             )
 
+            # Manage affinity groups/server groups
+            server_group_id = None
+            scheduller_hints = {}
+
+            if affinity_group_list:
+                # Only first id on the list will be used. Openstack restriction
+                server_group_id = affinity_group_list[0]["affinity_group_id"]
+                scheduller_hints["group"] = server_group_id
+
             self.logger.debug(
                 "nova.servers.create({}, {}, {}, nics={}, security_groups={}, "
                 "availability_zone={}, key_name={}, userdata={}, config_drive={}, "
-                "block_device_mapping={})".format(
+                "block_device_mapping={}, server_group={})".format(
                     name,
                     image_id,
                     flavor_id,
@@ -1922,6 +1941,7 @@ class vimconnector(vimconn.VimConnector):
                     userdata,
                     config_drive,
                     block_device_mapping,
+                    server_group_id,
                 )
             )
             server = self.nova.servers.create(
@@ -1936,6 +1956,7 @@ class vimconnector(vimconn.VimConnector):
                 userdata=userdata,
                 config_drive=config_drive,
                 block_device_mapping=block_device_mapping,
+                scheduler_hints=scheduller_hints,
             )  # , description=description)
 
             vm_start_time = time.time()
@@ -3437,3 +3458,60 @@ class vimconnector(vimconn.VimConnector):
             classification_dict[classification_id] = classification
 
         return classification_dict
+
+    def new_affinity_group(self, affinity_group_data):
+        """Adds a server group to VIM
+            affinity_group_data contains a dictionary with information, keys:
+                name: name in VIM for the server group
+                type: affinity or anti-affinity
+                scope: Only nfvi-node allowed
+        Returns the server group identifier"""
+        self.logger.debug("Adding Server Group '%s'", str(affinity_group_data))
+
+        try:
+            name = affinity_group_data["name"]
+            policy = affinity_group_data["type"]
+
+            self._reload_connection()
+            new_server_group = self.nova.server_groups.create(name, policy)
+
+            return new_server_group.id
+        except (
+            ksExceptions.ClientException,
+            nvExceptions.ClientException,
+            ConnectionError,
+            KeyError,
+        ) as e:
+            self._format_exception(e)
+
+    def get_affinity_group(self, affinity_group_id):
+        """Obtain server group details from the VIM. Returns the server group detais as a dict"""
+        self.logger.debug("Getting flavor '%s'", affinity_group_id)
+        try:
+            self._reload_connection()
+            server_group = self.nova.server_groups.find(id=affinity_group_id)
+
+            return server_group.to_dict()
+        except (
+            nvExceptions.NotFound,
+            nvExceptions.ClientException,
+            ksExceptions.ClientException,
+            ConnectionError,
+        ) as e:
+            self._format_exception(e)
+
+    def delete_affinity_group(self, affinity_group_id):
+        """Deletes a server group from the VIM. Returns the old affinity_group_id"""
+        self.logger.debug("Getting server group '%s'", affinity_group_id)
+        try:
+            self._reload_connection()
+            self.nova.server_groups.delete(affinity_group_id)
+
+            return affinity_group_id
+        except (
+            nvExceptions.NotFound,
+            ksExceptions.ClientException,
+            nvExceptions.ClientException,
+            ConnectionError,
+        ) as e:
+            self._format_exception(e)