/// <inheritdoc/> public override bool Provision(bool force) { // Perform the provisioning operations. controller = new SetupController <NodeDefinition>($"Provisioning [{hive.Definition.Name}] hive", hive.Nodes) { ShowStatus = this.ShowStatus, MaxParallel = this.MaxParallel }; controller.AddStep("node labels", (node, stepDelay) => SetLabels(node)); 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 entirely sure that the [force] option 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 this.forceVmOverwrite = 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 hive.Definition.Nodes) { if (string.IsNullOrEmpty(node.Labels.PhysicalMachine)) { node.Labels.PhysicalMachine = Environment.MachineName; } if (node.Labels.ComputeCores == 0) { node.Labels.ComputeCores = hive.Definition.Hosting.VmProcessors; } if (node.Labels.ComputeRamMB == 0) { node.Labels.ComputeRamMB = (int)(HiveDefinition.ValidateSize(hive.Definition.Hosting.VmMemory, typeof(HostingOptions), nameof(HostingOptions.VmMemory)) / NeonHelper.Mega); } if (node.Labels.StorageCapacityGB == 0) { node.Labels.StorageCapacityGB = (int)(node.GetVmMemory(hive.Definition) / NeonHelper.Giga); } } // If a public address isn't explicitly specified, we'll assume that the // tool is running inside the network and we can access the private address. foreach (var node in hive.Definition.Nodes) { if (string.IsNullOrEmpty(node.PublicAddress)) { node.PublicAddress = node.PrivateAddress; } } // Perform the provisioning operations. controller = new SetupController <NodeDefinition>($"Provisioning [{hive.Definition.Name}] hive", hive.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("create 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 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); }