| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| package com.cloud.hypervisor.vmware.mo; |
| |
| import static com.cloud.utils.NumbersUtil.toHumanReadableSize; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import com.vmware.vim25.FolderFileInfo; |
| |
| import com.cloud.exception.CloudException; |
| import com.cloud.hypervisor.vmware.util.VmwareContext; |
| import com.cloud.utils.Pair; |
| import com.vmware.pbm.PbmProfile; |
| import com.vmware.vim25.DatastoreHostMount; |
| import com.vmware.vim25.DatastoreSummary; |
| import com.vmware.vim25.FileInfo; |
| import com.vmware.vim25.FileQueryFlags; |
| import com.vmware.vim25.HostDatastoreBrowserSearchResults; |
| import com.vmware.vim25.HostDatastoreBrowserSearchSpec; |
| import com.vmware.vim25.HostMountInfo; |
| import com.vmware.vim25.ManagedObjectReference; |
| import com.vmware.vim25.ObjectContent; |
| import com.vmware.vim25.ObjectSpec; |
| import com.vmware.vim25.PropertyFilterSpec; |
| import com.vmware.vim25.PropertySpec; |
| import com.vmware.vim25.SelectionSpec; |
| import com.vmware.vim25.TraversalSpec; |
| |
| public class DatastoreMO extends BaseMO { |
| |
| private String _name; |
| private Pair<DatacenterMO, String> _ownerDc; |
| |
| public DatastoreMO(VmwareContext context, ManagedObjectReference morDatastore) { |
| super(context, morDatastore); |
| } |
| |
| public DatastoreMO(VmwareContext context, String morType, String morValue) { |
| super(context, morType, morValue); |
| } |
| |
| @Override |
| public String getName() throws Exception { |
| if (_name == null) |
| _name = _context.getVimClient().getDynamicProperty(_mor, "name"); |
| |
| return _name; |
| } |
| |
| public DatastoreSummary getDatastoreSummary() throws Exception { |
| return (DatastoreSummary)_context.getVimClient().getDynamicProperty(_mor, "summary"); |
| } |
| |
| public ManagedObjectReference getDataCenterMor() throws Exception { |
| return getOwnerDatacenter().first().getMor(); |
| } |
| |
| public HostDatastoreBrowserMO getHostDatastoreBrowserMO() throws Exception { |
| return new HostDatastoreBrowserMO(_context, (ManagedObjectReference)_context.getVimClient().getDynamicProperty(_mor, "browser")); |
| } |
| |
| public List<DatastoreHostMount> getHostMounts() throws Exception { |
| return _context.getVimClient().getDynamicProperty(_mor, "host"); |
| } |
| |
| public String getInventoryPath() throws Exception { |
| Pair<DatacenterMO, String> dcInfo = getOwnerDatacenter(); |
| return dcInfo.second() + "/" + getName(); |
| } |
| |
| public Pair<DatacenterMO, String> getOwnerDatacenter() throws Exception { |
| if (_ownerDc != null) |
| return _ownerDc; |
| |
| PropertySpec pSpec = new PropertySpec(); |
| pSpec.setType("Datacenter"); |
| pSpec.getPathSet().add("name"); |
| |
| TraversalSpec folderParentTraversal = new TraversalSpec(); |
| folderParentTraversal.setType("Folder"); |
| folderParentTraversal.setPath("parent"); |
| folderParentTraversal.setName("folderParentTraversal"); |
| SelectionSpec sSpec = new SelectionSpec(); |
| sSpec.setName("folderParentTraversal"); |
| folderParentTraversal.getSelectSet().add(sSpec); |
| |
| TraversalSpec dsParentTraversal = new TraversalSpec(); |
| dsParentTraversal.setType("Datastore"); |
| dsParentTraversal.setPath("parent"); |
| dsParentTraversal.setName("dsParentTraversal"); |
| dsParentTraversal.getSelectSet().add(folderParentTraversal); |
| |
| ObjectSpec oSpec = new ObjectSpec(); |
| oSpec.setObj(getMor()); |
| oSpec.setSkip(Boolean.TRUE); |
| oSpec.getSelectSet().add(dsParentTraversal); |
| |
| PropertyFilterSpec pfSpec = new PropertyFilterSpec(); |
| pfSpec.getPropSet().add(pSpec); |
| pfSpec.getObjectSet().add(oSpec); |
| List<PropertyFilterSpec> pfSpecArr = new ArrayList<>(); |
| pfSpecArr.add(pfSpec); |
| |
| List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr); |
| |
| assert (ocs != null && ocs.size() > 0); |
| assert (ocs.get(0).getObj() != null); |
| assert (ocs.get(0).getPropSet() != null); |
| String dcName = ocs.get(0).getPropSet().get(0).getVal().toString(); |
| _ownerDc = new Pair<>(new DatacenterMO(_context, ocs.get(0).getObj()), dcName); |
| return _ownerDc; |
| } |
| |
| public void renameDatastore(String newDatastoreName) throws Exception { |
| _context.getService().renameDatastore(_mor, newDatastoreName); |
| } |
| |
| public void makeDirectory(String path, ManagedObjectReference morDc) throws Exception { |
| String datastoreName = getName(); |
| ManagedObjectReference morFileManager = _context.getServiceContent().getFileManager(); |
| |
| String fullPath = path; |
| if (!DatastoreFile.isFullDatastorePath(fullPath)) |
| fullPath = String.format("[%s] %s", datastoreName, path); |
| |
| _context.getService().makeDirectory(morFileManager, fullPath, morDc, true); |
| |
| int retry = 2; |
| for (int i = 0; i < retry; i++) { |
| DatastoreFile datastoreFile = new DatastoreFile(fullPath); |
| if (!folderExists(String.format("[%s]", datastoreName), datastoreFile.getFileName())) { |
| _context.getService().makeDirectory(morFileManager, fullPath, morDc, true); |
| } else { |
| return; |
| } |
| } |
| } |
| |
| String getDatastoreRootPath() throws Exception { |
| return String.format("[%s]", getName()); |
| } |
| |
| public String getDatastorePath(String relativePathWithoutDatastoreName) throws Exception { |
| return getDatastorePath(relativePathWithoutDatastoreName, false); |
| } |
| |
| public String getDatastorePath(String relativePathWithoutDatastoreName, boolean endWithPathDelimiter) throws Exception { |
| String path = String.format("[%s] %s", getName(), relativePathWithoutDatastoreName); |
| if (endWithPathDelimiter) { |
| if (!path.endsWith("/")) |
| return path + "/"; |
| } |
| return path; |
| } |
| |
| public boolean deleteFolder(String folder, ManagedObjectReference morDc) throws Exception { |
| ManagedObjectReference morFileManager = _context.getServiceContent().getFileManager(); |
| ManagedObjectReference morTask = _context.getService().deleteDatastoreFileTask(morFileManager, folder, morDc); |
| |
| boolean result = _context.getVimClient().waitForTask(morTask); |
| |
| if (result) { |
| _context.waitForTaskProgressDone(morTask); |
| |
| return true; |
| } else { |
| logger.error("VMware deleteDatastoreFile_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask)); |
| } |
| |
| return false; |
| } |
| |
| public boolean deleteFile(String path, ManagedObjectReference morDc, boolean testExistence) throws Exception { |
| return deleteFile(path, morDc, testExistence, null); |
| } |
| |
| public boolean deleteFile(String path, ManagedObjectReference morDc, boolean testExistence, String excludeFolders) throws Exception { |
| String datastoreName = getName(); |
| ManagedObjectReference morFileManager = _context.getServiceContent().getFileManager(); |
| |
| String fullPath = path; |
| if (!DatastoreFile.isFullDatastorePath(fullPath)) |
| fullPath = String.format("[%s] %s", datastoreName, path); |
| DatastoreFile file = new DatastoreFile(fullPath); |
| // Test if file specified is null or empty. We don't need to attempt to delete and return success. |
| if (file.getFileName() == null || file.getFileName().isEmpty()) { |
| return true; |
| } |
| |
| try { |
| if (testExistence && !fileExists(fullPath)) { |
| String searchResult = searchFileInSubFolders(file.getFileName(), true, excludeFolders); |
| if (searchResult == null) { |
| return true; |
| } else { |
| fullPath = searchResult; |
| } |
| } |
| } catch (Exception e) { |
| logger.info("Unable to test file existence due to exception " + e.getClass().getName() + ", skip deleting of it"); |
| return true; |
| } |
| |
| ManagedObjectReference morTask = _context.getService().deleteDatastoreFileTask(morFileManager, fullPath, morDc); |
| |
| boolean result = _context.getVimClient().waitForTask(morTask); |
| if (result) { |
| _context.waitForTaskProgressDone(morTask); |
| return true; |
| } else { |
| logger.error("VMware deleteDatastoreFile_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask)); |
| } |
| return false; |
| } |
| |
| public boolean copyDatastoreFile(String srcFilePath, ManagedObjectReference morSrcDc, ManagedObjectReference morDestDs, String destFilePath, |
| ManagedObjectReference morDestDc, boolean forceOverwrite) throws Exception { |
| |
| String srcDsName = getName(); |
| DatastoreMO destDsMo = new DatastoreMO(_context, morDestDs); |
| String destDsName = destDsMo.getName(); |
| |
| ManagedObjectReference morFileManager = _context.getServiceContent().getFileManager(); |
| String srcFullPath = srcFilePath; |
| if (!DatastoreFile.isFullDatastorePath(srcFullPath)) |
| srcFullPath = String.format("[%s] %s", srcDsName, srcFilePath); |
| |
| String destFullPath = destFilePath; |
| if (!DatastoreFile.isFullDatastorePath(destFullPath)) |
| destFullPath = String.format("[%s] %s", destDsName, destFilePath); |
| |
| ManagedObjectReference morTask = _context.getService().copyDatastoreFileTask(morFileManager, srcFullPath, morSrcDc, destFullPath, morDestDc, forceOverwrite); |
| |
| boolean result = _context.getVimClient().waitForTask(morTask); |
| if (result) { |
| _context.waitForTaskProgressDone(morTask); |
| return true; |
| } else { |
| logger.error("VMware copyDatastoreFile_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask)); |
| } |
| return false; |
| } |
| |
| public boolean moveDatastoreFile(String srcFilePath, ManagedObjectReference morSrcDc, ManagedObjectReference morDestDs, String destFilePath, |
| ManagedObjectReference morDestDc, boolean forceOverwrite) throws Exception { |
| |
| String srcDsName = getName(); |
| DatastoreMO destDsMo = new DatastoreMO(_context, morDestDs); |
| String destDsName = destDsMo.getName(); |
| |
| ManagedObjectReference morFileManager = _context.getServiceContent().getFileManager(); |
| String srcFullPath = srcFilePath; |
| if (!DatastoreFile.isFullDatastorePath(srcFullPath)) |
| srcFullPath = String.format("[%s] %s", srcDsName, srcFilePath); |
| |
| String destFullPath = destFilePath; |
| if (!DatastoreFile.isFullDatastorePath(destFullPath)) |
| destFullPath = String.format("[%s] %s", destDsName, destFilePath); |
| |
| DatastoreMO srcDsMo = new DatastoreMO(_context, morDestDs); |
| try { |
| if (!srcDsMo.fileExists(srcFullPath)) { |
| logger.error(String.format("Cannot move file to destination datastore due to file %s does not exists", srcFullPath)); |
| return false; |
| } |
| } catch (Exception e) { |
| logger.error(String.format("Cannot move file to destination datastore due to file %s due to exception %s", srcFullPath, e.getMessage())); |
| return false; |
| } |
| |
| ManagedObjectReference morTask = _context.getService().moveDatastoreFileTask(morFileManager, srcFullPath, morSrcDc, destFullPath, morDestDc, forceOverwrite); |
| |
| boolean result = _context.getVimClient().waitForTask(morTask); |
| if (result) { |
| _context.waitForTaskProgressDone(morTask); |
| return true; |
| } else { |
| logger.error("VMware moveDatastoreFile_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask)); |
| } |
| return false; |
| } |
| |
| @Deprecated |
| public String[] listDirContent(String path) throws Exception { |
| String fullPath = path; |
| if (!DatastoreFile.isFullDatastorePath(fullPath)) |
| fullPath = String.format("[%s] %s", getName(), fullPath); |
| |
| Pair<DatacenterMO, String> dcPair = getOwnerDatacenter(); |
| String url = getContext().composeDatastoreBrowseUrl(dcPair.second(), fullPath); |
| |
| // TODO, VMware currently does not have a formal API to list Datastore directory content, |
| // following hacking may have performance hit if datastore has a large number of files |
| return _context.listDatastoreDirContent(url); |
| } |
| |
| public boolean fileExists(String fileFullPath) throws Exception { |
| DatastoreFile file = new DatastoreFile(fileFullPath); |
| DatastoreFile dirFile = new DatastoreFile(file.getDatastoreName(), file.getDir()); |
| |
| HostDatastoreBrowserMO browserMo = getHostDatastoreBrowserMO(); |
| |
| logger.info("Search file " + file.getFileName() + " on " + dirFile.getPath()); |
| HostDatastoreBrowserSearchResults results = browserMo.searchDatastore(dirFile.getPath(), file.getFileName(), true); |
| if (results != null) { |
| List<FileInfo> info = results.getFile(); |
| if (info != null && info.size() == 1 && !(info.get(0) instanceof FolderFileInfo)) { |
| logger.info("File " + fileFullPath + " exists on datastore"); |
| return true; |
| } |
| } |
| |
| logger.info("File " + fileFullPath + " does not exist on datastore"); |
| return false; |
| } |
| |
| public long fileDiskSize(String fileFullPath) throws Exception { |
| long size = 0; |
| DatastoreFile file = new DatastoreFile(fileFullPath); |
| DatastoreFile dirFile = new DatastoreFile(file.getDatastoreName(), file.getDir()); |
| |
| HostDatastoreBrowserMO browserMo = getHostDatastoreBrowserMO(); |
| |
| HostDatastoreBrowserSearchSpec searchSpec = new HostDatastoreBrowserSearchSpec(); |
| FileQueryFlags fqf = new FileQueryFlags(); |
| fqf.setFileSize(true); |
| fqf.setFileOwner(true); |
| fqf.setFileType(true); |
| fqf.setModification(true); |
| searchSpec.setDetails(fqf); |
| searchSpec.setSearchCaseInsensitive(false); |
| searchSpec.getMatchPattern().add(file.getFileName()); |
| logger.debug("Search file " + file.getFileName() + " on " + dirFile.getPath()); //ROOT-2.vmdk, [3ecf7a579d3b3793b86d9d019a97ae27] s-2-VM |
| HostDatastoreBrowserSearchResults result = browserMo.searchDatastore(dirFile.getPath(), searchSpec); |
| if (result != null) { |
| List<FileInfo> info = result.getFile(); |
| for (FileInfo fi : info) { |
| if (file.getFileName().equals(fi.getPath())) { |
| logger.debug("File found = " + fi.getPath() + ", size=" + toHumanReadableSize(fi.getFileSize())); |
| return fi.getFileSize(); |
| } |
| } |
| } |
| logger.debug("File " + fileFullPath + " does not exist on datastore"); |
| return size; |
| } |
| |
| public boolean folderExists(String folderParentDatastorePath, String folderName) throws Exception { |
| HostDatastoreBrowserMO browserMo = getHostDatastoreBrowserMO(); |
| |
| HostDatastoreBrowserSearchResults results = browserMo.searchDatastore(folderParentDatastorePath, folderName, true); |
| if (results != null) { |
| List<FileInfo> info = results.getFile(); |
| if (info != null && info.size() == 1 && info.get(0) instanceof FolderFileInfo) { |
| logger.info("Folder " + folderName + " exists on datastore"); |
| return true; |
| } |
| } |
| |
| logger.info("Folder " + folderName + " does not exist on datastore"); |
| return false; |
| } |
| |
| public String searchFileInSubFolders(String fileName, boolean caseInsensitive) throws Exception { |
| return searchFileInSubFolders(fileName,caseInsensitive,null); |
| } |
| |
| public String searchFileInSubFolders(String fileName, boolean caseInsensitive, String excludeFolders) throws Exception { |
| String datastorePath = "[" + getName() + "]"; |
| String rootDirectoryFilePath = String.format("%s %s", datastorePath, fileName); |
| String[] searchExcludedFolders = getSearchExcludedFolders(excludeFolders); |
| if (fileExists(rootDirectoryFilePath)) { |
| return rootDirectoryFilePath; |
| } |
| |
| String parentFolderPath; |
| String absoluteFileName = null; |
| logger.info("Searching file " + fileName + " in " + datastorePath); |
| |
| HostDatastoreBrowserMO browserMo = getHostDatastoreBrowserMO(); |
| ArrayList<HostDatastoreBrowserSearchResults> results = browserMo.searchDatastoreSubFolders("[" + getName() + "]", fileName, caseInsensitive); |
| if (results != null && results.size() > 1) { |
| logger.warn("Multiple files with name " + fileName + " exists in datastore " + datastorePath + ". Trying to choose first file found in search attempt."); |
| } else if (results == null) { |
| String msg = "No file found with name " + fileName + " found in datastore " + datastorePath; |
| logger.error(msg); |
| throw new CloudException(msg); |
| } |
| for (HostDatastoreBrowserSearchResults result : results) { |
| List<FileInfo> info = result.getFile(); |
| if (info != null && info.size() > 0) { |
| for (FileInfo fi : info) { |
| absoluteFileName = parentFolderPath = result.getFolderPath(); |
| logger.info("Found file " + fileName + " in datastore at " + absoluteFileName); |
| if (parentFolderPath.endsWith("]")) |
| absoluteFileName += " "; |
| else if (!parentFolderPath.endsWith("/")) |
| absoluteFileName +="/"; |
| absoluteFileName += fi.getPath(); |
| if(isValidCloudStackFolderPath(parentFolderPath, searchExcludedFolders)) { |
| return absoluteFileName; |
| } |
| break; |
| } |
| } |
| } |
| return absoluteFileName; |
| } |
| |
| private String[] getSearchExcludedFolders(String excludeFolders) { |
| return excludeFolders != null ? excludeFolders.replaceAll("\\s","").split(",") : new String[] {}; |
| } |
| |
| private boolean isValidCloudStackFolderPath(String dataStoreFolderPath, String[] searchExcludedFolders) throws Exception { |
| String dsFolder = dataStoreFolderPath.replaceFirst("\\[" + getName() + "\\]", "").trim(); |
| for( String excludedFolder : searchExcludedFolders) { |
| if (dsFolder.startsWith(excludedFolder)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public boolean isAccessibleToHost(String hostValue) throws Exception { |
| boolean isAccessible = true; |
| List<DatastoreHostMount> hostMounts = getHostMounts(); |
| for (DatastoreHostMount hostMount : hostMounts) { |
| String hostMountValue = hostMount.getKey().getValue(); |
| if (hostMountValue.equalsIgnoreCase(hostValue)) { |
| HostMountInfo mountInfo = hostMount.getMountInfo(); |
| isAccessible = mountInfo.isAccessible(); |
| break; |
| } |
| } |
| return isAccessible; |
| } |
| |
| public boolean isDatastoreStoragePolicyComplaint(String storagePolicyId) throws Exception { |
| PbmProfileManagerMO profMgrMo = new PbmProfileManagerMO(_context); |
| PbmProfile profile = profMgrMo.getStorageProfile(storagePolicyId); |
| |
| PbmPlacementSolverMO placementSolverMo = new PbmPlacementSolverMO(_context); |
| boolean isDatastoreCompatible = placementSolverMo.isDatastoreCompatibleWithStorageProfile(_mor, profile); |
| |
| return isDatastoreCompatible; |
| } |
| |
| public String getDatastoreType() throws Exception { |
| DatastoreSummary summary = _context.getVimClient().getDynamicProperty(getMor(), "summary"); |
| return summary.getType() == null ? "" : summary.getType(); |
| } |
| } |