def test_match_node_metadata_to_vcenter_one_node_added_by_ip(self): vsphere = VsphereVcenter(vcenter_host="fake_vcenter", vcenter_user="******", vcenter_password="******") cluster_metadata = proto_patch_encryption_support(CurieSettings.Cluster)() cluster_metadata.cluster_name = "Fake Cluster" cluster_metadata.cluster_hypervisor_info.esx_info.SetInParent() for index in range(3): # Add first 3 nodes by hostname to curie metadata curr_node = cluster_metadata.cluster_nodes.add() curr_node.id = "fake_host_%d" % index curr_node = cluster_metadata.cluster_nodes.add() curr_node.id = '10.60.5.63' # Add last node by ip vcenter_info = cluster_metadata.cluster_management_server_info.vcenter_info vcenter_info.vcenter_host = "fake_vmm_server_address" vcenter_info.vcenter_user = "******" vcenter_info.vcenter_password = "******" vcenter_info.vcenter_datacenter_name = "fake_datacenter" vcenter_info.vcenter_cluster_name = "fake_cluster" vcenter_info.vcenter_datastore_name = "fake_share_name" vcenter_info.vcenter_network_name = "fake_network" self.assertEqual(4, len(cluster_metadata.cluster_nodes)) vsphere.match_node_metadata_to_vcenter(self.m_vim_cluster, cluster_metadata) self.assertEqual(4, len(cluster_metadata.cluster_nodes)) for index, cluster_node in enumerate(cluster_metadata.cluster_nodes): self.assertEqual("fake_host_%d" % index, cluster_node.id)
def test_fill_cluster_metadata_do_not_include_reporting_fields(self): vsphere = VsphereVcenter(vcenter_host="fake_vcenter", vcenter_user="******", vcenter_password="******") cluster_metadata = proto_patch_encryption_support(CurieSettings.Cluster)() cluster_metadata.cluster_name = "Fake Cluster" cluster_metadata.cluster_hypervisor_info.esx_info.SetInParent() for index in range(4): curr_node = cluster_metadata.cluster_nodes.add() curr_node.id = "fake_host_%d" % index vcenter_info = cluster_metadata.cluster_management_server_info.vcenter_info vcenter_info.vcenter_host = "fake_vmm_server_address" vcenter_info.vcenter_user = "******" vcenter_info.vcenter_password = "******" vcenter_info.vcenter_datacenter_name = "fake_datacenter" vcenter_info.vcenter_cluster_name = "fake_cluster" vcenter_info.vcenter_datastore_name = "fake_share_name" vcenter_info.vcenter_network_name = "fake_network" for index, cluster_node in enumerate(cluster_metadata.cluster_nodes): self.assertEqual("fake_host_%d" % index, cluster_node.id) self.assertEqual(False, cluster_node.HasField("node_hardware")) vsphere.fill_cluster_metadata(self.m_vim_cluster, cluster_metadata, False) for index, cluster_node in enumerate(cluster_metadata.cluster_nodes): self.assertEqual("fake_host_%d" % index, cluster_node.id) self.assertEqual(False, cluster_node.HasField("node_hardware"))
def test_match_node_metadata_to_vcenter_node_does_not_match(self): vsphere = VsphereVcenter(vcenter_host="fake_vcenter", vcenter_user="******", vcenter_password="******") self.m_vim_cluster.name = "Fake Cluster" cluster_metadata = proto_patch_encryption_support(CurieSettings.Cluster)() cluster_metadata.cluster_name = "Fake Cluster" cluster_metadata.cluster_hypervisor_info.esx_info.SetInParent() for index in range(3): # Add first 3 nodes by hostname curr_node = cluster_metadata.cluster_nodes.add() curr_node.id = "fake_host_%d" % index curr_node = cluster_metadata.cluster_nodes.add() curr_node.id = '10.60.5.222' # Add last node by ip vcenter_info = cluster_metadata.cluster_management_server_info.vcenter_info vcenter_info.vcenter_host = "fake_vmm_server_address" vcenter_info.vcenter_user = "******" vcenter_info.vcenter_password = "******" vcenter_info.vcenter_datacenter_name = "fake_datacenter" vcenter_info.vcenter_cluster_name = "fake_cluster" vcenter_info.vcenter_datastore_name = "fake_share_name" vcenter_info.vcenter_network_name = "fake_network" self.assertEqual(4, len(cluster_metadata.cluster_nodes)) with self.assertRaises(CurieTestException) as ar: vsphere.match_node_metadata_to_vcenter(self.m_vim_cluster, cluster_metadata) self.assertEqual(4, len(cluster_metadata.cluster_nodes)) self.assertEqual("Node with ID '10.60.5.222' is in the Curie cluster metadata, but not " "found in vSphere cluster 'Fake Cluster'.", str(ar.exception.cause))
def test_match_node_metadata_to_vcenter_multiple_node_matches(self): vsphere = VsphereVcenter(vcenter_host="fake_vcenter", vcenter_user="******", vcenter_password="******") self.m_vim_cluster.name = "Fake Cluster" cluster_metadata = proto_patch_encryption_support(CurieSettings.Cluster)() cluster_metadata.cluster_name = "Fake Cluster" cluster_metadata.cluster_hypervisor_info.esx_info.SetInParent() for index in range(3): # Add first 3 nodes by hostname curr_node = cluster_metadata.cluster_nodes.add() curr_node.id = "fake_host_%d" % index curr_node = cluster_metadata.cluster_nodes.add() curr_node.id = '10.60.5.61' # Add last node by ip self.m_vim_cluster.host[0].config.network.vnic[0].spec.ip.ipAddress = '10.60.5.61' # Add repeated address vcenter_info = cluster_metadata.cluster_management_server_info.vcenter_info vcenter_info.vcenter_host = "fake_vmm_server_address" vcenter_info.vcenter_user = "******" vcenter_info.vcenter_password = "******" vcenter_info.vcenter_datacenter_name = "fake_datacenter" vcenter_info.vcenter_cluster_name = "fake_cluster" vcenter_info.vcenter_datastore_name = "fake_share_name" vcenter_info.vcenter_network_name = "fake_network" self.assertEqual(4, len(cluster_metadata.cluster_nodes)) with self.assertRaises(CurieTestException) as ar: vsphere.match_node_metadata_to_vcenter(self.m_vim_cluster, cluster_metadata) self.assertEqual(4, len(cluster_metadata.cluster_nodes)) self.assertEqual("More than one node in the vSphere cluster " "'Fake Cluster' matches node ID '10.60.5.61'. The " "matching nodes are: fake_host_0, fake_host_1.", str(ar.exception.cause))
def test_lookup_datastore_does_not_exist(self, m_SmartConnectNoSSL): vsphere = VsphereVcenter(vcenter_host="fake_vcenter", vcenter_user="******", vcenter_password="******") with vsphere: datastore = vsphere.lookup_datastore(self.m_vim_cluster, "this_is_a_non_existent_datastore") self.assertIsNone(datastore)
def test_lookup_datastore_mounted_on_all_hosts(self, m_SmartConnectNoSSL): vsphere = VsphereVcenter(vcenter_host="fake_vcenter", vcenter_user="******", vcenter_password="******") with vsphere: datastore = vsphere.lookup_datastore(self.m_vim_cluster, "fake_datastore") self.assertIsInstance(datastore, vim.Datastore) self.assertEqual(datastore.name, "fake_datastore")
def test_lookup_datastore_host_not_found(self, m_SmartConnectNoSSL): vsphere = VsphereVcenter(vcenter_host="fake_vcenter", vcenter_user="******", vcenter_password="******") with vsphere: with self.assertRaises(CurieTestException) as ar: vsphere.lookup_datastore(self.m_vim_cluster, "fake_datastore", "this_is_a_non_existent_host") self.assertEqual("Host 'this_is_a_non_existent_host' not found", str(ar.exception))
def test_lookup_datastore_not_on_all_hosts(self, m_SmartConnectNoSSL): m_some_other_datastore = mock.Mock(spec=vim.Datastore) m_some_other_datastore.name = "some_other_datastore" self.m_vim_cluster.host[0].datastore = [m_some_other_datastore] vsphere = VsphereVcenter(vcenter_host="fake_vcenter", vcenter_user="******", vcenter_password="******") with vsphere: datastore = vsphere.lookup_datastore(self.m_vim_cluster, "fake_datastore") self.assertIsNone(datastore)
def discover_nodes_vcenter(arg, ret): """ See 'DiscoveryUtil.handle_nodes_discovery_v2' for info. """ conn_params = arg.mgmt_server.conn_params with VsphereVcenter.from_proto(conn_params) as vcenter: vim_dc = vcenter.lookup_datacenter(arg.cluster_collection.name) for target_cluster in arg.cluster_collection.cluster_vec: vim_cluster = vcenter.lookup_cluster(vim_dc, target_cluster.name) node_coll = ret.node_collection_vec.add() node_coll.cluster_id = vim_cluster.name for vim_node in vim_cluster.host: node = node_coll.node_vec.add() # According to pyVim documentation, the hostname is guaranteed to be # the IP or DNS name for the host. node.id = vim_node.name node.name = vim_node.name node.hypervisor.type = node.hypervisor.kEsx node.hypervisor.version = vcenter.get_esx_versions_for_vim_host( vim_node)[0] # Have already validated that either both fields are set or not. if (arg.oob_info.conn_params.username and arg.oob_info.type != OobInterfaceType.kNone): node.oob_info.CopyFrom(arg.oob_info) node.oob_info.conn_params.address, node.oob_info.vendor = \ DiscoveryUtil.lookup_node_bmc_info(vim_node)
def __vim_vm_to_curie_vm(self, vim_vm): "Returns an object of the appropriate subclass of Vm for 'vim_vm'." # On a vSphere cluster, the VM name should be unique on the cluster where # the VM resides, so we can just use the VM name as the VM ID. vm_id = vim_vm.name curie_guest_os_type_value = None vim_vm_config = get_optional_vim_attr(vim_vm, "config") if vim_vm_config is not None: for config_option in vim_vm_config.extraConfig: if config_option.key == CURIE_GUEST_OS_TYPE_KEY: curie_guest_os_type_value = config_option.value break vm_ip = VsphereVcenter.get_vim_vm_ip_address(vim_vm) vim_host = get_optional_vim_attr(vim_vm.runtime, "host") if vim_host is not None: node_id = vim_host.name else: node_id = None vm_params = VmParams(self, vm_id) vm_params.vm_name = vim_vm.name vm_params.vm_ip = vm_ip vm_params.node_id = node_id if curie_guest_os_type_value is None: # We use the generic VsphereVm class for all VMs that don't have the # curie guest OS type option since non-curie VMs won't have the proper # configuration to enable, for example, remote file transfers to/from the # VM and remote execution of commands in the VM. vm_params.is_cvm = self.__vim_vm_is_nutanix_cvm(vim_vm) return VsphereVm(vm_params) else: CHECK_EQ(curie_guest_os_type_value, "unix") return VsphereUnixVm(vm_params)
def test_find_datastore_paths_flat(self, m_WaitForTask, m_SmartConnectNoSSL): m_datastore = mock.Mock(spec=vim.Datastore) m_task = mock.Mock() m_search_result = mock.Mock() m_search_result.folderPath = "[rtptest1]" m_task.info.result = m_search_result m_fileinfo_1 = mock.Mock() m_fileinfo_1.path = "__curie_goldimage_1416468224265211271_ubuntu1604_DSS" m_fileinfo_2 = mock.Mock() m_fileinfo_2.path = "__curie_goldimage_1416468224265211271_ubuntu1604_OLTP" m_search_result.file = [m_fileinfo_1, m_fileinfo_2] m_datastore.browser.Search.return_value = m_task vsphere = VsphereVcenter(vcenter_host="fake_vcenter", vcenter_user="******", vcenter_password="******") with vsphere: paths = vsphere.find_datastore_paths("__curie_goldimage*", m_datastore) self.assertEqual([ "[rtptest1]__curie_goldimage_1416468224265211271_ubuntu1604_DSS", "[rtptest1]__curie_goldimage_1416468224265211271_ubuntu1604_OLTP"], paths)
def is_ready(self): """See 'NodeUtil.is_ready' documentation for further details. Confirms node is ready by requesting node's health info from Vsphere corresponding to the node. Raises: CurieTestException: If the VsanNodeUtil's node is not found (passed through from __get_vim_host). """ with VsphereVcenter( self.__vcenter_info.vcenter_host, self.__vcenter_info.decrypt_field("vcenter_user"), self.__vcenter_info.decrypt_field( "vcenter_password")) as vcenter: vim_host = self.__get_vim_host(vcenter) return self.__vim_host_is_ready(vim_host)
def _update_cluster_version_info_vcenter(cluster_pb): """ See 'DiscoveryUtil.update_cluster_version_info' for info. """ mgmt_info = cluster_pb.cluster_management_server_info.vcenter_info hyp_info = cluster_pb.cluster_hypervisor_info.esx_info with VsphereVcenter.from_proto(mgmt_info) as vcenter: vim_dc = vcenter.lookup_datacenter(mgmt_info.vcenter_datacenter_name) vim_cluster = vcenter.lookup_cluster(vim_dc, mgmt_info.vcenter_cluster_name) if vim_cluster is None: raise CurieException(CurieError.kInvalidParameter, "Cluster not found in specified vCenter") esx_version_pairs = vcenter.get_esx_versions(vim_cluster) hyp_info.version.extend(pair[0] for pair in esx_version_pairs) hyp_info.build.extend(pair[1] for pair in esx_version_pairs) mgmt_info.vcenter_version, mgmt_info.vcenter_build = \ vcenter.get_vcenter_version_info() if cluster_pb.cluster_software_info.HasField("nutanix_info"): cvms = [vim_vm for vim_vm in vcenter.lookup_vms(vim_cluster) if vcenter.vim_vm_is_nutanix_cvm(vim_vm)] if not cvms: raise CurieException( CurieError.kInvalidParameter, "Unable to locate any CVMs on cluster. Is this a Nutanix cluster?") for cvm in cvms: ip = get_optional_vim_attr(cvm.guest, "ipAddress") if ip and CurieUtil.is_ipv4_address(ip): break else: raise CurieException( CurieError.kInvalidParameter, "Unable to locate any CVMs with IPv4 addresses on cluster") software_info = cluster_pb.cluster_software_info.nutanix_info cli = NutanixRestApiClient( ip, software_info.decrypt_field("prism_user"), software_info.decrypt_field("prism_password")) DiscoveryUtil._update_cluster_version_info_nos(cli, cluster_pb)
def update_metadata(self, include_reporting_fields): with self._open_vcenter_connection() as vcenter: vim_cluster = self._lookup_vim_cluster(vcenter) vcenter.fill_cluster_metadata(vim_cluster, self._metadata, include_reporting_fields) vcenter.match_node_metadata_to_vcenter(vim_cluster, self._metadata) if (self._metadata.cluster_software_info.HasField("vsan_info") or self._metadata.cluster_software_info.HasField( "generic_info")): # TODO (jklein): Clean-up/properly abstract out these checks. mgmt_info = self._metadata.cluster_management_server_info if mgmt_info.HasField("vcenter_info"): ds_name = mgmt_info.vcenter_info.vcenter_datastore_name else: raise CurieTestException("Invalid metadata") vim_ds = vcenter.lookup_datastore(vim_cluster, ds_name) if vim_ds.summary.type == "vsan": if self._metadata.cluster_software_info.HasField( "generic_info"): self._metadata.cluster_software_info.ClearField( "generic_info") self._metadata.cluster_software_info.vsan_info.SetInParent( ) else: if self._metadata.cluster_software_info.HasField( "vsan_info"): raise CurieTestException( "Target is not configured for VSAN") if include_reporting_fields: version_pairs = VsphereVcenter.get_esx_versions( vim_cluster, self._metadata.cluster_nodes) esx_versions = [pair[0] for pair in version_pairs] esx_builds = [pair[1] for pair in version_pairs] self._metadata.cluster_hypervisor_info.esx_info.version.extend( esx_versions) self._metadata.cluster_hypervisor_info.esx_info.build.extend( esx_builds) cluster_software_info = self._metadata.cluster_software_info if cluster_software_info.HasField("nutanix_info"): client = NutanixRestApiClient.from_proto( cluster_software_info.nutanix_info) nutanix_metadata = client.get_nutanix_metadata() if nutanix_metadata.version is not None: cluster_software_info.nutanix_info.version = \ nutanix_metadata.version if nutanix_metadata.cluster_uuid is not None: cluster_software_info.nutanix_info.cluster_uuid = \ nutanix_metadata.cluster_uuid if nutanix_metadata.cluster_incarnation_id is not None: cluster_software_info.nutanix_info.cluster_incarnation_id = \ nutanix_metadata.cluster_incarnation_id elif cluster_software_info.HasField("vsan_info"): pass elif cluster_software_info.HasField("generic_info"): pass else: raise CurieException( CurieError.kInternalError, "Unsupported software on vSphere cluster, %s" % self._metadata) self._node_id_metadata_map = dict([ (node.id, node) for node in self._metadata.cluster_nodes ])
def _open_vcenter_connection(self): return VsphereVcenter( self._vcenter_info.vcenter_host, self._vcenter_info.decrypt_field("vcenter_user"), self._vcenter_info.decrypt_field("vcenter_password"))
def update_cluster_virtual_ip(cluster_pb): """ Updates 'prism_host' to correspond to the cluster virtual IP. The 'prism_host' field is set for management and clustering software as appropriate for the target hypervisor. Returns: True if 'prism_host' was updated, else False. Raises: CurieException<kInvalidParameter>: 'cluster_pb' is a Nutanix cluster but does not have a Virtual IP. """ if not cluster_pb.cluster_software_info.HasField("nutanix_info"): return False prism_proto = None ntnx_proto = cluster_pb.cluster_software_info.nutanix_info prism_user = ntnx_proto.decrypt_field("prism_user") prism_password = ntnx_proto.decrypt_field("prism_password") c_uuid = ntnx_proto.cluster_uuid if cluster_pb.cluster_management_server_info.HasField("prism_info"): prism_proto = cluster_pb.cluster_management_server_info.prism_info client = NutanixRestApiClient.from_proto(prism_proto, timeout_secs=10) cluster_json = client.clusters_get(cluster_id=c_uuid) else: cvm_addresses = [] if cluster_pb.cluster_management_server_info.HasField("vmm_info"): vmm_info = cluster_pb.cluster_management_server_info.vmm_info vmm_client = VmmClient(address=vmm_info.vmm_server, username=vmm_info.vmm_user, password=vmm_info.vmm_password) with vmm_client: vms = vmm_client.get_vms(cluster_name=vmm_info.vmm_cluster_name) for vm in vms: if VmmClient.is_nutanix_cvm(vm): if VmmClient.is_powered_on(vm): log.debug("Found CVM '%s' with IPs: %s", vm["name"], vm["ips"]) cvm_addresses.extend(vm["ips"]) else: log.debug("Skipping CVM '%s' because it is not powered on.", vm["name"]) else: node_ids = [node.id for node in cluster_pb.cluster_nodes] # NB: We currently have an asymmetrical input for Prism credentials # depending on whether they're considered as management software or # clustering software. In the latter case, which is when 'nutanix_info' # is set but not 'prism_info', the user is not asked for a Prism host. # In this case, we discover CVMs via vCenter, attempt to connect to Prism # on each in sequence until successful, and then query the virtual IP. mgmt_info = cluster_pb.cluster_management_server_info.vcenter_info with VsphereVcenter.from_proto(mgmt_info) as vcenter: vim_dc = vcenter.lookup_datacenter(mgmt_info.vcenter_datacenter_name) vim_cluster = vcenter.lookup_cluster(vim_dc, mgmt_info.vcenter_cluster_name) for vim_cvm in (vm for vm in vcenter.lookup_vms(vim_cluster) if vcenter.vim_vm_is_nutanix_cvm(vm)): vim_host = get_optional_vim_attr(vim_cvm.runtime, "host") if vim_host: if vim_host.name in node_ids: cvm_address = vcenter.get_vim_vm_ip_address(vim_cvm) if cvm_address: log.debug("Found CVM '%s' with address '%s'" % (vim_cvm.name, cvm_address)) cvm_addresses.append(cvm_address) else: log.debug("Skipping CVM '%s'; Host '%s' is not in the " "metadata" % (vim_cvm.name, vim_host.name)) # We run Nutanix API only against powered on CVMs. if not cvm_addresses: raise CurieTestException( cause="No Nutanix CVMs found.", impact="The cluster virtual IP address can not be discovered.", corrective_action="Please verify that the cluster contains Nutanix " "CVMs, and that they are powered on.", ) for cvm_address in cvm_addresses: client = NutanixRestApiClient(cvm_address, prism_user, prism_password) try: cluster_json = client.clusters_get(cluster_id=c_uuid, max_retries=3) except BaseException: log.warning("Unable to query CVM with IP '%s'", cvm_address, exc_info=True) else: break else: raise CurieTestException( cause="Failed to query Prism on any Nutanix CVM.", impact="The cluster virtual IP address can not be discovered.", corrective_action="Please verify that the Nutanix CVMs on the " "cluster are powered on, and that the network " "connectivity to the CVMs is correct.", ) if "clusterExternalIPAddress" in cluster_json: cluster_name = cluster_json.get("name") cluster_vip = cluster_json["clusterExternalIPAddress"] elif "entities" in cluster_json: cluster_data = cluster_json["entities"][0] cluster_name = cluster_data.get("name") cluster_vip = cluster_data["clusterExternalIPAddress"] else: raise CurieException( CurieError.kInvalidParameter, "Unrecognized response from NutanixRestApiClient.clusters_get") if not cluster_vip: raise CurieException( CurieError.kInvalidParameter, "Cluster '%s' does not appear to be configured with a virtual IP " "(received '%s')" % (cluster_name, cluster_vip)) else: log.debug("Identified Nutanix cluster virtual IP address: '%s'", cluster_vip) ntnx_proto.prism_host = cluster_vip if prism_proto: prism_proto.prism_host = cluster_vip return True
def __vim_vm_is_nutanix_cvm(self, vim_vm): "Returns True if the VM 'vim_vm' corresponds to a Nutanix CVM." if not self._metadata.cluster_software_info.HasField("nutanix_info"): return False return VsphereVcenter.vim_vm_is_nutanix_cvm(vim_vm)