/* ============================================================================ Copyright 2014 Hewlett Packard Licensed 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. ============================================================================ */ using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using System.Management.Automation.Provider; using System.Collections.ObjectModel; using OpenStack.Storage; using OpenStack.Client.Powershell.Utility; using OpenStack.Client.Powershell.Providers.Common; using System.Management.Automation.Runspaces; using OpenStack; using System.Threading.Tasks; using System.Threading; using OpenStack.Client.Powershell.Providers.ObjectStorage; using System.Diagnostics.Contracts; using OpenStack.Client.Powershell.Utility; namespace OpenStack.Client.Powershell.Providers.Storage { [CmdletProvider("Object Storage", ProviderCapabilities.ExpandWildcards)] public class ObjectStorageNavigationProvider : BaseNavigationCmdletProvider { const string cDelimiter = @"\"; const string cFolderMarker = "folder.txt"; private class FolderStatistics { public long TotalFilesFound = 0; public long TotalBytes = 0; public long TotalFoldersFound = 0; } private IStorageServiceClient _storageClient; #region Implementation of DriveCmdletProvider //================================================================================================== /// /// Removes an Item from the store.. /// /// //================================================================================================== protected override void ClearItem(string path) { base.ClearItem(path); } //================================================================================================== /// /// /// //================================================================================================== private string StorageServiceURL { get { if (this.Drive == null || this.Drive.SharePath == null) { string serviceName = this.Context.CurrentServiceProvider.ServiceMaps.TranslateServiceName(CoreServices.ObjectStorage); return this.Context.ServiceCatalog.GetPublicEndpoint(serviceName, this.Context.CurrentRegion).ToString(); } else return this.Drive.SharePath; } } //================================================================================================== /// /// /// /// //================================================================================================== public System.Collections.ObjectModel.Collection GetAvailableDrives(Settings settings, ProviderInfo providerInfo) { Contract.Assert(settings != null); IEnumerable storageContainers = null; ObjectStorageDriveParameters parameters = new ObjectStorageDriveParameters(); if (this.Settings != null) { parameters.Settings = this.Settings; } else { parameters.Settings = settings; } try { // Get a list of Storage Containers... Task> getContainersTask = this.CreateServiceClient(CoreServices.ObjectStorage).ListStorageContainers(); getContainersTask.Wait(); storageContainers = getContainersTask.Result; } catch (Exception ex) { Console.WriteLine(ex); } Collection drives = new Collection(); // For every storageContainer that the User has access to, create a Drive that he can mount within Powershell.. try { if (storageContainers.Count() > 0) { foreach (StorageContainer storageContainer in storageContainers) { PSDriveInfo driveInfo = new PSDriveInfo(storageContainer.Name, providerInfo, "/", "Root folder for your storageContainer", null); parameters.Settings = this.Settings; ObjectStoragePSDriveInfo kvsDriveInfo = new ObjectStoragePSDriveInfo(driveInfo, parameters, this.Context, this.StorageServiceURL); //kvsDriveInfo.SharePath = storageContainer.SharePath; drives.Add(kvsDriveInfo); } } else { PSDriveInfo driveInfo = new PSDriveInfo("OS-Init", this.ProviderInfo, "/", "Root folder for your storageContainer", null); return new Collection { new ObjectStoragePSDriveInfo(driveInfo, parameters, this.Context, this.StorageServiceURL) }; } } catch (Exception) { } return drives; } //================================================================================================== /// /// /// //================================================================================================== private void SetDefaultDrive() { Runspace runSpace = Runspace.DefaultRunspace; runSpace.Open(); Pipeline pipeline = runSpace.CreatePipeline(); Command setDefaultDriveCmd = new Command("Set-Location OS-Init:"); pipeline.Commands.Add(setDefaultDriveCmd); } //================================================================================================== /// /// /// /// //================================================================================================== private Collection CreateDefaultDrive(ObjectStorageDriveParameters parameters) { WriteDebug("No Storage Containers found, initializing defaults."); PSDriveInfo driveInfo = new PSDriveInfo("OS-Init", this.ProviderInfo, "/", "Root folder for your Container", null); return new Collection { new ObjectStoragePSDriveInfo(driveInfo, parameters, this.Context, this.StorageServiceURL) }; } //================================================================================================== /// /// This is called when the CLIManifest.psd1 is registered with the Import-Module cmdlet. /// /// //================================================================================================== protected override System.Collections.ObjectModel.Collection InitializeDefaultDrives() { //Thread.Sleep(new TimeSpan(0, 0, 0, 5, 0)); this.InitializeSession(); if (this.Context.ServiceCatalog.Exists(this.ProviderInfo.Name)) { IEnumerable storageContainers = null; ObjectStorageDriveParameters parameters = new ObjectStorageDriveParameters(); var storageServiceClient = this.CreateServiceClient(CoreServices.ObjectStorage); Task accountTask = storageServiceClient.GetStorageAccount(); accountTask.Wait(); StorageAccount account = accountTask.Result; storageContainers = account.Containers; Collection drives = new Collection(); // For every storageContainer that the User has access to, create a Drive that he can mount within Powershell.. try { if (storageContainers != null) { if (storageContainers.Count() == 1 && storageContainers.First().Name == string.Empty) { return this.CreateDefaultDrive(parameters); } parameters.Settings = this.Settings; foreach (StorageContainer storageContainer in storageContainers) { PSDriveInfo driveInfo = new PSDriveInfo(storageContainer.Name, this.ProviderInfo, "/", "Root folder for your storageContainer", null); ObjectStoragePSDriveInfo kvsDriveInfo = new ObjectStoragePSDriveInfo(driveInfo, parameters, this.Context, this.StorageServiceURL); //kvsDriveInfo.SharePath = storageContainer.SharePath; drives.Add(kvsDriveInfo); } } else { return this.CreateDefaultDrive(parameters); } } catch (Exception ex) { WriteDebug("Exception : HPOSNavigationProvider.InitializeDefaultDrives"); WriteDebug(ex.Message); this.WriteError(new ErrorRecord(ex, "1", ErrorCategory.InvalidArgument, this)); } return drives; } else { return null; } } //======================================================================================================= /// /// /// //======================================================================================================= public bool ContainsWildcard(string path) { if (path.Contains("?") || path.Contains("*") || path.Contains("[") || path.Contains("]")) return true; else return false; } //======================================================================================================= /// /// /// /// /// //======================================================================================================= private string GetContainerName(string path) { return this.Drive.Name; if (path == "//") { return this.Drive.Name; } return null; } //================================================================================================== /// /// Retrieves a StorageObject with a fully qualified path from the User. /// /// /// //================================================================================================== private IEnumerable GetStorageObjects(string path) { List modelViewItems = new List(); Task listStorageObjectTask; string folderName = null; if (path != @"\" && !path.EndsWith(@"\")) path = path + @"\"; StoragePath storagePath = this.CreateStoragePath(path); var client = this.CreateServiceClient(CoreServices.ObjectStorage); if (path == "\\") { folderName = "/"; } else { folderName = path.Replace(@"\", "/").TrimStart('/'); } listStorageObjectTask = client.GetStorageFolder(this.GetContainerName(path), folderName); listStorageObjectTask.Wait(); foreach (StorageItem item in listStorageObjectTask.Result.Objects.Union(listStorageObjectTask.Result.Folders)) { StorageItemViewModel modelView = new StorageItemViewModel(item); modelViewItems.Add(modelView); } return modelViewItems; } //================================================================================================== /// /// /// //================================================================================================== private void WriteData(IEnumerable storageItemViewModel, string path, WildcardPattern pattern = null) { FolderStatistics statistics = new FolderStatistics(); bool doesExist = false; if (storageItemViewModel.Count() > 0 || storageItemViewModel == null) { // Write out any files that we find within this particular folder.. statistics = this.WriteFiles(storageItemViewModel, path); doesExist = true; } //List folders = storageObjects.Where(so => so.Name.EndsWith(@"\")).ToList(); List folders = storageItemViewModel.Where(so => so.Type == "Folder").ToList(); // List folders = storageObjects.Where(so => so.StorageObjectType == StorageObjectType.Folder).ToList(); if (folders != null) { // Now we'll write out any decendant folders found within the current directory.. this.WriteFolders(folders, ref statistics); doesExist = true; } // Finally write out the summary folder stats section of the command.. if (doesExist) { this.WriteFolderStatistics(statistics); } else { Console.WriteLine("No files or folders found."); Console.WriteLine(""); } } //================================================================================================== /// /// Writes out the files represented as StorageObjects for the supplied path. /// //================================================================================================== private FolderStatistics WriteFiles(IEnumerable storageObjects, string path) { FolderStatistics statistics = new FolderStatistics(); if (storageObjects != null && storageObjects.ToList() != null) { storageObjects.ToList().ForEach(item => { if (item.Type == "File") { Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), this.Context.Forecolor); WriteItemObject(item, item.Name, false); statistics.TotalFilesFound++; //statistics.TotalBytes = statistics.TotalBytes + item.Length; } }); } return statistics; } //================================================================================================== /// /// Writes out a list of supplied Folders and returns stats on them as well. /// //================================================================================================== private void WriteFolders(List folders, ref FolderStatistics statistics) { if (folders != null) { // Write out each folder we find in the current directory.. foreach (StorageItemViewModel obj in folders) { string fixedString = obj.Name.TrimEnd('/'); string[] folderNames = fixedString.Split('/'); string folderName = folderNames[folderNames.Count() - 1]; //obj.Name = folderName; if (folderName != "") { WriteItemObject(obj, folderName, false); statistics.TotalFoldersFound++; } } } else { statistics.TotalFoldersFound = 0; } } //================================================================================================== /// /// Writes out summary information on a given folder. /// //================================================================================================== private void WriteFolderStatistics(FolderStatistics statistics) { if (statistics.TotalFoldersFound > 0 || statistics.TotalFilesFound > 0) WriteItemObject ("----------------------------------------------------------------------------------------------", cDelimiter, false); WriteItemObject(" ", cDelimiter, false); WriteItemObject("Total # Folders found : " + Convert.ToString(statistics.TotalFoldersFound) , cDelimiter, false); WriteItemObject("Total # Items Found : " + Convert.ToString(statistics.TotalFilesFound) , cDelimiter, false); WriteItemObject("Total # Bytes in Folder : " + Convert.ToString(statistics.TotalBytes), cDelimiter, false); // WriteItemObject("Total Cost of Folder : " + "$" + Convert.ToString(statistics.TotalBytes * 2), cDelimiter, false); WriteItemObject(" ", cDelimiter, false); } //================================================================================================== /// /// Writes out header information to be used during listing operations. /// //================================================================================================== private void WriteHeader() { // Write out the commands header information first.. WriteItemObject(" ", cDelimiter, false); Console.ForegroundColor = ConsoleColor.DarkGray; WriteItemObject("==============================================================================================", cDelimiter, false); Console.ForegroundColor = ConsoleColor.Yellow; WriteItemObject("Storage Container : " + this.Drive.Name, cDelimiter, false); WriteItemObject("Directory of : " + this.Drive.Name + " " + cDelimiter + this.Drive.CurrentLocation, cDelimiter, false); Console.ForegroundColor = ConsoleColor.DarkGray; WriteItemObject("==============================================================================================", cDelimiter, false); Console.ForegroundColor = ConsoleColor.Green; WriteItemObject(" ", cDelimiter, false); } //================================================================================================== /// /// /// /// //================================================================================================== private bool CheckDefaultDrive(bool suppressMsg) { if (this.Drive.Name == "OS-Init") { if (!suppressMsg) { Console.WriteLine(" ", cDelimiter, false); Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine("==============================================================================================", cDelimiter, false); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("You are currently connected to the default Container. This drive is used to boot-strap the"); Console.WriteLine("Container creation prcoess. The only valid command that you can issue from here is the"); Console.WriteLine("New-Container command. After the Container is created successfully, issue CD yourcontainer:"); Console.WriteLine("This will bind to that container exposing the full functionality of OpenStack Object Storage. "); Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine("==============================================================================================", cDelimiter, false); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine(" ", cDelimiter, false); } return true; } else { return false; } } protected override object GetItemDynamicParameters(string path) { return base.GetItemDynamicParameters(path); } protected override void SetItem(string path, object value) { base.SetItem(path, value); } protected override string[] ExpandPath(string path) { return base.ExpandPath(path); } protected override string GetParentPath(string path, string root) { return base.GetParentPath(path, root); } protected override object GetChildNamesDynamicParameters(string path) { return base.GetChildNamesDynamicParameters(path); } protected override object GetChildItemsDynamicParameters(string path, bool recurse) { return base.GetChildItemsDynamicParameters(path, recurse); } protected override object ClearItemDynamicParameters(string path) { return base.ClearItemDynamicParameters(path); } protected override string NormalizeRelativePath(string path, string basePath) { return base.NormalizeRelativePath(path, basePath); } //================================================================================================== /// /// /// /// /// //================================================================================================== protected override bool IsItemContainer(string path) { if (path == @"\") { if (this.CheckDefaultDrive(false)) return true; } else { StoragePath storagePath = null; if (path.Contains(":")) { storagePath = new StoragePath(this.StorageServiceURL + path); } else { storagePath = new StoragePath(this.StorageServiceURL, this.Drive.Name, path.Substring(1) + "/"); } IStorageServiceClient storageService = this.CreateServiceClient(CoreServices.ObjectStorage); Task getStorageObjectTask = storageService.GetStorageObject(storagePath.Volume, storagePath.ResourcePath); //(storagePath.AbsoluteURI); getStorageObjectTask.Wait(); StorageObject sObject = getStorageObjectTask.Result; if (sObject != null) { return true; } else { return false; } } return true; } //================================================================================================== /// /// Displays all StorageObjects for a given folder.. /// /// /// //================================================================================================== protected override void GetChildItems(string path, bool recurse) { // Write out the commands header information first.. if (this.CheckDefaultDrive(true)) return; this.WriteHeader(); this.WriteData(this.GetStorageObjects(path).ToList(), path); } //================================================================================================== /// /// Called when the user decides to delete a KVSDrive. /// /// /// //================================================================================================== protected override PSDriveInfo RemoveDrive(PSDriveInfo drive) { if (drive == null) { WriteError(new ErrorRecord(new ArgumentNullException("drive"), "NullDrive", ErrorCategory.InvalidArgument, drive)); return null; } return drive; } //================================================================================================== /// /// Called by the PS runtime when a NewDrive is required.. /// /// /// //================================================================================================== protected override PSDriveInfo NewDrive(PSDriveInfo drive) { if (drive == null) { WriteError(new ErrorRecord(new ArgumentNullException("drive"), "NullDrive", ErrorCategory.InvalidArgument, drive)); return null; } if (drive.Root == null) { WriteError(new ErrorRecord(new ArgumentNullException("drive.Root"), "NullRoot", ErrorCategory.InvalidArgument, drive)); return null; } if (drive is ObjectStoragePSDriveInfo) { return drive; } var driveParams = this.DynamicParameters as ObjectStorageDriveParameters; return new ObjectStoragePSDriveInfo(drive, driveParams, this.Context, this.StorageServiceURL); } //================================================================================================== /// /// Called by the PS runtime when /// /// //================================================================================================== protected override object NewDriveDynamicParameters() { // The KVSDriveParameters instance will pull its values from the config file when created.. return new ObjectStorageDriveParameters(); } //================================================================================================== /// /// /// /// /// //================================================================================================== private string GetLeafFolder(string path) { string[] elements = path.Replace(@"\", "/").Split('/'); return elements[elements.Count() - 2]; } //================================================================================================== /// /// /// /// /// /// //================================================================================================== protected override void NewItem(string path, string itemTypeName, object newItemValue) { //// Validate the folder name first.. //if (path.Contains("%") || path.Contains(".")) //{ // Console.WriteLine(""); // Console.WriteLine("Folder names cannot contain % or . characters" ); // Console.WriteLine(""); //} //else //{ // if (!path.EndsWith("/")) // { // path = path + "/"; // } // StoragePath newFolderPath = this.CreateStoragePath(path); // this.RepositoryFactory.CreateStorageObjectRepository().MakeFolder(newFolderPath.AbsoluteURI); //} } //================================================================================================== /// /// /// /// /// //================================================================================================== private string ExtractPattern(string path) { string[] elements = path.Split('\\'); return elements[elements.Length - 1]; } //================================================================================================== /// /// /// /// //================================================================================================== protected override void GetItem(string path) { // Write out the commands header information first.. if (this.CheckDefaultDrive(true)) return; this.WriteHeader(); IEnumerable storageObjects = this.GetStorageObjects(path); this.WriteData(storageObjects.ToList(), path); } //================================================================================================== /// /// /// /// /// //================================================================================================== protected override bool ItemExists(string path) { if (path == "//") { if (this.CheckDefaultDrive(true)) { return false; } else { return base.ItemExists(path); } } return base.ItemExists(path); } #endregion #region Properties //================================================================================================== /// /// /// //================================================================================================== private ObjectStoragePSDriveInfo Drive { get { return this.PSDriveInfo as ObjectStoragePSDriveInfo; } } //========================================================================================= /// /// /// /// //========================================================================================= private StoragePath CreateStoragePath(string path) { return this.Drive.CreateStoragePath(path); } #endregion //================================================================================================== /// /// This test should not verify the existance of the item at the path. /// It should only perform syntactic and semantic validation of the /// path. For instance, for the file system provider, that path should /// be canonicalized, syntactically verified, and ensure that the path /// does not refer to a device. /// /// /// //================================================================================================== protected override bool IsValidPath(string path) { return true; } } }