public void Strings() { Assert.Equal("500m", ByteUnits.ToMilliByteString(0.5m)); Assert.Equal("1000000m", ByteUnits.ToMilliByteString(1000)); Assert.Equal("500", ByteUnits.ToByteString(500)); Assert.Equal("1000000", ByteUnits.ToByteString(1000000)); Assert.Equal("1K", ByteUnits.ToKString(1000)); Assert.Equal("2K", ByteUnits.ToKString(2000)); Assert.Equal("0.5K", ByteUnits.ToKString(500)); Assert.Equal("1Ki", ByteUnits.ToKiString(1024)); Assert.Equal("2Ki", ByteUnits.ToKiString(2048)); Assert.Equal("0.5Ki", ByteUnits.ToKiString(512)); Assert.Equal("1M", ByteUnits.ToMString(1000000)); Assert.Equal("2M", ByteUnits.ToMString(2000000)); Assert.Equal("0.5M", ByteUnits.ToMString(500000)); Assert.Equal("1Mi", ByteUnits.ToMiString(1 * ByteUnits.MebiBytes)); Assert.Equal("2Mi", ByteUnits.ToMiString(2 * ByteUnits.MebiBytes)); Assert.Equal("0.5Mi", ByteUnits.ToMiString(ByteUnits.MebiBytes / 2)); Assert.Equal("1G", ByteUnits.ToGString(1000000000)); Assert.Equal("2G", ByteUnits.ToGString(2000000000)); Assert.Equal("0.5G", ByteUnits.ToGString(500000000)); Assert.Equal("1Gi", ByteUnits.ToGiString(1 * ByteUnits.GibiBytes)); Assert.Equal("2Gi", ByteUnits.ToGiString(2 * ByteUnits.GibiBytes)); Assert.Equal("0.5Gi", ByteUnits.ToGiString(ByteUnits.GibiBytes / 2)); Assert.Equal("1T", ByteUnits.ToTString(1000000000000)); Assert.Equal("2T", ByteUnits.ToTString(2000000000000)); Assert.Equal("0.5T", ByteUnits.ToTString(500000000000)); Assert.Equal("1Ti", ByteUnits.ToTiString(1 * ByteUnits.TebiBytes)); Assert.Equal("2Ti", ByteUnits.ToTiString(2 * ByteUnits.TebiBytes)); Assert.Equal("0.5Ti", ByteUnits.ToTiString(ByteUnits.TebiBytes / 2)); Assert.Equal("1P", ByteUnits.ToPString(1000000000000000)); Assert.Equal("2P", ByteUnits.ToPString(2000000000000000)); Assert.Equal("0.5P", ByteUnits.ToPString(500000000000000)); Assert.Equal("1Pi", ByteUnits.ToPiString(1 * ByteUnits.PebiBytes)); Assert.Equal("2Pi", ByteUnits.ToPiString(2 * ByteUnits.PebiBytes)); Assert.Equal("0.5Pi", ByteUnits.ToPiString(ByteUnits.PebiBytes / 2)); Assert.Equal("1E", ByteUnits.ToEString(1000000000000000000)); Assert.Equal("2E", ByteUnits.ToEString(2000000000000000000)); Assert.Equal("0.5E", ByteUnits.ToEString(500000000000000000)); Assert.Equal("1Ei", ByteUnits.ToEiString(1 * ByteUnits.ExbiBytes)); Assert.Equal("2Ei", ByteUnits.ToEiString(2 * ByteUnits.ExbiBytes)); Assert.Equal("0.5Ei", ByteUnits.ToEiString(ByteUnits.ExbiBytes / 2)); }
/// <summary> /// Inspects the node to determine physical machine capabilities like /// processor count, RAM, and primary disk capacity and then sets the /// corresponding node labels. /// </summary> /// <param name="node">The target node.</param> private void SetLabels(SshProxy <NodeDefinition> node) { CommandResponse result; // Download [/proc/meminfo] and extract the [MemTotal] value (in kB). result = node.SudoCommand("cat /proc/meminfo"); if (result.ExitCode == 0) { var memInfo = result.OutputText; var memTotalRegex = new Regex(@"^MemTotal:\s*(?<size>\d+)\s*kB", RegexOptions.Multiline); var memMatch = memTotalRegex.Match(memInfo); if (memMatch.Success && long.TryParse(memMatch.Groups["size"].Value, out var memSizeKiB)) { // Note that the RAM reported by Linux is somewhat less than the // physical RAM installed. node.Metadata.Labels.ComputeRam = (int)(memSizeKiB / 1024); // Convert KiB --> MiB } } // Download [/proc/cpuinfo] and count the number of processors. result = node.SudoCommand("cat /proc/cpuinfo"); if (result.ExitCode == 0) { var cpuInfo = result.OutputText; var processorRegex = new Regex(@"^processor\s*:\s*\d+", RegexOptions.Multiline); var processorMatches = processorRegex.Matches(cpuInfo); node.Metadata.Labels.ComputeCores = processorMatches.Count; } // Determine the primary disk size. // $hack(jeff.lill): // // I'm not entirely sure how to determine which block device is hosting // the primary file system for all systems. For now, I'm just going to // assume that this can be one of: // // /dev/sda1 // /dev/sda // /dev/xvda1 // /dev/xvda // // I'll try each of these in order and setting the label for the // first reasonable result we get back. var blockDevices = new string[] { "/dev/sda1", "/dev/sda", "/dev/xvda1", "/dev/xvda" }; foreach (var blockDevice in blockDevices) { result = node.SudoCommand($"lsblk -b --output SIZE -n -d {blockDevice}", RunOptions.LogOutput); if (result.ExitCode == 0) { if (long.TryParse(result.OutputText.Trim(), out var deviceSize) && deviceSize > 0) { node.Metadata.Labels.StorageSize = ByteUnits.ToGiString(deviceSize); break; } } } }
/// <inheritdoc/> public override bool Provision(bool force) { if (IsProvisionNOP) { // There's nothing to do here. return(true); } // Update the node labels with the actual capabilities of the // virtual machines being provisioned. foreach (var node in cluster.Definition.Nodes) { if (string.IsNullOrEmpty(node.Labels.PhysicalMachine)) { node.Labels.PhysicalMachine = Environment.MachineName; } if (node.Labels.ComputeCores == 0) { node.Labels.ComputeCores = cluster.Definition.Hosting.VmProcessors; } if (node.Labels.ComputeRam == 0) { node.Labels.ComputeRam = (int)(ClusterDefinition.ValidateSize(cluster.Definition.Hosting.VmMemory, typeof(HostingOptions), nameof(HostingOptions.VmMemory)) / ByteUnits.MebiBytes); } if (string.IsNullOrEmpty(node.Labels.StorageSize)) { node.Labels.StorageSize = ByteUnits.ToGiString(node.GetVmMemory(cluster.Definition)); } } // If a public address isn't explicitly specified, we'll assume that we're // running inside the network and we can access the private address. foreach (var node in cluster.Definition.Nodes) { if (string.IsNullOrEmpty(node.PublicAddress)) { node.PublicAddress = node.PrivateAddress; } } // Perform the provisioning operations. controller = new SetupController <NodeDefinition>($"Provisioning [{cluster.Definition.Name}] cluster", cluster.Nodes) { ShowStatus = this.ShowStatus, MaxParallel = 1 // We're only going to provision one VM at a time on a local Hyper-V instance. }; controller.AddGlobalStep("prepare hyper-v", () => PrepareHyperV()); controller.AddStep("virtual machines", (node, stepDelay) => ProvisionVM(node)); controller.AddGlobalStep(string.Empty, () => Finish(), quiet: true); if (!controller.Run()) { Console.Error.WriteLine("*** ERROR: One or more configuration steps failed."); return(false); } return(true); }
/// <inheritdoc/> public override bool Provision(bool force) { // $todo(jeff.lill): // // I'm not implementing [force] here. I'm not entirely sure // that this makes sense for production clusters. // // Perhaps it would make more sense to replace this with a // [neon cluster remove] command. // // https://github.com/nforgeio/neonKUBE/issues/156 if (IsProvisionNOP) { // There's nothing to do here. return(true); } // Update the node labels with the actual capabilities of the // virtual machines being provisioned. foreach (var node in cluster.Definition.Nodes) { if (string.IsNullOrEmpty(node.Labels.PhysicalMachine)) { node.Labels.PhysicalMachine = node.VmHost; } if (node.Labels.ComputeCores == 0) { node.Labels.ComputeCores = node.GetVmProcessors(cluster.Definition); } if (node.Labels.ComputeRam == 0) { node.Labels.ComputeRam = (int)(node.GetVmMemory(cluster.Definition) / ByteUnits.MebiBytes); } if (string.IsNullOrEmpty(node.Labels.StorageSize)) { node.Labels.StorageSize = ByteUnits.ToGiString(node.GetVmDisk(cluster.Definition)); } } // Build a list of [SshProxy] instances that map to the specified XenServer // hosts. We'll use the [XenClient] instances as proxy metadata. var sshProxies = new List <SshProxy <XenClient> >(); xenHosts = new List <XenClient>(); foreach (var host in cluster.Definition.Hosting.VmHosts) { var hostAddress = host.Address; var hostname = host.Name; var hostUsername = host.Username ?? cluster.Definition.Hosting.VmHostUsername; var hostPassword = host.Password ?? cluster.Definition.Hosting.VmHostPassword; if (string.IsNullOrEmpty(hostname)) { hostname = host.Address; } var xenHost = new XenClient(hostAddress, hostUsername, hostPassword, name: host.Name, logFolder: logFolder); xenHosts.Add(xenHost); sshProxies.Add(xenHost.SshProxy); } // We're going to provision the XenServer hosts in parallel to // speed up cluster setup. This works because each XenServer // is essentially independent from the others. controller = new SetupController <XenClient>($"Provisioning [{cluster.Definition.Name}] cluster", sshProxies) { ShowStatus = this.ShowStatus, MaxParallel = this.MaxParallel }; controller.AddWaitUntilOnlineStep(); controller.AddStep("host folders", (node, stepDelay) => node.CreateHostFolders()); controller.AddStep("verify readiness", (node, stepDelay) => VerifyReady(node)); controller.AddStep("virtual machine template", (node, stepDelay) => CheckVmTemplate(node)); controller.AddStep("virtual machines", (node, stepDelay) => ProvisionVirtualMachines(node)); controller.AddGlobalStep(string.Empty, () => Finish(), quiet: true); if (!controller.Run()) { Console.Error.WriteLine("*** ERROR: One or more configuration steps failed."); return(false); } return(true); }