LWB increase deployment timeout. Check vnf management ip is returned
[osm/RO.git] / lcm / osm_lcm / ROclient.py
index ecaa950..e0533c5 100644 (file)
@@ -56,18 +56,15 @@ def remove_envelop(item, indata=None):
     vnfd or nsd content
     :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
     :param indata: Content to be inspected
-    :return: the useful part of indata (a reference, not a new dictionay) plus boolean indicating if it was enveloped
+    :return: the useful part of indata (a reference, not a new dictionay)
     """
     clean_indata = indata
-    enveloped = False
     if not indata:
-        return {}, False
+        return {}
     if item == "vnfd":
         if clean_indata.get('vnfd:vnfd-catalog'):
-            enveloped = True
             clean_indata = clean_indata['vnfd:vnfd-catalog']
         elif clean_indata.get('vnfd-catalog'):
-            enveloped = True
             clean_indata = clean_indata['vnfd-catalog']
         if clean_indata.get('vnfd'):
             if not isinstance(clean_indata['vnfd'], list) or len(clean_indata['vnfd']) != 1:
@@ -75,43 +72,44 @@ def remove_envelop(item, indata=None):
             clean_indata = clean_indata['vnfd'][0]
     elif item == "nsd":
         if clean_indata.get('nsd:nsd-catalog'):
-            enveloped = True
             clean_indata = clean_indata['nsd:nsd-catalog']
         elif clean_indata.get('nsd-catalog'):
-            enveloped = True
             clean_indata = clean_indata['nsd-catalog']
         if clean_indata.get('nsd'):
             if not isinstance(clean_indata['nsd'], list) or len(clean_indata['nsd']) != 1:
                 raise ROClientException("'nsd' must be a list only one element")
             clean_indata = clean_indata['nsd'][0]
+    elif item == "sdn":
+        if len(indata) == 1 and "sdn_controller" in indata:
+            clean_indata = indata["sdn_controller"]
     elif item == "tenant":
         if len(indata) == 1 and "tenant" in indata:
-            enveloped = True
             clean_indata = indata["tenant"]
-    elif item == "vim" or item == "datacenter":
+    elif item in ("vim", "vim_account", "datacenters"):
         if len(indata) == 1 and "datacenter" in indata:
-            enveloped = True
             clean_indata = indata["datacenter"]
     elif item == "ns" or item == "instances":
         if len(indata) == 1 and "instance" in indata:
-            enveloped = True
             clean_indata = indata["instance"]
     else:
         assert False, "remove_envelop with unknown item {}".format(item)
 
-    return clean_indata, enveloped
+    return clean_indata
 
 
 class ROClient:
     headers_req = {'Accept': 'application/yaml', 'content-type': 'application/yaml'}
-    client_to_RO = {'tenant': 'tenants', 'vim': 'datacenters', 'vnfd': 'vnfs', 'nsd': 'scenarios',
+    client_to_RO = {'tenant': 'tenants', 'vim': 'datacenters', 'vim_account': 'datacenters', 'sdn': 'sdn_controllers',
+                    'vnfd': 'vnfs', 'nsd': 'scenarios',
                     'ns': 'instances'}
     mandatory_for_create = {
         'tenant': ("name", ),
-        'vim': ("name", "vim_url"),
         'vnfd': ("name", "id", "connection-point", "vdu"),
         'nsd': ("name", "id", "constituent-vnfd"),
         'ns': ("name", "scenario", "datacenter"),
+        'vim': ("name", "vim_url"),
+        'vim_account': (),
+        'sdn': ("name", "port", 'ip', 'dpid', 'type'),
     }
     timeout_large = 120
     timeout_short = 30
@@ -150,7 +148,7 @@ class ROClient:
     def __setitem__(self,index, value):
         if index == 'tenant':
             self.tenant_id_name = value
-        elif index == 'datacenter':
+        elif index == 'datacenter' or index == 'vim':
             self.datacenter_id_name = value
         elif index == 'username':
             self.username = value
@@ -227,13 +225,46 @@ class ROClient:
             return {'nsd-catalog': {'nsd': [indata]}}
         elif item == "tenant":
             return {'tenant': indata}
-        elif item == "vim" or item == "datacenter":
+        elif item in ("vim", "vim_account", "datacenter"):
             return {'datacenter': indata}
         elif item == "ns" or item == "instances":
             return {'instance': indata}
         else:
             assert False, "_create_envelop with unknown item {}".format(item)
 
+    @staticmethod
+    def update_descriptor(desc, kwargs):
+        desc = deepcopy(desc)  # do not modify original descriptor
+        try:
+            for k, v in kwargs.items():
+                update_content = desc
+                kitem_old = None
+                klist = k.split(".")
+                for kitem in klist:
+                    if kitem_old is not None:
+                        update_content = update_content[kitem_old]
+                    if isinstance(update_content, dict):
+                        kitem_old = kitem
+                    elif isinstance(update_content, list):
+                        kitem_old = int(kitem)
+                    else:
+                        raise ROClientException(
+                            "Invalid query string '{}'. Descriptor is not a list nor dict at '{}'".format(k, kitem))
+                if v == "__DELETE__":
+                    del update_content[kitem_old]
+                else:
+                    update_content[kitem_old] = v
+            return desc
+        except KeyError:
+            raise ROClientException(
+                "Invalid query string '{}'. Descriptor does not contain '{}'".format(k, kitem_old))
+        except ValueError:
+            raise ROClientException("Invalid query string '{}'. Expected integer index list instead of '{}'".format(
+                k, kitem))
+        except IndexError:
+            raise ROClientException(
+                "Invalid query string '{}'. Index '{}' out of  range".format(k, kitem_old))
+
     @staticmethod
     def check_ns_status(ns_descriptor):
         """
@@ -248,14 +279,14 @@ class ROClient:
 
         for net in ns_descriptor["nets"]:
             net_total += 1
-            if net["status"] == "ERROR":
+            if net["status"] in ("ERROR", "VIM_ERROR"):
                 return "ERROR", net["error_msg"]
             elif net["status"] == "ACTIVE":
                 net_done += 1
         for vnf in ns_descriptor["vnfs"]:
             for vm in vnf["vms"]:
                 vm_total += 1
-                if vm["status"] == "ERROR":
+                if vm["status"] in ("ERROR", "VIM_ERROR"):
                     return "ERROR", vm["error_msg"]
                 elif vm["status"] == "ACTIVE":
                     vm_done += 1
@@ -268,44 +299,22 @@ class ROClient:
     @staticmethod
     def get_ns_vnf_ip(ns_descriptor):
         """
-        Get a dict with the IPs of every vnf.
+        Get a dict with the IPs of every vnf and vdu
         :param ns_descriptor: instance descriptor obtained with self.show("ns", )
-        :return: dict iwth key member_vnf_index, value ip_address
+        :return: dict with {member_vnf_index: ip_address, ... member_vnf_index.vdu_id: ip_address ...}
         """
-        ns_ip={}
+        ns_ip = {"vnf": {}, "vdu": {}}
         for vnf in ns_descriptor["vnfs"]:
-            ns_ip[str(vnf["member_vnf_index"])] = vnf["ip_address"]
-            #uuid  sce_vnf_id
-            # vnf[mgmt_access]: '{interface_id: cf3cbf00-385c-49b4-9a3f-b400b7b15dc6, vm_id: d0dd22a9-91ef-46f1-8e8f-8cf4b2d5b2d7}'
-            # vnf[vms]
+            if not vnf.get("ip_address"):
+                raise ROClientException("No ip_address returned for ns member_vnf_index '{}'".format(
+                    vnf["member_vnf_index"]), http_code=500)
+            ns_ip["vnf"][str(vnf["member_vnf_index"])] = vnf.get("ip_address")
+            ns_ip["vdu"][str(vnf["member_vnf_index"])] = {}
+            for vm in vnf["vms"]:
+                if vm.get("ip_address"):
+                    ns_ip["vdu"][str(vnf["member_vnf_index"])][vm["vdu_osm_id"]] = vm["ip_address"]
         return ns_ip
 
-    async def get_list(self, item, all_tenants=False, filter_by=None):
-        """
-        Obtain a list of items filtering by the specigy filter_by.
-        :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
-        :param all_tenants: True if not filtering by tenant. Only allowed for admin
-        :param filter_by: dictionary with filtering
-        :return: a list of dict. It can be empty. Raises ROClientException on Error,
-        """
-        try:
-            if item not in self.client_to_RO:
-                raise ROClientException("Invalid item {}".format(item))
-            if item == 'tenant':
-                all_tenants = None
-            with aiohttp.ClientSession(loop=self.loop) as session:
-                content = await self._list_item(session, self.client_to_RO[item], all_tenants=all_tenants,
-                                                filter_dict=filter_by)
-            if isinstance(content, dict):
-                if len(content) == 1:
-                    for _, v in content.items():
-                        return v
-                    return content.values()[0]
-                else:
-                    raise ROClientException("Output not a list neither dict with len equal 1", http_code=500)
-                return content
-        except aiohttp.errors.ClientOSError as e:
-            raise ROClientException(e, http_code=504)
 
     async def _get_item_uuid(self, session, item, item_id_name, all_tenants=False):
         if all_tenants:
@@ -367,7 +376,7 @@ class ROClient:
             uuid = item_id_name
         else:
             # check that exist
-            uuid = self._get_item_uuid(session, item, item_id_name, all_tenants)
+            uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
         
         url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
         self.logger.debug("GET %s", url )
@@ -392,7 +401,7 @@ class ROClient:
             self.datacenter = await self._get_item_uuid(session, "datacenters", self.datacenter_id_name, True)
         return self.datacenter
 
-    async def _create_item(self, session, item, descriptor, all_tenants=False):
+    async def _create_item(self, session, item, descriptor, item_id_name=None, action=None, all_tenants=False):
         if all_tenants:
             tenant_text = "/any"
         elif all_tenants is None:
@@ -402,6 +411,7 @@ class ROClient:
                 await self._get_tenant(session)
             tenant_text = "/" + self.tenant
         payload_req = yaml.safe_dump(descriptor)
+        #print payload_req
 
         api_version_text = ""
         if item == "vnfs":
@@ -413,10 +423,21 @@ class ROClient:
             api_version_text = "/v3"
             item = "nsd"
 
-        #print payload_req
+        if not item_id_name:
+            uuid=""
+        elif self.check_if_uuid(item_id_name):
+            uuid = "/{}".format(item_id_name)
+        else:
+            # check that exist
+            uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
+            uuid = "/{}".format(uuid)
+        if not action:
+            action = ""
+        else:
+            action = "/".format(action)
 
-        url = "{}{apiver}{tenant}/{item}".format(self.endpoint_url, apiver=api_version_text, tenant=tenant_text,
-                                                        item=item)
+        url = "{}{apiver}{tenant}/{item}{id}{action}".format(self.endpoint_url, apiver=api_version_text, tenant=tenant_text,
+                                                        item=item, id=uuid, action=action)
         self.logger.debug("openmano POST %s %s", url, payload_req)
         with aiohttp.Timeout(self.timeout_large):
             async with session.post(url, headers=self.headers_req, data=payload_req) as response:
@@ -425,9 +446,7 @@ class ROClient:
                 if response.status >= 300:
                     raise ROClientException(response_text, http_code=response.status)
 
-        response_desc = self._parse_yaml(response_text, response=True)
-        desc, _ = remove_envelop(item, response_desc)
-        return desc
+        return self._parse_yaml(response_text, response=True)
 
     async def _del_item(self, session, item, item_id_name, all_tenants=False):
         if all_tenants:
@@ -440,7 +459,10 @@ class ROClient:
             tenant_text = "/" + self.tenant
         if not self.check_if_uuid(item_id_name):
             # check that exist
-            uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
+            _all_tenants = all_tenants
+            if item == "datacenters":
+                _all_tenants = True
+            uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants=_all_tenants)
         else:
             uuid = item_id_name
         
@@ -479,7 +501,7 @@ class ROClient:
                     raise ROClientException(response_text, http_code=response.status)
         return self._parse_yaml(response_text, response=True)
 
-    async def _edit_item(self, session, item, descriptor, item_id_name, all_tenants=False):
+    async def _edit_item(self, session, item, item_id, descriptor, all_tenants=False):
         if all_tenants:
             tenant_text = "/any"
         elif all_tenants is None:
@@ -489,15 +511,11 @@ class ROClient:
                 await self._get_tenant(session)
             tenant_text = "/" + self.tenant
 
-        if not uuid:
-            #check that exist
-            uuid = self._get_item_uuid(session, "tenants", item_id_name, all_tenants)
-        
         payload_req = yaml.safe_dump(descriptor)
             
         #print payload_req
             
-        url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
+        url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, item_id)
         self.logger.debug("openmano PUT %s %s", url, payload_req)
         with aiohttp.Timeout(self.timeout_large):
             async with session.put(url, headers=self.headers_req, data=payload_req) as response:
@@ -507,6 +525,33 @@ class ROClient:
                     raise ROClientException(response_text, http_code=response.status)
         return self._parse_yaml(response_text, response=True)
 
+    async def get_list(self, item, all_tenants=False, filter_by=None):
+        """
+        Obtain a list of items filtering by the specigy filter_by.
+        :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
+        :param all_tenants: True if not filtering by tenant. Only allowed for admin
+        :param filter_by: dictionary with filtering
+        :return: a list of dict. It can be empty. Raises ROClientException on Error,
+        """
+        try:
+            if item not in self.client_to_RO:
+                raise ROClientException("Invalid item {}".format(item))
+            if item == 'tenant':
+                all_tenants = None
+            with aiohttp.ClientSession(loop=self.loop) as session:
+                content = await self._list_item(session, self.client_to_RO[item], all_tenants=all_tenants,
+                                                filter_dict=filter_by)
+            if isinstance(content, dict):
+                if len(content) == 1:
+                    for _, v in content.items():
+                        return v
+                    return content.values()[0]
+                else:
+                    raise ROClientException("Output not a list neither dict with len equal 1", http_code=500)
+                return content
+        except aiohttp.errors.ClientOSError as e:
+            raise ROClientException(e, http_code=504)
+
     async def show(self, item, item_id_name=None, all_tenants=False):
         """
         Obtain the information of an item from its id or name
@@ -520,13 +565,14 @@ class ROClient:
                 raise ROClientException("Invalid item {}".format(item))
             if item == 'tenant':
                 all_tenants = None
+            elif item == 'vim':
+                all_tenants = True
+            elif item == 'vim_account':
+                all_tenants = False
 
             with aiohttp.ClientSession(loop=self.loop) as session:
                 content = await self._get_item(session, self.client_to_RO[item], item_id_name, all_tenants=all_tenants)
-            if len(content) == 1:
-                return content.values()[0]
-            else:
-                return content
+                return remove_envelop(item, content)
         except aiohttp.errors.ClientOSError as e:
             raise ROClientException(e, http_code=504)
 
@@ -541,22 +587,17 @@ class ROClient:
         try:
             if item not in self.client_to_RO:
                 raise ROClientException("Invalid item {}".format(item))
-            if item == 'tenant':
+            if item == 'tenant' or item == 'vim':
                 all_tenants = None
 
             with aiohttp.ClientSession(loop=self.loop) as session:
-                if item == 'vim':
-                    # check that exist
-                    item_id = await self._get_item_uuid(session, "datacenters", item_id_name, all_tenants=True)
-                    all_tenants = None
                 return await self._del_item(session, self.client_to_RO[item], item_id_name, all_tenants=all_tenants)
         except aiohttp.errors.ClientOSError as e:
             raise ROClientException(e, http_code=504)
 
-    async def create(self, item, descriptor=None, descriptor_format=None, **kwargs):
-        """
-        Creates an item from its descriptor
-        :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
+    async def edit(self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs):
+        """ Edit an item
+        :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns', 'vim'
         :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
         :param descriptor_format: Can be 'json' or 'yaml'
         :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
@@ -573,91 +614,134 @@ class ROClient:
 
             if item not in self.client_to_RO:
                 raise ROClientException("Invalid item {}".format(item))
-            desc, enveloped = remove_envelop(item, descriptor)
+            desc = remove_envelop(item, descriptor)
 
             # Override descriptor with kwargs
             if kwargs:
-                desc = deepcopy(desc)  # do not modify original descriptor
-                try:
-                    for k, v in kwargs.items():
-                        update_content = desc
-                        kitem_old = None
-                        klist = k.split(".")
-                        for kitem in klist:
-                            if kitem_old is not None:
-                                update_content = update_content[kitem_old]
-                            if isinstance(update_content, dict):
-                                kitem_old = kitem
-                            elif isinstance(update_content, list):
-                                kitem_old = int(kitem)
-                            else:
-                                raise ROClientException(
-                                    "Invalid query string '{}'. Descriptor is not a list nor dict at '{}'".format(k, kitem))
-                        if v == "__DELETE__":
-                            del update_content[kitem_old]
-                        else:
-                            update_content[kitem_old] = v
-                except KeyError:
-                    raise ROClientException(
-                        "Invalid query string '{}'. Descriptor does not contain '{}'".format(k, kitem_old))
-                except ValueError:
-                    raise ROClientException("Invalid query string '{}'. Expected integer index list instead of '{}'".format(
-                        k, kitem))
-                except IndexError:
-                    raise ROClientException(
-                        "Invalid query string '{}'. Index '{}' out of  range".format(k, kitem_old))
-
-            for mandatory in self.mandatory_for_create[item]:
-                if mandatory not in desc:
-                    raise ROClientException("'{}' is mandatory parameter for {}".format(mandatory, item))
-
+                desc = self.update_descriptor(desc, kwargs)
             all_tenants = False
             if item in ('tenant', 'vim'):
                 all_tenants = None
 
-            if not enveloped:
-                create_desc = self._create_envelop(item, desc)
-            else:
-                create_desc = descriptor
+            create_desc = self._create_envelop(item, desc)
 
             with aiohttp.ClientSession(loop=self.loop) as session:
-                return await self._create_item(session, self.client_to_RO[item], create_desc, all_tenants)
+                _all_tenants = all_tenants
+                if item == 'vim':
+                    _all_tenants = True
+                item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name, all_tenants=_all_tenants)
+                # await self._get_tenant(session)
+                outdata = await self._edit_item(session, self.client_to_RO[item], item_id, create_desc, all_tenants=all_tenants)
+                return remove_envelop(item, outdata)
         except aiohttp.errors.ClientOSError as e:
             raise ROClientException(e, http_code=504)
 
-    def edit_tenant(self, uuid=None, name=None, descriptor=None, descriptor_format=None, new_name=None, new_description=None):
-        """Edit the parameters of a tenant
-        Params: must supply a descriptor or/and a new_name or new_description
-            uuid or/and name. If only name is supplied, there must be only one or an exception is raised
-            descriptor: with format {'tenant':{params to change info}}
-                must be a dictionary or a json/yaml text.
-            name: the tenant name. Overwrite descriptor name if any
-            description: tenant descriptor.. Overwrite descriptor description if any
-        Return: Raises an exception on error, not found or found several
-                Obtain a dictionary with format {'tenant':{newtenant_info}}
+    async def create(self, item, descriptor=None, descriptor_format=None, **kwargs):
+        """
+        Creates an item from its descriptor
+        :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
+        :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
+        :param descriptor_format: Can be 'json' or 'yaml'
+        :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
+               keys can be a dot separated list to specify elements inside dict
+        :return: dictionary with the information or raises ROClientException on Error
         """
         try:
-            # TODO revise
             if isinstance(descriptor, str):
-                descriptor = self.parse(descriptor, descriptor_format)
+                descriptor = self._parse(descriptor, descriptor_format)
             elif descriptor:
                 pass
-            elif new_name or new_description:
-                descriptor={"tenant": {}}
             else:
-                raise ROClientException("Missing descriptor")
+                descriptor = {}
 
-            if 'tenant' not in descriptor or len(descriptor)!=1:
-                raise ROClientException("Descriptor must contain only one 'tenant' field")
-            if new_name:
-                descriptor['tenant']['name'] = new_name
-            if new_description:
-                descriptor['tenant']['description'] = new_description
+            if item not in self.client_to_RO:
+                raise ROClientException("Invalid item {}".format(item))
+            desc = remove_envelop(item, descriptor)
+
+            # Override descriptor with kwargs
+            if kwargs:
+                desc = self.update_descriptor(desc, kwargs)
+
+            for mandatory in self.mandatory_for_create[item]:
+                if mandatory not in desc:
+                    raise ROClientException("'{}' is mandatory parameter for {}".format(mandatory, item))
+
+            all_tenants = False
+            if item in ('tenant', 'vim'):
+                all_tenants = None
+
+            create_desc = self._create_envelop(item, desc)
 
-            return self._edit_item("tenants", descriptor, uuid, name, all_tenants=None)
+            with aiohttp.ClientSession(loop=self.loop) as session:
+                outdata = await self._create_item(session, self.client_to_RO[item], create_desc,
+                                                  all_tenants=all_tenants)
+                return remove_envelop(item, outdata)
         except aiohttp.errors.ClientOSError as e:
             raise ROClientException(e, http_code=504)
 
+    async def attach_datacenter(self, datacenter=None, descriptor=None, descriptor_format=None, **kwargs):
+
+        if isinstance(descriptor, str):
+            descriptor = self._parse(descriptor, descriptor_format)
+        elif descriptor:
+            pass
+        else:
+            descriptor = {}
+        desc = remove_envelop("vim", descriptor)
+
+        # # check that exist
+        # uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=True)
+        # tenant_text = "/" + self._get_tenant()
+        if kwargs:
+            desc = self.update_descriptor(desc, kwargs)
+
+        if not desc.get("vim_tenant_name") and not desc.get("vim_tenant_id"):
+            raise ROClientException("Wrong descriptor. At least vim_tenant_name or vim_tenant_id must be provided")
+        create_desc = self._create_envelop("vim", desc)
+        payload_req = yaml.safe_dump(create_desc)
+        with aiohttp.ClientSession(loop=self.loop) as session:
+            # check that exist
+            item_id = await self._get_item_uuid(session, "datacenters", datacenter, all_tenants=True)
+            await self._get_tenant(session)
+
+            url = "{}/{tenant}/datacenters/{datacenter}".format(self.endpoint_url, tenant=self.tenant,
+                                                     datacenter=item_id)
+            self.logger.debug("openmano POST %s %s", url, payload_req)
+            with aiohttp.Timeout(self.timeout_large):
+                async with session.post(url, headers=self.headers_req, data=payload_req) as response:
+                    response_text = await response.read()
+                    self.logger.debug("POST {} [{}] {}".format(url, response.status, response_text[:100]))
+                    if response.status >= 300:
+                        raise ROClientException(response_text, http_code=response.status)
+
+            response_desc = self._parse_yaml(response_text, response=True)
+            desc  = remove_envelop("vim", response_desc)
+            return desc
+
+    async def detach_datacenter(self, datacenter=None):
+        #TODO replace the code with delete_item(vim_account,...)
+        with aiohttp.ClientSession(loop=self.loop) as session:
+            # check that exist
+            item_id = await self._get_item_uuid(session, "datacenters", datacenter, all_tenants=False)
+            tenant = await self._get_tenant(session)
+
+            url = "{}/{tenant}/datacenters/{datacenter}".format(self.endpoint_url, tenant=tenant,
+                                                     datacenter=item_id)
+            self.logger.debug("openmano DELETE %s", url)
+            with aiohttp.Timeout(self.timeout_large):
+                async with session.delete(url, headers=self.headers_req) as response:
+                    response_text = await response.read()
+                    self.logger.debug("DELETE {} [{}] {}".format(url, response.status, response_text[:100]))
+                    if response.status >= 300:
+                        raise ROClientException(response_text, http_code=response.status)
+
+            response_desc = self._parse_yaml(response_text, response=True)
+            desc = remove_envelop("vim", response_desc)
+            return desc
+
+
+    # TODO convert to asyncio
+
     #DATACENTERS
 
     def edit_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
@@ -695,66 +779,6 @@ class ROClient:
                 descriptor['datacenter'][param] = kwargs[param]
         return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
     
-    def attach_datacenter(self, uuid_name=None, descriptor=None, descriptor_format=None,  vim_user=None, vim_password=None, vim_tenant_name=None, vim_tenant_id=None):
-        #check that exist
-        uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=True)
-        tenant_text = "/"+self._get_tenant()
-
-        if isinstance(descriptor, str):
-            descriptor = self.parse(descriptor, descriptor_format)
-        elif descriptor:
-            pass
-        elif vim_user or vim_password or vim_tenant_name or vim_tenant_id:
-            descriptor={"datacenter": {}}
-        else:
-            raise ROClientException("Missing descriptor or params")
-        
-        if vim_user or vim_password or vim_tenant_name or vim_tenant_id:
-            #print args.name
-            try:
-                if vim_user:
-                    descriptor['datacenter']['vim_user'] = vim_user
-                if vim_password:
-                    descriptor['datacenter']['vim_password'] = vim_password
-                if vim_tenant_name:
-                    descriptor['datacenter']['vim_tenant_name'] = vim_tenant_name
-                if vim_tenant_id:
-                    descriptor['datacenter']['vim_tenant'] = vim_tenant_id
-            except (KeyError, TypeError) as e:
-                if str(e)=='datacenter':           error_pos= "missing field 'datacenter'"
-                else:                       error_pos="wrong format"
-                raise ROClientException("Wrong datacenter descriptor: " + error_pos)
-
-        payload_req = yaml.safe_dump(descriptor)
-        #print payload_req
-        url = "{}{}/datacenters/{}".format(self.endpoint_url, tenant_text, uuid)
-        self.logger.debug("openmano POST %s %s", url, payload_req)
-        mano_response = requests.post(url, headers = self.headers_req, data=payload_req)
-        self.logger.debug("openmano response: %s", mano_response.text )
-    
-        content = self._parse_yaml(mano_response.text, response=True)
-        if mano_response.status_code==200:
-            return content
-        else:
-            raise ROClientException(str(content), http_code=mano_response.status)        
-
-    def detach_datacenter(self, uuid_name=None):
-        if not uuid:
-            #check that exist
-            uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=False)
-        tenant_text = "/"+self._get_tenant()
-        url = "{}{}/datacenters/{}".format(self.endpoint_url, tenant_text, uuid)
-        self.logger.debug("openmano DELETE %s", url)
-        mano_response = requests.delete(url, headers = self.headers_req)
-        self.logger.debug("openmano response: %s", mano_response.text )
-    
-        content = self._parse_yaml(mano_response.text, response=True)
-        if mano_response.status_code==200:
-            return content
-        else:
-            raise ROClientException(str(content), http_code=mano_response.status)        
-
-    #VNFS
 
     def edit_scenario(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
         """Edit the parameters of a scenario
@@ -770,7 +794,7 @@ class ROClient:
         Return: Raises an exception on error, not found or found several
                 Obtain a dictionary with format {'scenario':{new_scenario_info}}
         """
+
         if isinstance(descriptor, str):
             descriptor = self.parse(descriptor, descriptor_format)
         elif descriptor:
@@ -779,7 +803,7 @@ class ROClient:
             descriptor={"scenario": {}}
         else:
             raise ROClientException("Missing descriptor")
+
         if 'scenario' not in descriptor or len(descriptor)>2:
             raise ROClientException("Descriptor must contain only one 'scenario' field")
         for param in kwargs:
@@ -888,16 +912,81 @@ class ROClient:
 
 if __name__ == '__main__':
     RO_URL = "http://localhost:9090/openmano"
-    RO_TENANT = "osm"
-    RO_VIM = "myvim"
+    TEST_TENANT = "myTenant"
+    TEST_VIM1 = "myvim"
+    TEST_URL1 = "https://localhost:5000/v1"
+    TEST_TYPE1 = "openstack"
+    TEST_CONFIG1 = {"use_floating_ip": True}
+    TEST_VIM2 = "myvim2"
+    TEST_URL2 = "https://localhost:5000/v2"
+    TEST_TYPE2 = "openvim"
+    TEST_CONFIG2 = {"config2": "config2", "config3": True}
 
     streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
     logging.basicConfig(format=streamformat)
+    logger = logging.getLogger("ROClient")
 
+    tenant_id = None
+    vim_id = False
     loop = asyncio.get_event_loop()
-    myClient = ROClient(endpoint_url=RO_URL, loop=loop, tenant_id=RO_TENANT, datacenter_id=RO_VIM, debug=True)
-    content = loop.run_until_complete(myClient.list_tenants())
-    print(content)
+    myClient = ROClient(endpoint_url=RO_URL, loop=loop, loglevel="DEBUG")
+    try:
+        # test tenant
+        content = loop.run_until_complete(myClient.get_list("tenant"))
+        print("tenants", content)
+        content = loop.run_until_complete(myClient.create("tenant", name=TEST_TENANT))
+        tenant_id = True
+        content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
+        print("tenant", TEST_TENANT, content)
+        content = loop.run_until_complete(myClient.edit("tenant", TEST_TENANT,  description="another description"))
+        content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
+        print("tenant edited", TEST_TENANT, content)
+        myClient["tenant"] = TEST_TENANT
+
+
+        # test VIM
+        content = loop.run_until_complete(myClient.create("vim", name=TEST_VIM1, type=TEST_TYPE1, vim_url=TEST_URL1, config=TEST_CONFIG1))
+        vim_id = True
+        content = loop.run_until_complete(myClient.get_list("vim"))
+        print("vim", content)
+        content = loop.run_until_complete(myClient.show("vim", TEST_VIM1))
+        print("vim", TEST_VIM1, content)
+        content = loop.run_until_complete(myClient.edit("vim", TEST_VIM1,  description="another description",
+                                                        name=TEST_VIM2, type=TEST_TYPE2, vim_url=TEST_URL2,
+                                                        config=TEST_CONFIG2))
+        content = loop.run_until_complete(myClient.show("vim", TEST_VIM2))
+        print("vim edited", TEST_VIM2, content)
+
+        # test VIM_ACCOUNT
+        content = loop.run_until_complete(myClient.attach_datacenter(TEST_VIM2, vim_username='user',
+                                                          vim_password='pass', vim_tenant_name='vimtenant1', config=TEST_CONFIG1))
+        vim_id = True
+        content = loop.run_until_complete(myClient.get_list("vim_account"))
+        print("vim_account", content)
+        content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
+        print("vim_account", TEST_VIM2, content)
+        content = loop.run_until_complete(myClient.edit("vim_account", TEST_VIM2,  vim_username='user2', vim_password='pass2',
+                                                        vim_tenant_name="vimtenant2", config=TEST_CONFIG2))
+        content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
+        print("vim_account edited", TEST_VIM2, content)
+
+        myClient["vim"] = TEST_VIM2
+
+    except Exception as e:
+        logger.error("Error {}".format(e), exc_info=True)
+
+    for item in (("vim_account", TEST_VIM1), ("vim", TEST_VIM1),
+                 ("vim_account", TEST_VIM2), ("vim", TEST_VIM2),
+                 ("tenant", TEST_TENANT)):
+        try:
+            content = loop.run_until_complete(myClient.delete(item[0], item[1]))
+            print("{} {} deleted; {}".format(item[0], item[1], content))
+        except Exception as e:
+            if e.http_code == 404:
+                print("{} {} not present or already deleted".format(item[0], item[1]))
+            else:
+                logger.error("Error {}".format(e), exc_info=True)
+
     loop.close()