__author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes"
__date__ = "$26-aug-2014 11:09:29$"
-__version__ = "0.5.38-r548"
+__version__ = "0.5.39-r549"
version_date = "Nov 2017"
database_version = 27 # expected database schema version
else:
av_index = None
- vm_id = myvim.new_vminstance(myVMDict['name'], myVMDict['description'], myVMDict.get('start', None),
+ vm_id, _ = myvim.new_vminstance(myVMDict['name'], myVMDict['description'], myVMDict.get('start', None),
myVMDict['imageRef'], myVMDict['flavorRef'], myVMDict['networks'],
availability_zone_index=av_index,
availability_zone_list=vnf_availability_zones)
iface_id: uuid of intance_interfaces
sdn_port_id:
sdn_net_id:
+ created_items: dictionary with extra elements created that need to be deleted. e.g. ports, volumes,...
created: False if the VIM element is not created by other actions, and it should not be deleted
vim_status: VIM status of the element. Stored also at database in the instance_XXX
M depends: dict with task_index(from depends_on) to task class
task["extra"]["sdn_vim_id"] = to_supersede["extra"]["sdn_vim_id"]
if to_supersede["extra"].get("interfaces"):
task["extra"]["interfaces"] = to_supersede["extra"]["interfaces"]
+ if to_supersede["extra"].get("created_items"):
+ if not task["extra"].get("created_items"):
+ task["extra"]["created_items"] = {}
+ task["extra"]["created_items"].update(to_supersede["extra"]["created_items"])
# Mark task as SUPERSEDED.
# If task is in self.pending_tasks, it will be removed and database will be update
# If task is in self.refresh_tasks, it will be removed
"Cannot create VM because depends on a network not created or found: " +
str(task_net["error_msg"]))
net["net_id"] = network_id
- vim_vm_id = self.vim.new_vminstance(*params)
+ vim_vm_id, created_items = self.vim.new_vminstance(*params)
# fill task_interfaces. Look for snd_net_id at database for each interface
task_interfaces = {}
task["vim_interfaces"] = {}
task["extra"]["interfaces"] = task_interfaces
task["extra"]["created"] = True
+ task["extra"]["created_items"] = created_items
task["error_msg"] = None
task["status"] = "DONE"
task["vim_id"] = vim_vm_id
iface["sdn_port_id"], vm_vim_id) + str(e), exc_info=True)
# TODO Set error_msg at instance_nets
- self.vim.delete_vminstance(vm_vim_id)
+ self.vim.delete_vminstance(vm_vim_id, task["extra"].get("created_items"))
task["status"] = "DONE"
task["error_msg"] = None
return True, None
return False, None
def _get_net_internal(self, task, filter_param):
+ """
+ Common code for get_net and new_net. It looks for a network on VIM with the filter_params
+ :param task: task for this find or find-or-create action
+ :param filter_param: parameters to send to the vimconnector
+ :return: a dict with the content to update the instance_nets database table. Raises an exception on error, or
+ when network is not found or found more than one
+ """
vim_nets = self.vim.get_network_list(filter_param)
if not vim_nets:
raise VimThreadExceptionNotFound("Network not found with this criteria: '{}'".format(filter))
availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
availability_zone_index is None
- Returns the instance identifier or raises an exception on error
+ Returns a tuple with the instance 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_vminstance and action_vminstance. Can be used to store created ports, volumes, 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" )
"""Returns the VM instance information from VIM"""
raise vimconnNotImplemented( "Should have implemented this" )
- def delete_vminstance(self, vm_id):
- """Removes a VM instance from VIM
- Returns the instance identifier"""
+ def delete_vminstance(self, vm_id, created_items=None):
+ """
+ Removes a VM instance from VIM and each associate 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
+ :return: None or the same vm_id. Raises an exception on fail
+ """
raise vimconnNotImplemented( "Should have implemented this" )
def refresh_vms_status(self, vm_list):
"""
raise vimconnNotImplemented( "Should have implemented this" )
- def action_vminstance(self, vm_id, action_dict):
- """Send and action over a VM instance from VIM
- Returns the vm_id if the action was successfully sent to the VIM"""
+ def action_vminstance(self, vm_id, action_dict, created_items={}):
+ """
+ Send and action over a VM instance. Returns created_items if the action was successfully sent to the VIM.
+ created_items is a dictionary with items that
+ :param vm_id: VIM identifier of the VM, provided by method new_vminstance
+ :param action_dict: dictionary with the action to perform
+ :param created_items: provided by method new_vminstance is a dictionary with key-values that will be passed to
+ the method delete_vminstance. Can be used to store created ports, volumes, etc. Format is vimconnector
+ dependent, but do not use nested dictionaries and a value of None should be the same as not present. This
+ method can modify this value
+ :return: None, or a console dict
+ """
raise vimconnNotImplemented( "Should have implemented this" )
def get_vminstance_console(self, vm_id, console_type="vnc"):
disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
size': (mandatory) string with the size of the disk in GB
- Returns: instance identifier or raises an exception on error
+ Returns a tuple with the instance 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_vminstance and action_vminstance. Can be used to store created ports, volumes, 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("Creating a new VM instance")
net_list[index]['vim_id'] = reservation.instances[0].interfaces[index].id
instance = reservation.instances[0]
- return instance.id
+ return instance.id, None
except Exception as e:
self.format_vimconn_exception(e)
except Exception as e:
self.format_vimconn_exception(e)
- def delete_vminstance(self, vm_id):
+ def delete_vminstance(self, vm_id, created_items=None):
"""Removes a VM instance from VIM
Returns the instance identifier"""
self.logger.error("Exception getting vm status: %s", str(e), exc_info=True)
self.format_vimconn_exception(e)
- def action_vminstance(self, vm_id, action_dict):
+ def action_vminstance(self, vm_id, action_dict, created_items={}):
"""Send and action over a VM instance from VIM
Returns the vm_id if the action was successfully sent to the VIM"""
self.conn.terminate_instances(vm_id)
elif "reboot" in action_dict:
self.conn.reboot_instances(vm_id)
- return vm_id
+ return None
except Exception as e:
self.format_vimconn_exception(e)
def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None,
availability_zone_index=None, availability_zone_list=None):
- '''Adds a VM instance to VIM
+ """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
availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
availability_zone_index is None
#TODO ip, security groups
- Returns the instance identifier
- '''
+ Returns a tuple with the instance 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_vminstance and action_vminstance. Can be used to store created ports, volumes, 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_vminstance input: image='%s' flavor='%s' nics='%s'",image_id, flavor_id,str(net_list))
try:
server = None
- metadata={}
- net_list_vim=[]
- external_network=[] # list of external networks to be connected to instance, later on used to create floating_ip
+ created_items = {}
+ metadata = {}
+ net_list_vim = []
+ external_network = [] # list of external networks to be connected to instance, later on used to create floating_ip
no_secured_ports = [] # List of port-is with port-security disabled
self._reload_connection()
- metadata_vpci={} # For a specific neutron plugin
+ metadata_vpci = {} # For a specific neutron plugin
block_device_mapping = None
for net in net_list:
- if not net.get("net_id"): #skip non connected iface
+ if not net.get("net_id"): # skip non connected iface
continue
port_dict={
if net.get("mac_address"):
port_dict["mac_address"]=net["mac_address"]
new_port = self.neutron.create_port({"port": port_dict })
+ created_items[("port", str(new_port["port"]["id"]))] = True
net["mac_adress"] = new_port["port"]["mac_address"]
net["vim_id"] = new_port["port"]["id"]
# if try to use a network without subnetwork, it will return a emtpy list
security_groups = self.config.get('security_groups')
if type(security_groups) is str:
security_groups = ( security_groups, )
- #cloud config
+ # cloud config
config_drive, userdata = self._create_user_data(cloud_config)
- #Create additional volumes in case these are present in disk_list
+ # Create additional volumes in case these are present in disk_list
base_disk_index = ord('b')
if disk_list != None:
block_device_mapping = {}
else:
volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
chr(base_disk_index))
+ created_items[("volume", str(volume.id))] = True
block_device_mapping['_vd' + chr(base_disk_index)] = volume.id
base_disk_index += 1
- #wait until volumes are with status available
+ # wait until volumes are with status available
keep_waiting = True
elapsed_time = 0
while keep_waiting and elapsed_time < volume_timeout:
time.sleep(1)
elapsed_time += 1
- #if we exceeded the timeout rollback
+ # if we exceeded the timeout rollback
if elapsed_time >= volume_timeout:
- #delete the volumes we just created
- for volume_id in block_device_mapping.itervalues():
- self.cinder.volumes.delete(volume_id)
-
- #delete ports we just created
- for net_item in net_list_vim:
- if 'port-id' in net_item:
- self.neutron.delete_port(net_item['port-id'])
-
raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
http_code=vimconn.HTTP_Request_Timeout)
# get availability Zone
for port_id in no_secured_ports:
try:
self.neutron.update_port(port_id, {"port": {"port_security_enabled": False, "security_groups": None} })
-
except Exception as e:
self.logger.error("It was not possible to disable port security for port {}".format(port_id))
- self.delete_vminstance(server.id)
raise
- #print "DONE :-)", server
- pool_id = None
- floating_ips = self.neutron.list_floatingips().get("floatingips", ())
+ # print "DONE :-)", server
+ pool_id = None
if external_network:
+ floating_ips = self.neutron.list_floatingips().get("floatingips", ())
self.__wait_for_vm(server.id, 'ACTIVE')
for floating_network in external_network:
try:
assigned = False
- while(assigned == False):
+ while not assigned:
if floating_ips:
ip = floating_ips.pop(0)
if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id:
continue
raise
- return server.id
+ return server.id, created_items
# except nvExceptions.NotFound as e:
# error_value=-vimconn.HTTP_Not_Found
# error_text= "vm instance %s not found" % vm_id
# raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
except Exception as e:
- # delete the volumes we just created
- if block_device_mapping:
- for volume_id in block_device_mapping.itervalues():
- self.cinder.volumes.delete(volume_id)
-
- # Delete the VM
- if server != None:
- self.delete_vminstance(server.id)
- else:
- # delete ports we just created
- for net_item in net_list_vim:
- if 'port-id' in net_item:
- self.neutron.delete_port(net_item['port-id'])
+ server_id = None
+ if server:
+ server_id = server.id
+ try:
+ self.delete_vminstance(server_id, created_items)
+ except Exception as e2:
+ self.logger.error("new_vminstance rollback fail {}".format(e2))
self._format_exception(e)
except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
self._format_exception(e)
- def delete_vminstance(self, vm_id):
+ def delete_vminstance(self, vm_id, created_items=None):
'''Removes a VM instance from VIM. Returns the old identifier
'''
#print "osconnector: Getting VM from VIM"
+ if created_items == None:
+ created_items = {}
try:
self._reload_connection()
- #delete VM ports attached to this networks before the virtual machine
- ports = self.neutron.list_ports(device_id=vm_id)
- for p in ports['ports']:
+ # delete VM ports attached to this networks before the virtual machine
+ for k, v in created_items.items():
+ if not v: # skip already deleted
+ continue
try:
- self.neutron.delete_port(p["id"])
+ if k[0] == "port":
+ self.neutron.delete_port(k[1])
except Exception as e:
self.logger.error("Error deleting port: " + type(e).__name__ + ": "+ str(e))
- #commented because detaching the volumes makes the servers.delete not work properly ?!?
- #dettach volumes attached
- server = self.nova.servers.get(vm_id)
- volumes_attached_dict = server._info['os-extended-volumes:volumes_attached']
- #for volume in volumes_attached_dict:
- # self.cinder.volumes.detach(volume['id'])
+ # #commented because detaching the volumes makes the servers.delete not work properly ?!?
+ # #dettach volumes attached
+ # server = self.nova.servers.get(vm_id)
+ # volumes_attached_dict = server._info['os-extended-volumes:volumes_attached'] #volume['id']
+ # #for volume in volumes_attached_dict:
+ # # self.cinder.volumes.detach(volume['id'])
- self.nova.servers.delete(vm_id)
+ if vm_id:
+ self.nova.servers.delete(vm_id)
- #delete volumes.
- #Although having detached them should have them in active status
- #we ensure in this loop
+ # delete volumes. Although having detached, they should have in active status before deleting
+ # we ensure in this loop
keep_waiting = True
elapsed_time = 0
while keep_waiting and elapsed_time < volume_timeout:
keep_waiting = False
- for volume in volumes_attached_dict:
- if self.cinder.volumes.get(volume['id']).status != 'available':
- keep_waiting = True
- else:
- self.cinder.volumes.delete(volume['id'])
+ for k, v in created_items.items():
+ if not v: # skip already deleted
+ continue
+ try:
+ if k[0] == "volume":
+ if self.cinder.volumes.get(k[1]).status != 'available':
+ keep_waiting = True
+ else:
+ self.cinder.volumes.delete(k[1])
+ except Exception as e:
+ self.logger.error("Error deleting volume: " + type(e).__name__ + ": " + str(e))
if keep_waiting:
time.sleep(1)
elapsed_time += 1
-
- return vm_id
+ return None
except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
self._format_exception(e)
- #TODO insert exception vimconn.HTTP_Unauthorized
- #if reaching here is because an exception
def refresh_vms_status(self, vm_list):
'''Get the status of the virtual machines and their interfaces/ports
vm_dict[vm_id] = vm
return vm_dict
- def action_vminstance(self, vm_id, action_dict):
+ def action_vminstance(self, vm_id, action_dict, created_items={}):
'''Send and action over a VM instance from VIM
- Returns the vm_id if the action was successfully sent to the VIM'''
+ Returns None or the console dict if the action was successfully sent to the VIM'''
self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
try:
self._reload_connection()
except Exception as e:
raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
- return vm_id
+ return None
except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
self._format_exception(e)
#TODO insert exception vimconn.HTTP_Unauthorized
type: 'virtual', 'PF', 'VF', 'VFnotShared'
vim_id: filled/added by this function
#TODO ip, security groups
- Returns the instance identifier
+ Returns a tuple with the instance 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_vminstance and action_vminstance. Can be used to store created ports, volumes, 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_vminstance input: image='%s' flavor='%s' nics='%s'", image_id, flavor_id, str(net_list))
try:
# return result, error_text
break
- return vminstance_id
+ return vminstance_id, None
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_vminstance(self, vm_id):
+ def delete_vminstance(self, vm_id, created_items=None):
'''Removes a VM instance from VIM, returns the deleted vm_id'''
try:
self._get_my_tenant()
net_dict[net_id] = net
return net_dict
- def action_vminstance(self, vm_id, action_dict):
+ def action_vminstance(self, vm_id, action_dict, created_items={}):
'''Send and action over a VM instance from VIM'''
'''Returns the status'''
try:
self.logger.info("Action over VM instance POST %s", url)
vim_response = requests.post(url, headers = self.headers_req, data=json.dumps(action_dict) )
self._check_http_request_response(vim_response)
- return vm_id
+ return None
except (requests.exceptions.RequestException, js_e.ValidationError) as e:
self._format_request_exception(e)
availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
availability_zone_index is None
- Returns the instance identifier or raises an exception on error
+ Returns a tuple with the instance 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_vminstance and action_vminstance. Can be used to store created ports, volumes, 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.info("Creating new instance for entry {}".format(name))
self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {} disk_list {}".format(
wait_time +=INTERVAL_TIME
if vapp_uuid is not None:
- return vapp_uuid
+ return vapp_uuid, None
else:
raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
return vm_dict
- def delete_vminstance(self, vm__vim_uuid):
+ def delete_vminstance(self, vm__vim_uuid, created_items=None):
"""Method poweroff and remove VM instance from vcloud director network.
Args:
self.logger.debug("ParseError in response from NSX Manager {}".format(Err.message), exc_info=True)
- def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
+ def action_vminstance(self, vm__vim_uuid=None, action_dict=None, created_items={}):
"""Send and action over a VM instance from VIM
Returns the vm_id if the action was successfully sent to the VIM"""
vdc = self.get_vdc_details()
if vdc is None:
- return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
+ raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
vapp_name = self.get_namebyvappid(vdc, vm__vim_uuid)
if vapp_name is None:
reboot_task = the_vapp.reboot()
else:
raise vimconn.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict))
- return vm__vim_uuid
+ return None
except Exception as exp :
self.logger.debug("action_vminstance: Failed with Exception {}".format(exp))
raise vimconn.vimconnException("action_vminstance: Failed with Exception {}".format(exp))
return None
print (" Booting {} image id {} ".format(vm_name, vim_catalog))
- vm_uuid = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
+ vm_uuid, _ = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
if vm_uuid is not None and validate_uuid4(vm_uuid):
print("Image booted and vm uuid {}".format(vm_uuid))
vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vm_uuid, isuuid=True)
return None
print (" Booting {} image id {} ".format(vm_name, vim_catalog))
- vm_uuid = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
+ vm_uuid, _ = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
if vm_uuid is not None and validate_uuid4(vm_uuid):
print("Image booted and vm uuid {}".format(vm_uuid))
vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vm_uuid, isuuid=True)
net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'vpci': vpci, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}]
- self.__class__.instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id, flavor_id=flavor_id, net_list=net_list)
+ self.__class__.instance_id, _ = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id, flavor_id=flavor_id, net_list=net_list)
self.assertEqual(type(self.__class__.instance_id),str)
net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'model': model_name, 'type': 'virtual', 'net_id': self.__class__.network_id}]
- instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
+ instance_id, _ = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
flavor_id=flavor_id,
net_list=net_list)
self.assertEqual(type(instance_id),str)
net_list = [{'use': net_use, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}]
- instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
+ instance_id, _ = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
flavor_id=flavor_id,
net_list=net_list)
self.assertEqual(type(instance_id),str)
net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': _type, 'net_id': self.__class__.network_id}]
- instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
+ instance_id, _ = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
flavor_id=flavor_id,
net_list=net_list)
self.assertEqual(type(instance_id),str)
net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}]
- instance_id = test_config["vim_conn"].new_vminstance(name='Cloud_vm', image_id=self.__class__.image_id,
+ instance_id, _ = test_config["vim_conn"].new_vminstance(name='Cloud_vm', image_id=self.__class__.image_id,
flavor_id=flavor_id,
net_list=net_list,
cloud_config=cloud_data)
net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}]
- instance_id = test_config["vim_conn"].new_vminstance(name='VM_test1', image_id=self.__class__.image_id,
+ instance_id, _ = test_config["vim_conn"].new_vminstance(name='VM_test1', image_id=self.__class__.image_id,
flavor_id=flavor_id,
net_list=net_list,
disk_list=device_data)