Revision 224ff0f7 lib/client/gnt_node.py
b/lib/client/gnt_node.py | ||
---|---|---|
27 | 27 |
# C0103: Invalid name gnt-node |
28 | 28 |
|
29 | 29 |
import itertools |
30 |
import errno |
|
31 |
import tempfile |
|
30 | 32 |
|
31 | 33 |
from ganeti.cli import * |
32 | 34 |
from ganeti import cli |
... | ... | |
37 | 39 |
from ganeti import errors |
38 | 40 |
from ganeti import netutils |
39 | 41 |
from ganeti import pathutils |
42 |
from ganeti import serializer |
|
43 |
from ganeti import ssh |
|
40 | 44 |
from cStringIO import StringIO |
41 | 45 |
|
42 | 46 |
from ganeti import confd |
... | ... | |
134 | 138 |
errors.ECODE_INVAL) |
135 | 139 |
|
136 | 140 |
|
137 |
def _RunSetupSSH(options, nodes):
|
|
138 |
"""Wrapper around utils.RunCmd to call setup-ssh
|
|
141 |
def _TryReadFile(path):
|
|
142 |
"""Tries to read a file.
|
|
139 | 143 |
|
140 |
@param options: The command line options |
|
141 |
@param nodes: The nodes to setup |
|
144 |
If the file is not found, C{None} is returned. |
|
145 |
|
|
146 |
@type path: string |
|
147 |
@param path: Filename |
|
148 |
@rtype: None or string |
|
149 |
@todo: Consider adding a generic ENOENT wrapper |
|
142 | 150 |
|
143 | 151 |
""" |
152 |
try: |
|
153 |
return utils.ReadFile(path) |
|
154 |
except EnvironmentError, err: |
|
155 |
if err.errno == errno.ENOENT: |
|
156 |
return None |
|
157 |
else: |
|
158 |
raise |
|
159 |
|
|
144 | 160 |
|
145 |
assert nodes, "Empty node list" |
|
161 |
def _ReadSshKeys(keyfiles, _tostderr_fn=ToStderr): |
|
162 |
"""Reads SSH keys according to C{keyfiles}. |
|
163 |
|
|
164 |
@type keyfiles: dict |
|
165 |
@param keyfiles: Dictionary with keys of L{constants.SSHK_ALL} and two-values |
|
166 |
tuples (private and public key file) |
|
167 |
@rtype: list |
|
168 |
@return: List of three-values tuples (L{constants.SSHK_ALL}, private and |
|
169 |
public key as strings) |
|
170 |
|
|
171 |
""" |
|
172 |
result = [] |
|
146 | 173 |
|
147 |
cmd = [pathutils.SETUP_SSH] |
|
174 |
for (kind, (private_file, public_file)) in keyfiles.items(): |
|
175 |
private_key = _TryReadFile(private_file) |
|
176 |
public_key = _TryReadFile(public_file) |
|
148 | 177 |
|
149 |
# Pass --debug|--verbose to the external script if set on our invocation |
|
150 |
# --debug overrides --verbose |
|
178 |
if public_key and private_key: |
|
179 |
result.append((kind, private_key, public_key)) |
|
180 |
elif public_key or private_key: |
|
181 |
_tostderr_fn("Couldn't find a complete set of keys for kind '%s'; files" |
|
182 |
" '%s' and '%s'", kind, private_file, public_file) |
|
183 |
|
|
184 |
return result |
|
185 |
|
|
186 |
|
|
187 |
def _SetupSSH(options, cluster_name, node): |
|
188 |
"""Configures a destination node's SSH daemon. |
|
189 |
|
|
190 |
@param options: Command line options |
|
191 |
@type cluster_name |
|
192 |
@param cluster_name: Cluster name |
|
193 |
@type node: string |
|
194 |
@param node: Destination node name |
|
195 |
|
|
196 |
""" |
|
197 |
if options.force_join: |
|
198 |
ToStderr("The \"--force-join\" option is no longer supported and will be" |
|
199 |
" ignored.") |
|
200 |
|
|
201 |
cmd = [pathutils.PREPARE_NODE_JOIN] |
|
202 |
|
|
203 |
# Pass --debug/--verbose to the external script if set on our invocation |
|
151 | 204 |
if options.debug: |
152 | 205 |
cmd.append("--debug") |
153 |
elif options.verbose: |
|
206 |
|
|
207 |
if options.verbose: |
|
154 | 208 |
cmd.append("--verbose") |
155 |
if not options.ssh_key_check: |
|
156 |
cmd.append("--no-ssh-key-check") |
|
157 |
if options.force_join: |
|
158 |
cmd.append("--force-join") |
|
159 | 209 |
|
160 |
cmd.extend(nodes) |
|
210 |
host_keys = _ReadSshKeys(constants.SSH_DAEMON_KEYFILES) |
|
211 |
|
|
212 |
(_, root_keyfiles) = \ |
|
213 |
ssh.GetAllUserFiles(constants.SSH_LOGIN_USER, mkdir=False, dircheck=False) |
|
214 |
|
|
215 |
root_keys = _ReadSshKeys(root_keyfiles) |
|
216 |
|
|
217 |
(_, cert_pem) = \ |
|
218 |
utils.ExtractX509Certificate(utils.ReadFile(pathutils.NODED_CERT_FILE)) |
|
219 |
|
|
220 |
data = { |
|
221 |
constants.SSHS_CLUSTER_NAME: cluster_name, |
|
222 |
constants.SSHS_NODE_DAEMON_CERTIFICATE: cert_pem, |
|
223 |
constants.SSHS_SSH_HOST_KEY: host_keys, |
|
224 |
constants.SSHS_SSH_ROOT_KEY: root_keys, |
|
225 |
} |
|
226 |
|
|
227 |
srun = ssh.SshRunner(cluster_name) |
|
228 |
scmd = srun.BuildCmd(node, constants.SSH_LOGIN_USER, |
|
229 |
utils.ShellQuoteArgs(cmd), |
|
230 |
batch=False, ask_key=options.ssh_key_check, |
|
231 |
strict_host_check=options.ssh_key_check, quiet=False, |
|
232 |
use_cluster_key=False) |
|
233 |
|
|
234 |
tempfh = tempfile.TemporaryFile() |
|
235 |
try: |
|
236 |
tempfh.write(serializer.DumpJson(data)) |
|
237 |
tempfh.seek(0) |
|
161 | 238 |
|
162 |
result = utils.RunCmd(cmd, interactive=True) |
|
239 |
result = utils.RunCmd(scmd, interactive=True, input_fd=tempfh) |
|
240 |
finally: |
|
241 |
tempfh.close() |
|
163 | 242 |
|
164 | 243 |
if result.failed: |
165 |
errmsg = ("Command '%s' failed with exit code %s; output %r" % |
|
166 |
(result.cmd, result.exit_code, result.output)) |
|
167 |
raise errors.OpExecError(errmsg) |
|
244 |
raise errors.OpExecError("Command '%s' failed: %s" % |
|
245 |
(result.cmd, result.fail_reason)) |
|
168 | 246 |
|
169 | 247 |
|
170 | 248 |
@UsesRPC |
... | ... | |
206 | 284 |
sip = opts.secondary_ip |
207 | 285 |
|
208 | 286 |
# read the cluster name from the master |
209 |
output = cl.QueryConfigValues(["cluster_name"]) |
|
210 |
cluster_name = output[0] |
|
287 |
(cluster_name, ) = cl.QueryConfigValues(["cluster_name"]) |
|
211 | 288 |
|
212 | 289 |
if not readd and opts.node_setup: |
213 | 290 |
ToStderr("-- WARNING -- \n" |
... | ... | |
218 | 295 |
"and grant full intra-cluster ssh root access to/from it\n", node) |
219 | 296 |
|
220 | 297 |
if opts.node_setup: |
221 |
_RunSetupSSH(opts, [node])
|
|
298 |
_SetupSSH(opts, cluster_name, node)
|
|
222 | 299 |
|
223 | 300 |
bootstrap.SetupNodeDaemon(cluster_name, node, opts.ssh_key_check) |
224 | 301 |
|
Also available in: Unified diff