diff --git a/pkg/console/constant.go b/pkg/console/constant.go index c0c1e4001..64368668f 100644 --- a/pkg/console/constant.go +++ b/pkg/console/constant.go @@ -83,4 +83,6 @@ const ( persistentSizeNote = "Note: persistent partition stores data like system package and container images, not the VM data. \nYou can specify a size like 200Gi or 153600Mi. \nLeave it blank to use the default value." authorizedFile = "/home/rancher/.ssh/authorized_keys" + + defaultHostname = "rancher" ) diff --git a/pkg/console/install_panels.go b/pkg/console/install_panels.go index 958c7250b..503258cd5 100644 --- a/pkg/console/install_panels.go +++ b/pkg/console/install_panels.go @@ -506,7 +506,7 @@ func addDiskPanel(c *Console) error { if installModeOnly { return showNext(c, passwordConfirmPanel, passwordPanel) } - return showHostnamePage(c) + return showNetworkPage(c) } diskConfirm := func(_ *gocui.Gui, _ *gocui.View) error { @@ -840,7 +840,7 @@ func addAskCreatePanel(c *Console) error { return showRolePage(c) } if alreadyInstalled { - return showHostnamePage(c) + return showNetworkPage(c) } return showDiskPage(c) }, @@ -898,7 +898,7 @@ func addAskRolePanel(c *Console) error { c.config.Install.Role = selected askRoleV.Close() if alreadyInstalled { - return showHostnamePage(c) + return showNetworkPage(c) } return showDiskPage(c) }, @@ -1199,20 +1199,18 @@ func addHostnamePanel(c *Console) error { return c.setContentByName(hostnameValidatorPanel, message) } + getNextPagePanel := func() []string { + return []string{dnsServersPanel} + } + next := func() error { c.CloseElements(hostnamePanel, hostnameValidatorPanel) - return showNetworkPage(c) + return showNext(c, getNextPagePanel()...) } prev := func(_ *gocui.Gui, _ *gocui.View) error { c.CloseElements(hostnamePanel, hostnameValidatorPanel) - if alreadyInstalled { - if c.config.Install.Mode == config.ModeJoin { - return showNext(c, askRolePanel) - } - return showNext(c, askCreatePanel) - } - return showDiskPage(c) + return showNetworkPage(c) } validate := func() (string, error) { @@ -1234,6 +1232,10 @@ func addHostnamePanel(c *Console) error { hostnameV.PreShow = func() error { c.Gui.Cursor = true + // On the first run through the interactive installer, the hostname is + // not yet set in the harvester config, but we might have been given + // a new hostname via DHCP... + checkDHCPHostname(c.config, false) hostnameV.Value = c.config.Hostname return c.setContentByName(titlePanel, hostnameTitle) } @@ -1391,10 +1393,6 @@ func addNetworkPanel(c *Console) error { return "", nil } - getNextPagePanel := func() []string { - return []string{dnsServersPanel} - } - gotoNextPage := func(fromPanel string) error { if err := networkValidatorV.Show(); err != nil { return err @@ -1421,7 +1419,7 @@ func addNetworkPanel(c *Console) error { spinner.Stop(false, "") g.Update(func(_ *gocui.Gui) error { closeThisPage() - return showNext(c, getNextPagePanel()...) + return showHostnamePage(c) }) } }(c.Gui) @@ -1430,7 +1428,13 @@ func addNetworkPanel(c *Console) error { gotoPrevPage := func(_ *gocui.Gui, _ *gocui.View) error { closeThisPage() - return showHostnamePage(c) + if alreadyInstalled { + if c.config.Install.Mode == config.ModeJoin { + return showNext(c, askRolePanel) + } + return showNext(c, askCreatePanel) + } + return showDiskPage(c) } // askInterfaceV askInterfaceV.PreShow = func() error { @@ -2016,6 +2020,8 @@ func addInstallPanel(c *Console) error { if alreadyInstalled && c.config.Automatic == true && c.config.ManagementInterface.Method == "dhcp" { configureInstallModeDHCP(c) } + + // Need to merge remote config first logrus.Info("Local config: ", c.config) if c.config.Install.ConfigURL != "" { printToPanel(c.Gui, fmt.Sprintf("Fetching %s...", c.config.Install.ConfigURL), installPanel) @@ -2031,28 +2037,50 @@ func addInstallPanel(c *Console) error { return } logrus.Info("Local config (merged): ", c.config) + } - if needToGetVIPFromDHCP(c.config.VipMode, c.config.Vip, c.config.VipHwAddr) { - printToPanel(c.Gui, "Configuring network...", installPanel) - if _, err := applyNetworks(c.config.ManagementInterface, c.config.Hostname); err != nil { - printToPanel(c.Gui, fmt.Sprintf("can't apply networks: %s", err), installPanel) - return - } - mgmtName := getManagementInterfaceName(c.config.ManagementInterface) - vip, err := getVipThroughDHCP(mgmtName) - if err != nil { - printToPanel(c.Gui, fmt.Sprintf("fail to get vip: %s", err), installPanel) - return - } - c.config.Vip = vip.ipv4Addr - c.config.VipHwAddr = vip.hwAddr + // case insensitive for network method and vip mode + c.config.ManagementInterface.Method = strings.ToLower(c.config.ManagementInterface.Method) + c.config.VipMode = strings.ToLower(c.config.VipMode) + + // lookup MAC Address to populate device names where needed + // lookup device name to populate MAC Address + // This needs to happen early, before a possible call to + // applyNetworks() in the DHCP case. + for i := range c.config.ManagementInterface.Interfaces { + if err := c.config.ManagementInterface.Interfaces[i].FindNetworkInterfaceNameAndHwAddr(); err != nil { + logrus.Error(err) + printToPanel(c.Gui, err.Error(), installPanel) + return + } + } + + if c.config.Automatic && c.config.Install.ManagementInterface.Method == config.NetworkMethodDHCP { + // Only need to do this for automatic installs, as manual installs will + // have already run applyNetworks() + printToPanel(c.Gui, "Configuring network...", installPanel) + if output, err := applyNetworks(c.config.ManagementInterface, c.config.Hostname); err != nil { + printToPanel(c.Gui, fmt.Sprintf("Can't apply networks: %s\n%s", err, string(output)), installPanel) + return } } - c.config.VipMode = strings.ToLower(c.config.VipMode) - if c.config.Hostname == "" { - c.config.Hostname = generateHostName() + if needToGetVIPFromDHCP(c.config.VipMode, c.config.Vip, c.config.VipHwAddr) { + mgmtName := getManagementInterfaceName(c.config.ManagementInterface) + vip, err := getVipThroughDHCP(mgmtName) + if err != nil { + printToPanel(c.Gui, fmt.Sprintf("fail to get vip: %s", err), installPanel) + return + } + c.config.Vip = vip.ipv4Addr + c.config.VipHwAddr = vip.hwAddr } + + // If no hostname was provided in the config, this function will + // default the hostname to either what's supplied by the DHCP sever, + // or a randomly generated name. + checkDHCPHostname(c.config, true) + if c.config.TTY == "" { c.config.TTY = getFirstConsoleTTY() } @@ -2065,19 +2093,6 @@ func addInstallPanel(c *Console) error { c.config.ServerURL = formatted } - // lookup MAC Address to populate device names where needed - // lookup device name to populate MAC Address - tmpInterfaces := []config.NetworkInterface{} - for _, iface := range c.config.ManagementInterface.Interfaces { - if err := iface.FindNetworkInterfaceNameAndHwAddr(); err != nil { - logrus.Error(err) - printToPanel(c.Gui, err.Error(), installPanel) - return - } - tmpInterfaces = append(tmpInterfaces, iface) - } - c.config.ManagementInterface.Interfaces = tmpInterfaces - if !alreadyInstalled { // Have to handle preflight warnings here because we can't check // the NIC speed until we've got the correct set of interfaces. @@ -2111,9 +2126,6 @@ func addInstallPanel(c *Console) error { c.config.DataDisk = "" } - // case insensitive for network method and vip mode - c.config.ManagementInterface.Method = strings.ToLower(c.config.ManagementInterface.Method) - if err := validateConfig(ConfigValidator{}, c.config); err != nil { printToPanel(c.Gui, err.Error(), installPanel) return @@ -2434,7 +2446,7 @@ func addDNSServersPanel(c *Console) error { } gotoPrevPage := func(_ *gocui.Gui, _ *gocui.View) error { closeThisPage() - return showNetworkPage(c) + return showHostnamePage(c) } gotoNextPage := func() error { closeThisPage() @@ -2560,6 +2572,23 @@ func configureInstallModeDHCP(c *Console) { } +func checkDHCPHostname(c *config.HarvesterConfig, generate bool) { + if c.Hostname == "" { + hostname, err := os.Hostname() + if err != nil { + logrus.Errorf("error fetching hostname from underlying OS: %v", err) + } + + if hostname != defaultHostname && hostname != "" { + c.Hostname = hostname + } else { + if generate { + c.Hostname = generateHostName() + } + } + } +} + func mergeCloudInit(c *config.HarvesterConfig) error { cloudConfig, err := config.ReadUserDataConfig() if err != nil { diff --git a/pkg/console/network.go b/pkg/console/network.go index b6e3be4ad..7d06147a0 100644 --- a/pkg/console/network.go +++ b/pkg/console/network.go @@ -24,6 +24,29 @@ func applyNetworks(network config.Network, hostname string) ([]byte, error) { return nil, err } + // If called without a hostname set, we enable setting hostname via the + // DHCP server, in case the DHCP server is configured to give us a + // hostname we can use by default. + // + // If we move the network interface page of the installer so it's before + // the hostname page, this function will activate once the management + // NIC is configured, and if the DHCP server is configured correctly, + // the system hostname will be set to the one provided by the server. + // Later, on the hostname page, we can default the hostname field to + // the current system hostname. + + dhclientSetHostname := "no" + if hostname == "" { + dhclientSetHostname = "yes" + } + output, err := exec.Command("sed", "-i", + fmt.Sprintf(`s/^DHCLIENT_SET_HOSTNAME=.*/DHCLIENT_SET_HOSTNAME="%s"/`, dhclientSetHostname), + "/etc/sysconfig/network/dhcp").CombinedOutput() + if err != nil { + logrus.Error(err, string(output)) + return output, err + } + conf := &yipSchema.YipConfig{ Name: "Network Configuration", Stages: map[string][]yipSchema.Stage{ @@ -33,7 +56,7 @@ func applyNetworks(network config.Network, hostname string) ([]byte, error) { }, }, } - _, err := config.UpdateManagementInterfaceConfig(&conf.Stages["live"][1], network, true) + _, err = config.UpdateManagementInterfaceConfig(&conf.Stages["live"][1], network, true) if err != nil { return nil, err } @@ -56,6 +79,10 @@ func applyNetworks(network config.Network, hostname string) ([]byte, error) { cmd := exec.Command("/usr/bin/yip", "-s", "live", tempFile.Name()) cmd.Env = os.Environ() bytes, err = cmd.CombinedOutput() + if err != nil { + logrus.Error(err, string(bytes)) + return bytes, err + } // Restore Down NIC to up if err := upAllLinks(); err != nil { logrus.Errorf("failed to bring all link up: %s", err.Error())