github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/environs/manual/init.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package manual 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "strconv" 11 "strings" 12 13 "github.com/juju/utils" 14 15 "github.com/juju/juju/instance" 16 "github.com/juju/juju/juju/arch" 17 "github.com/juju/juju/utils/ssh" 18 ) 19 20 // detectionScript is the script to run on the remote machine to 21 // detect the OS series and hardware characteristics. 22 const detectionScript = `#!/bin/bash 23 set -e 24 lsb_release -cs 25 uname -m 26 grep MemTotal /proc/meminfo 27 cat /proc/cpuinfo` 28 29 // checkProvisionedScript is the script to run on the remote machine 30 // to check if a machine has already been provisioned. 31 // 32 // This is a little convoluted to avoid returning an error in the 33 // common case of no matching files. 34 const checkProvisionedScript = "ls /etc/init/ | grep juju.*\\.conf || exit 0" 35 36 // CheckProvisioned checks if any juju init service already 37 // exist on the host machine. 38 var CheckProvisioned = checkProvisioned 39 40 func checkProvisioned(host string) (bool, error) { 41 logger.Infof("Checking if %s is already provisioned", host) 42 cmd := ssh.Command("ubuntu@"+host, []string{"/bin/bash"}, nil) 43 var stdout, stderr bytes.Buffer 44 cmd.Stdout = &stdout 45 cmd.Stderr = &stderr 46 cmd.Stdin = strings.NewReader(checkProvisionedScript) 47 if err := cmd.Run(); err != nil { 48 if stderr.Len() != 0 { 49 err = fmt.Errorf("%v (%v)", err, strings.TrimSpace(stderr.String())) 50 } 51 return false, err 52 } 53 output := strings.TrimSpace(stdout.String()) 54 provisioned := len(output) > 0 55 if provisioned { 56 logger.Infof("%s is already provisioned [%q]", host, output) 57 } else { 58 logger.Infof("%s is not provisioned", host) 59 } 60 return provisioned, nil 61 } 62 63 // DetectSeriesAndHardwareCharacteristics detects the OS 64 // series and hardware characteristics of the remote machine 65 // by connecting to the machine and executing a bash script. 66 var DetectSeriesAndHardwareCharacteristics = detectSeriesAndHardwareCharacteristics 67 68 func detectSeriesAndHardwareCharacteristics(host string) (hc instance.HardwareCharacteristics, series string, err error) { 69 logger.Infof("Detecting series and characteristics on %s", host) 70 cmd := ssh.Command("ubuntu@"+host, []string{"/bin/bash"}, nil) 71 var stdout, stderr bytes.Buffer 72 cmd.Stdout = &stdout 73 cmd.Stderr = &stderr 74 cmd.Stdin = bytes.NewBufferString(detectionScript) 75 if err := cmd.Run(); err != nil { 76 if stderr.Len() != 0 { 77 err = fmt.Errorf("%v (%v)", err, strings.TrimSpace(stderr.String())) 78 } 79 return hc, "", err 80 } 81 lines := strings.Split(stdout.String(), "\n") 82 series = strings.TrimSpace(lines[0]) 83 84 arch := arch.NormaliseArch(lines[1]) 85 hc.Arch = &arch 86 87 // HardwareCharacteristics wants memory in megabytes, 88 // meminfo reports it in kilobytes. 89 memkB := strings.Fields(lines[2])[1] // "MemTotal: NNN kB" 90 hc.Mem = new(uint64) 91 *hc.Mem, err = strconv.ParseUint(memkB, 10, 0) 92 *hc.Mem /= 1024 93 94 // For each "physical id", count the number of cores. 95 // This way we only count physical cores, not additional 96 // logical cores due to hyperthreading. 97 recorded := make(map[string]bool) 98 var physicalId string 99 hc.CpuCores = new(uint64) 100 for _, line := range lines[3:] { 101 if strings.HasPrefix(line, "physical id") { 102 physicalId = strings.TrimSpace(strings.SplitN(line, ":", 2)[1]) 103 } else if strings.HasPrefix(line, "cpu cores") { 104 var cores uint64 105 value := strings.TrimSpace(strings.SplitN(line, ":", 2)[1]) 106 if cores, err = strconv.ParseUint(value, 10, 0); err != nil { 107 return hc, "", err 108 } 109 if !recorded[physicalId] { 110 *hc.CpuCores += cores 111 recorded[physicalId] = true 112 } 113 } 114 } 115 if *hc.CpuCores == 0 { 116 // In the case of a single-core, non-HT CPU, we'll see no 117 // "physical id" or "cpu cores" lines. 118 *hc.CpuCores = 1 119 } 120 121 // TODO(axw) calculate CpuPower. What algorithm do we use? 122 logger.Infof("series: %s, characteristics: %s", series, hc) 123 return hc, series, nil 124 } 125 126 // InitUbuntuUser adds the ubuntu user if it doesn't 127 // already exist, updates its ~/.ssh/authorized_keys, 128 // and enables passwordless sudo for it. 129 // 130 // InitUbuntuUser will initially attempt to login as 131 // the ubuntu user, and verify that passwordless sudo 132 // is enabled; only if this is false will there be an 133 // attempt with the specified login. 134 // 135 // authorizedKeys may be empty, in which case the file 136 // will be created and left empty. 137 // 138 // stdin and stdout will be used for remote sudo prompts, 139 // if the ubuntu user must be created/updated. 140 func InitUbuntuUser(host, login, authorizedKeys string, stdin io.Reader, stdout io.Writer) error { 141 logger.Infof("initialising %q, user %q", host, login) 142 143 // To avoid unnecessary prompting for the specified login, 144 // initUbuntuUser will first attempt to ssh to the machine 145 // as "ubuntu" with password authentication disabled, and 146 // ensure that it can use sudo without a password. 147 // 148 // Note that we explicitly do not allocate a PTY, so we 149 // get a failure if sudo prompts. 150 cmd := ssh.Command("ubuntu@"+host, []string{"sudo", "-n", "true"}, nil) 151 if cmd.Run() == nil { 152 logger.Infof("ubuntu user is already initialised") 153 return nil 154 } 155 156 // Failed to login as ubuntu (or passwordless sudo is not enabled). 157 // Use specified login, and execute the initUbuntuScript below. 158 if login != "" { 159 host = login + "@" + host 160 } 161 script := fmt.Sprintf(initUbuntuScript, utils.ShQuote(authorizedKeys)) 162 var options ssh.Options 163 options.AllowPasswordAuthentication() 164 options.EnablePTY() 165 cmd = ssh.Command(host, []string{"sudo", "/bin/bash -c " + utils.ShQuote(script)}, &options) 166 var stderr bytes.Buffer 167 cmd.Stdin = stdin 168 cmd.Stdout = stdout // for sudo prompt 169 cmd.Stderr = &stderr 170 if err := cmd.Run(); err != nil { 171 if stderr.Len() != 0 { 172 err = fmt.Errorf("%v (%v)", err, strings.TrimSpace(stderr.String())) 173 } 174 return err 175 } 176 return nil 177 } 178 179 const initUbuntuScript = ` 180 set -e 181 (id ubuntu &> /dev/null) || useradd -m ubuntu -s /bin/bash 182 umask 0077 183 temp=$(mktemp) 184 echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > $temp 185 install -m 0440 $temp /etc/sudoers.d/90-juju-ubuntu 186 rm $temp 187 su ubuntu -c 'install -D -m 0600 /dev/null ~/.ssh/authorized_keys' 188 export authorized_keys=%s 189 if [ ! -z "$authorized_keys" ]; then 190 su ubuntu -c 'printf "%%s\n" "$authorized_keys" >> ~/.ssh/authorized_keys' 191 fi`