diff --git a/QualiConf.ini b/QualiConf.ini index f8b77a0..c9965e2 100644 --- a/QualiConf.ini +++ b/QualiConf.ini @@ -7,4 +7,4 @@ Path=drivers [OpenStack_Python] Name=cloudshell-cp-openstack -Path=package \ No newline at end of file +Path=package diff --git a/package/cloudshell/cp/openstack/command/operations/connectivity_operation.py b/package/cloudshell/cp/openstack/command/operations/connectivity_operation.py index 62b51fc..8aab974 100644 --- a/package/cloudshell/cp/openstack/command/operations/connectivity_operation.py +++ b/package/cloudshell/cp/openstack/command/operations/connectivity_operation.py @@ -72,27 +72,22 @@ def apply_connectivity(self, openstack_session, cp_resource_model, conn_request, # Add more description # TODO : implement remove actions dict for action in actions: - # FIXME: Move this "ifs into a separate function - if action.type == 'setVlan': - curr_dict = set_vlan_actions_dict - # FIXME: Check whether this is 'removeVlan' - else: - curr_dict = remove_vlan_actions_dict - action_vlanid = action.connectionParams.vlanId - actionid = action.actionId + curr_dict = self._get_curr_actions_dict(action_type=action.type, + set_vlan_action_dict=set_vlan_actions_dict, + remove_vlan_action_dict=remove_vlan_actions_dict) + if curr_dict is None: + raise ValueError("Unknown action: Action not one of 'setVlan' or 'removeVlan'.") + actionid = action.actionId deployed_app_res_name = action.actionTarget.fullName + action_resource_info = self._get_action_resource_info(deployed_app_res_name, actionid, action) - for cust_attr in action.customActionAttributes : - if cust_attr.attributeName == 'VM_UUID': - vm_uuid = cust_attr.attributeValue - # FIXME : changed this to object for later readability - resource_info = (deployed_app_res_name, vm_uuid, actionid) + action_vlanid = action.connectionParams.vlanId if action_vlanid in curr_dict.keys(): - curr_dict[action_vlanid].append(resource_info) + curr_dict[action_vlanid].append(action_resource_info) else: - curr_dict[action_vlanid] = [resource_info] + curr_dict[action_vlanid] = [action_resource_info] results = [] if set_vlan_actions_dict: @@ -100,13 +95,12 @@ def apply_connectivity(self, openstack_session, cp_resource_model, conn_request, cp_resource_model=cp_resource_model, vlan_actions=set_vlan_actions_dict, logger=logger) - results += result if remove_vlan_actions_dict: result = self._do_remove_vlan_actions(openstack_session=openstack_session, cp_resource_model=cp_resource_model, - vlan_actions=set_vlan_actions_dict, + vlan_actions=remove_vlan_actions_dict, logger=logger) results += result @@ -132,21 +126,20 @@ def _do_set_vlan_actions(self, openstack_session, cp_resource_model, vlan_action results = [] for k, values in vlan_actions.iteritems(): - # FIXME: results getting overwritten - # FIXME: update the nethod name - net = self.network_service.create_network_with_vlanid(openstack_session=openstack_session, + net = self.network_service.create_or_get_network_with_vlanid(openstack_session=openstack_session, vlanid=int(k), logger=logger) if not net: - # FIXME : create error for the action - results = self._set_fail_results(values=values, - action_type='setVlan', - failure_text="Failed to Create Network with VLAN ID {0}".format(k)) + fail_results = self._set_fail_results(values=values, + action_type='setVlan', + failure_text="Failed to Create Network with VLAN ID {0}".format(k)) + results += fail_results else: net_id = net['id'] subnet = net['subnets'] if not subnet: + # FIXME: serialize this # FIXME: Rename this function to create_and_attach subnet = self.network_service.attach_subnet_to_net(openstack_session=openstack_session, cp_resource_model=cp_resource_model, @@ -155,37 +148,19 @@ def _do_set_vlan_actions(self, openstack_session, cp_resource_model, vlan_action else: subnet = subnet[0] if not subnet: - # FIXME: create error for action - results = self._set_fail_results(values=values, - action_type='setVlan', - failure_text="Failed to attach Subnet to Network {0}".format(net_id)) + fail_results = self._set_fail_results(values=values, + action_type='setVlan', + failure_text="Failed to attach Subnet to Network {0}".format(net_id)) + results += fail_results else: attach_results = [] - # FIXME: let's move this for val in values: - - instance_id = val[1] - # returns MAC Address of the attached port - which is reflected in updated Port - result = self.instance_service.attach_nic_to_net(openstack_session, instance_id, net_id, logger) - if not result: - action_result = ConnectivityActionResultModel() - action_result.success = False - action_result.actionId = val[2] - action_result.errorMessage = \ - "Failed to Attach NIC on Network {0} to Instance {1}".format(net_id, val[0]) - action_result.infoMessage = None - action_result.updatedInterface = None - else: - action_result = ConnectivityActionResultModel() - action_result.success = "True" - action_result.actionId = val[2] - action_result.errorMessage = "" - action_result.infoMessage = \ - "Successfully Attached NIC on Network {0} to Instance {1}".format(net_id, val[0]) - action_result.updatedInterface = result - action_result.type = 'setVlan' + action_result = self._attach_nic_to_instance_action_result(openstack_session=openstack_session, + action_resource_info=val, + net_id=net_id, + logger=logger) attach_results.append(action_result) - results = attach_results + results += attach_results return results def _do_remove_vlan_actions(self, openstack_session, cp_resource_model, vlan_actions, logger): @@ -197,15 +172,42 @@ def _do_remove_vlan_actions(self, openstack_session, cp_resource_model, vlan_act :param LoggingSessionContext logger: :return: """ - logger.info("_do_remove_vlan_actions called.") - return [] + + results = [] + + for k, values in vlan_actions.iteritems(): + net = self.network_service.get_network_with_vlanid(openstack_session=openstack_session, + vlanid=int(k), logger=logger) + if not net: + fail_results = self._set_fail_results(values=values, + action_type='removeVlan', + failure_text="Failed to get Network with VLAN ID {0}".format(k)) + results += fail_results + else: + net_id = net['id'] + + remove_results = [] + for val in values: + action_result = self._detach_nic_from_instance_action_result(openstack_session=openstack_session, + action_resource_info=val, + net_id=net_id, + logger=logger) + remove_results.append(action_result) + + results += remove_results + + # We should just remove subnet(s) and net from Openstack now (If any exception that gets logged) + self.network_service.remove_subnet_and_net(openstack_session=openstack_session, + network=net, logger=logger) + + return results def _set_fail_results(self, values, failure_text, action_type, logger=None): """ For all connections (obtained from values), set the failed results text, useful in generating output :param tuple values: :param str failure_text: - :param str action_type + :param str action_type: :param logger: :return ConnectivityActionResultModel List: """ @@ -219,4 +221,139 @@ def _set_fail_results(self, values, failure_text, action_type, logger=None): action_result.type = action_type action_result.updatedInterface = None results.append(action_result) - return results \ No newline at end of file + return results + + def _get_curr_actions_dict(self, action_type, set_vlan_action_dict=None, remove_vlan_action_dict=None): + """ + + :param str action_type: + :param dict set_vlan_action_dict: + :param dict remove_vlan_action_dict: + :return: + """ + if action_type == 'setVlan' and set_vlan_action_dict is not None: + return set_vlan_action_dict + + elif action_type == 'removeVlan' and remove_vlan_action_dict is not None: + return remove_vlan_action_dict + + return None + + def _get_action_resource_info(self, deployed_app_resource_name, actionid, action): + """ + + :param str deployed_app_resource_name: + :param str actionid: + :param action: action obtained from JSON + :return ActionResourceInfo: + + """ + + custom_action_attributes = action.customActionAttributes + vm_uuid = None + + for cust_attr in custom_action_attributes: + if cust_attr.attributeName == 'VM_UUID': + vm_uuid = cust_attr.attributeValue + + connector_attributes = action.connectorAttributes + + # in removeVlan + iface_ip = None + iface_port_id = None + iface_mac = None + if connector_attributes: + for conn_attribute in connector_attributes: + if conn_attribute.attributeName == 'Interface': + iface_ip, iface_port_id, iface_mac = conn_attribute.attributeValue.split("/") + + return ActionResourceInfo(deployed_app_resource_name=deployed_app_resource_name, + actionid=actionid, + vm_uuid=vm_uuid, + interface_ip=iface_ip, + interface_port_id=iface_port_id, + interface_mac=iface_mac) + + def _attach_nic_to_instance_action_result(self, openstack_session, action_resource_info, net_id, logger): + """ + + :param keystoneauth1.session.Session openstack_session: + :param ActionResourceInfo action_resource_info: + :param str net_id: + :param LoggingSessionContext logger: + :return ConnectivityActionResultModel: + """ + action_result = ConnectivityActionResultModel() + + instance_id = action_resource_info.vm_uuid + result = self.instance_service.attach_nic_to_net(openstack_session, instance_id, net_id, logger) + if not result: + action_result.success = "False" + action_result.actionId = action_resource_info.actionid + action_result.errorMessage = "Failed to Attach NIC on Network {0} to Instance {1}".format( + net_id, + action_resource_info.deployed_app_resource_name) + action_result.infoMessage = "" + action_result.updatedInterface = "" + action_result.type = 'setVlan' + else: + action_result.success = "True" + action_result.actionId = action_resource_info.actionid + action_result.errorMessage = "" + action_result.infoMessage = "Successfully Attached NIC on Network {0} to Instance {1}".format( + net_id, + action_resource_info.deployed_app_resource_name) + action_result.updatedInterface = result + action_result.type = 'setVlan' + + return action_result + + + def _detach_nic_from_instance_action_result(self, openstack_session, action_resource_info, net_id, logger): + """ + + :param keystoneauth1.session.Session openstack_session: + :param ActionResourceInfo action_resource_info: + :param str net_id: + :param LoggingSessionContext logger: + :return ConnectivityActionResultModel: + """ + action_result = ConnectivityActionResultModel() + + port_id = action_resource_info.interface_port_id + vm_uuid = action_resource_info.vm_uuid + + result = self.instance_service.detach_nic_from_instance(openstack_session=openstack_session, + instance_id=vm_uuid, port_id=port_id, logger=logger) + if not result: + action_result.success = "False" + action_result.actionId = action_resource_info.actionid + action_result.errorMessage = "Failed to Detach NIC {0} from Instance {1}".format( + port_id, + action_resource_info.deployed_app_resource_name) + action_result.infoMessage = "" + action_result.updatedInterface = "" + action_result.type = 'removeVlan' + else: + action_result.success = "True" + action_result.actionId = action_resource_info.actionid + action_result.errorMessage = "" + action_result.infoMessage = "Successfully detached NIC {0} from Instance {1}".format( + port_id, + action_resource_info.deployed_app_resource_name) + action_result.updatedInterface = result + action_result.type = 'removeVlan' + + return action_result + + +# FIXME : Move this out to a different place +class ActionResourceInfo: + + def __init__(self, deployed_app_resource_name, actionid, vm_uuid, interface_ip, interface_port_id, interface_mac): + self.deployed_app_resource_name = deployed_app_resource_name + self.vm_uuid = vm_uuid + self.actionid = actionid + self.iface_ip = interface_ip + self.interface_port_id = interface_port_id + self.interface_mac = interface_mac diff --git a/package/cloudshell/cp/openstack/domain/services/neutron/neutron_network_service.py b/package/cloudshell/cp/openstack/domain/services/neutron/neutron_network_service.py index 9ed7fdd..eb8d793 100644 --- a/package/cloudshell/cp/openstack/domain/services/neutron/neutron_network_service.py +++ b/package/cloudshell/cp/openstack/domain/services/neutron/neutron_network_service.py @@ -1,6 +1,8 @@ from neutronclient.v2_0 import client as neutron_client from neutronclient.common.exceptions import Conflict as NetCreateConflict +import traceback + class NeutronNetworkService(object): """ A wrapper class around Neutron API @@ -11,7 +13,7 @@ def __init__(self): self.cidr_subnet_num = 0 self.allocated_subnets = [] - def create_network_with_vlanid(self, openstack_session, vlanid, logger): + def create_or_get_network_with_vlanid(self, openstack_session, vlanid, logger): """ :param keystoneauth1.session.Session openstack_session: @@ -28,8 +30,6 @@ def create_network_with_vlanid(self, openstack_session, vlanid, logger): 'provider:segmentation_id': vlanid, 'name': nw_name, 'admin_state_up': True} - - # FIXME : If an exception is raised - we just raise it all the way back? For now yes try: new_net = client.create_network({'network': create_nw_json}) new_net = new_net['network'] @@ -37,11 +37,28 @@ def create_network_with_vlanid(self, openstack_session, vlanid, logger): new_net = client.list_networks(**{'provider:segmentation_id':vlanid}) new_net = new_net['networks'][0] except Exception as e: - logger.error("Exception {0} Occurred while creating network".format(e)) + logger.error(traceback.format_exc()) return None return new_net + def get_network_with_vlanid(self, openstack_session, vlanid, logger): + """ + + :param keystoneauth1.session.Session openstack_session: + :param int vlanid: + :param LoggingSessionContext logger: + :return: + """ + + client = neutron_client.Client(session=openstack_session) + + net = client.list_networks(**{'provider:segmentation_id': vlanid}) + if net['networks']: + return net['networks'][0] + else: + return None + def attach_subnet_to_net(self, openstack_session, cp_resource_model, net_id, logger): """ Atttach a subnet to the network with given net_id. @@ -67,11 +84,31 @@ def attach_subnet_to_net(self, openstack_session, cp_resource_model, net_id, log new_subnet = client.create_subnet({'subnet':create_subnet_json}) new_subnet = new_subnet['subnet'] except Exception as e: - logger.error("Exception {0} Occurred while creating network".format(e)) + logger.error(traceback.format_exc()) return None return new_subnet + def remove_subnet_and_net(self, openstack_session, network, logger): + """ + + :param keystoneauth1.session.Session openstack_session: + :param dict network: + :param LoggingSessionContext logger: + :return: + """ + + client = neutron_client.Client(session=openstack_session) + + try: + for subnet in network['subnets']: + client.delete_subnet(subnet) + + client.delete_network(network['id']) + + except Exception as e: + logger.error(traceback.format_exc()) + def _get_unused_cidr(self, cp_resvd_cidrs, logger): """ Gets unused CIDR that excludes the reserved CIDRs diff --git a/package/cloudshell/cp/openstack/domain/services/nova/nova_instance_service.py b/package/cloudshell/cp/openstack/domain/services/nova/nova_instance_service.py index 2858bba..a50aef2 100644 --- a/package/cloudshell/cp/openstack/domain/services/nova/nova_instance_service.py +++ b/package/cloudshell/cp/openstack/domain/services/nova/nova_instance_service.py @@ -5,6 +5,8 @@ from novaclient import client as novaclient from cloudshell.cp.openstack.common.driver_helper import CloudshellDriverHelper +import traceback + class NovaInstanceService(object): """Implements management of Compute Instances.""" API_VERSION = '2.0' @@ -201,8 +203,40 @@ def attach_nic_to_net(self, openstack_session, instance_id, net_id, logger): try: res = instance.interface_attach(net_id=net_id, port_id=None, fixed_ip=None) iface_mac = res.to_dict().get('mac_addr') - return iface_mac + iface_portid = res.to_dict().get('port_id') + iface_ip = res.to_dict().get('fixed_ips')[0]['ip_address'] + result = "/".join([iface_ip, iface_portid, iface_mac]) + return result except Exception as e: logger.info("Exception: {0} during interface attach".format(e)) - return None \ No newline at end of file + return None + + def detach_nic_from_instance(self, openstack_session, instance_id, port_id, logger): + """ + + :param keystoneauth1.session.Session openstack_session: + :param str instance_id: + :param str port_id: + :param LoggingSesssionContext logger: + :return bool: Success or Failure + """ + + + logger.info("Detaching port {0} from Instance {1}".format(port_id, instance_id)) + + instance = self.get_instance_from_instance_id(openstack_session=openstack_session, + instance_id=instance_id, + logger=logger) + logger.info("Returned instance {0}".format(instance)) + if instance is None: + return False + + try: + instance.interface_detach(port_id) + + return True + + except Exception as e: + logger.error(traceback.format_exc()) + return False \ No newline at end of file