/// <summary> /// Returns the list of <see cref="NodeDefinition"/> instances describing which hive /// nodes are to be hosted by a specific XenServer. /// </summary> /// <param name="xenHost">The target XenServer.</param> /// <returns>The list of nodes to be hosted on the XenServer.</returns> private List <SshProxy <NodeDefinition> > GetHostedNodes(XenClient xenHost) { var nodeDefinitions = hive.Definition.NodeDefinitions.Values; return(hive.Nodes.Where(n => n.Metadata.VmHost.Equals(xenHost.Name, StringComparison.InvariantCultureIgnoreCase)) .OrderBy(n => n.Name, StringComparer.CurrentCultureIgnoreCase) .ToList()); }
/// <summary> /// Determines whether a specific XenServer/XCP-ng host machine is running by logging into it. /// </summary> /// <param name="addressOrFQDN">Specifies the IP address or hostname for the target XenServer host machine.</param> /// <param name="username">Specifies the username to be used to connect to the host.</param> /// <param name="password">Specifies the host password.</param> /// <returns><c>true</c> if the host machine is running.</returns> public static bool IsRunning(string addressOrFQDN, string username, string password) { try { using (var client = new XenClient(addressOrFQDN, username, password)) { return(true); } } catch { return(false); } }
/// <summary> /// Connects to a XenServer/XCP-ng host and removes any VMs matching the name or file /// wildcard pattern, forceably shutting the VMs down when necessary. Note that the /// VM's drives will also be removed. /// </summary> /// <param name="addressOrFQDN">Specifies the IP address or hostname for the target XenServer host machine.</param> /// <param name="username">Specifies the username to be used to connect to the host.</param> /// <param name="password">Specifies the host password.</param> /// <param name="nameOrPattern">Specifies the VM name or pattern including '*' or '?' wildcards to be used to remove VMs.</param> public static void RemoveVMs(string addressOrFQDN, string username, string password, string nameOrPattern) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(addressOrFQDN), nameof(addressOrFQDN)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(username), nameof(username)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(password), nameof(password)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(nameOrPattern), nameof(nameOrPattern)); var nameRegex = NeonHelper.FileWildcardRegex(nameOrPattern); using (var client = new XenClient(addressOrFQDN, username, password)) { foreach (var vm in client.Machine.List() .Where(vm => nameRegex.IsMatch(vm.NameLabel))) { if (vm.IsRunning) { client.Machine.Shutdown(vm, turnOff: true); } client.Machine.Remove(vm, keepDrives: false); } } }
/// <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 hives and especially // when there are pet nodes. // // Perhaps it would make more sense to replace this with a // [neon hive remove] command. // // https://github.com/jefflill/NeonForge/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 hive.Definition.Nodes) { if (string.IsNullOrEmpty(node.Labels.PhysicalMachine)) { node.Labels.PhysicalMachine = node.VmHost; } if (node.Labels.ComputeCores == 0) { node.Labels.ComputeCores = node.GetVmProcessors(hive.Definition); } if (node.Labels.ComputeRamMB == 0) { node.Labels.ComputeRamMB = (int)(node.GetVmMemory(hive.Definition) / NeonHelper.Mega); } if (node.Labels.StorageCapacityGB == 0) { node.Labels.StorageCapacityGB = (int)(node.GetVmDisk(hive.Definition) / NeonHelper.Giga); } } // 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 hive.Definition.Hosting.VmHosts) { var hostAddress = host.Address; var hostname = host.Name; var hostUsername = host.Username ?? hive.Definition.Hosting.VmHostUsername; var hostPassword = host.Password ?? hive.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 hive setup. This works because each XenServer // is essentially independent from the others. controller = new SetupController <XenClient>($"Provisioning [{hive.Definition.Name}] hive", sshProxies) { ShowStatus = this.ShowStatus, MaxParallel = this.MaxParallel }; controller.AddWaitUntilOnlineStep(); controller.AddStep("sudo config", (node, stepDelay) => { using (var sshClient = node.CloneSshClient()) { // We're going to rewrite [/etc/sudoers.d/nopasswd] so that client // connections won't require a TTY and also that SUDO password // prompting will be disabled for all users. // // The file will end up looking like: // // Defaults !requiretty // %sudo ALL=NOPASSWD: ALL var response = sshClient.RunCommand("echo \"Defaults !requiretty\" >> /etc/sudoers.d/nopasswd"); if (response.ExitStatus != 0) { node.Fault($"Cannot update [/etc/sudoers.d/nopasswd]: {response.Result}"); return; } response = sshClient.RunCommand("echo \"%sudo ALL=NOPASSWD: ALL\" >> /etc/sudoers.d/nopasswd"); if (response.ExitStatus != 0) { node.Fault($"Cannot update [/etc/sudoers.d/nopasswd]: {response.Result}"); return; } } }); controller.AddStep("hive folders", (node, stepDelay) => node.CreateHiveHostFolders()); controller.AddStep("verify readiness", (node, stepDelay) => VerifyReady(node)); controller.AddStep("virtual machine template", (node, stepDelay) => CheckVmTemplate(node)); controller.AddStep("provision 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); }
/// <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); }