def _VerifyRequest(exp_timeout, req): self.assertEqual(req.read_timeout, exp_timeout) req.success = True req.resp_status_code = http.HTTP_OK req.resp_body = serializer.DumpJson((True, hex(req.read_timeout)))
def _GetInvalidResponseA(self, req): self.assertEqual(req.path, "/version") req.success = True req.resp_status_code = http.HTTP_OK req.resp_body = serializer.DumpJson( ("This", "is", "an", "invalid", "response", "!", 1, 2, 3))
def _GetInvalidResponseB(self, req): self.assertEqual(req.path, "/version") req.success = True req.resp_status_code = http.HTTP_OK req.resp_body = serializer.DumpJson("invalid response")
class RemoteApiHandler(http.auth.HttpServerRequestAuthentication, http.server.HttpServerHandler): """REST Request Handler Class. """ AUTH_REALM = "Ganeti Remote API" def __init__(self, user_fn, reqauth, _client_cls=None): """Initializes this class. @type user_fn: callable @param user_fn: Function receiving username as string and returning L{http.auth.PasswordFileUser} or C{None} if user is not found @type reqauth: bool @param reqauth: Whether to require authentication """ # pylint: disable=W0233 # it seems pylint doesn't see the second parent class there http.server.HttpServerHandler.__init__(self) http.auth.HttpServerRequestAuthentication.__init__(self) self._client_cls = _client_cls self._resmap = connector.Mapper() self._user_fn = user_fn self._reqauth = reqauth @staticmethod def FormatErrorMessage(values): """Formats the body of an error message. @type values: dict @param values: dictionary with keys C{code}, C{message} and C{explain}. @rtype: tuple; (string, string) @return: Content-type and response body """ return (http.HTTP_APP_JSON, serializer.DumpJson(values)) def _GetRequestContext(self, req): """Returns the context for a request. The context is cached in the req.private variable. """ if req.private is None: (HandlerClass, items, args) = \ self._resmap.getController(req.request_path) ctx = RemoteApiRequestContext() ctx.handler = HandlerClass(items, args, req, _client_cls=self._client_cls) method = req.request_method.upper() try: ctx.handler_fn = getattr(ctx.handler, method) except AttributeError: raise http.HttpNotImplemented( "Method %s is unsupported for path %s" % (method, req.request_path)) ctx.handler_access = baserlib.GetHandlerAccess(ctx.handler, method) # Require permissions definition (usually in the base class) if ctx.handler_access is None: raise AssertionError("Permissions definition missing") # This is only made available in HandleRequest ctx.body_data = None req.private = ctx # Check for expected attributes assert req.private.handler assert req.private.handler_fn assert req.private.handler_access is not None return req.private def AuthenticationRequired(self, req): """Determine whether authentication is required. """ return self._reqauth or bool( self._GetRequestContext(req).handler_access) def Authenticate(self, req, username, password): """Checks whether a user can access a resource. """ ctx = self._GetRequestContext(req) user = self._user_fn(username) if not (user and self.VerifyBasicAuthPassword(req, username, password, user.password)): # Unknown user or password wrong return False if (not ctx.handler_access or set(user.options).intersection(ctx.handler_access)): # Allow access return True # Access forbidden raise http.HttpForbidden() def HandleRequest(self, req): """Handles a request. """ ctx = self._GetRequestContext(req) # Deserialize request parameters if req.request_body: # RFC2616, 7.2.1: Any HTTP/1.1 message containing an entity-body SHOULD # include a Content-Type header field defining the media type of that # body. [...] If the media type remains unknown, the recipient SHOULD # treat it as type "application/octet-stream". req_content_type = req.request_headers.get( http.HTTP_CONTENT_TYPE, http.HTTP_APP_OCTET_STREAM) if req_content_type.lower() != http.HTTP_APP_JSON.lower(): raise http.HttpUnsupportedMediaType() try: ctx.body_data = serializer.LoadJson(req.request_body) except Exception: raise http.HttpBadRequest(message="Unable to parse JSON data") else: ctx.body_data = None try: result = ctx.handler_fn() except rpcerr.TimeoutError: raise http.HttpGatewayTimeout() except rpcerr.ProtocolError, err: raise http.HttpBadGateway(str(err)) req.resp_headers[http.HTTP_CONTENT_TYPE] = http.HTTP_APP_JSON return serializer.DumpJson(result)
def _GetVersionResponseFail(self, errinfo, req): self.assertEqual(req.path, "/version") req.success = True req.resp_status_code = http.HTTP_OK req.resp_body = serializer.DumpJson((False, errinfo))
def encode_string(self, message): return (serializer.DumpJson(message) + hv_kvm.QmpConnection._MESSAGE_END_TOKEN)
def Run(self): """Main program. """ self._ComposePaths() self.SetupLogging() # Option checking if self.args: raise Error("No arguments expected") if self.opts.downgrade and not self.opts.no_verify: self.opts.no_verify = True # Check master name if not (self.CheckHostname(self.opts.SSCONF_MASTER_NODE) or self.opts.ignore_hostname): logging.error("Aborting due to hostname mismatch") sys.exit(constants.EXIT_FAILURE) self._AskUser() # Check whether it's a Ganeti configuration directory if not (os.path.isfile(self.opts.CONFIG_DATA_PATH) and os.path.isfile(self.opts.SERVER_PEM_PATH) and os.path.isfile(self.opts.KNOWN_HOSTS_PATH)): raise Error(("%s does not seem to be a Ganeti configuration" " directory") % self.opts.data_dir) if not os.path.isdir(self.opts.conf_dir): raise Error("Not a directory: %s" % self.opts.conf_dir) self.config_data = serializer.LoadJson( utils.ReadFile(self.opts.CONFIG_DATA_PATH)) try: config_version = self.config_data["version"] except KeyError: raise Error("Unable to determine configuration version") (config_major, config_minor, config_revision) = \ version.SplitVersion(config_version) logging.info("Found configuration version %s (%d.%d.%d)", config_version, config_major, config_minor, config_revision) if "config_version" in self.config_data["cluster"]: raise Error("Inconsistent configuration: found config_version in" " configuration file") # Downgrade to the previous stable version if self.opts.downgrade: self._Downgrade(config_major, config_minor, config_version, config_revision) # Upgrade from 2.{0..14} to 2.15 elif config_major == 2 and config_minor in range(0, 15): if config_revision != 0: logging.warning("Config revision is %s, not 0", config_revision) if not self.UpgradeAll(): raise Error("Upgrade failed:\n%s" % '\n'.join(self.errors)) elif config_major == TARGET_MAJOR and config_minor == TARGET_MINOR: logging.info("No changes necessary") else: raise Error( "Configuration version %d.%d.%d not supported by this tool" % (config_major, config_minor, config_revision)) try: logging.info("Writing configuration file to %s", self.opts.CONFIG_DATA_PATH) utils.WriteFile(file_name=self.opts.CONFIG_DATA_PATH, data=serializer.DumpJson(self.config_data), mode=0600, dry_run=self.opts.dry_run, backup=True) if not self.opts.dry_run: # This creates the cluster certificate if it does not exist yet. # In this case, we do not automatically create a client certificate # as well, because if the cluster certificate did not exist before, # no client certificate will exist on any node yet. In this case # all client certificate should be renewed by 'gnt-cluster # renew-crypto --new-node-certificates'. This will be enforced # by a nagging warning in 'gnt-cluster verify'. bootstrap.GenerateClusterCrypto( False, False, False, False, False, False, None, nodecert_file=self.opts.SERVER_PEM_PATH, rapicert_file=self.opts.RAPI_CERT_FILE, spicecert_file=self.opts.SPICE_CERT_FILE, spicecacert_file=self.opts.SPICE_CACERT_FILE, hmackey_file=self.opts.CONFD_HMAC_KEY, cds_file=self.opts.CDS_FILE) except Exception: logging.critical( "Writing configuration failed. It is probably in an" " inconsistent state and needs manual intervention.") raise self._TestLoadingConfigFile()
def _WriteConfig(filename, data): utils.WriteFile(filename, data=serializer.DumpJson(data))
class RemoteApiHandler(http.auth.HttpServerRequestAuthentication, http.server.HttpServerHandler): """REST Request Handler Class. """ AUTH_REALM = "Ganeti Remote API" def __init__(self, authenticator, reqauth, _client_cls=None): """Initializes this class. @type authenticator: an implementation of {RapiAuthenticator} interface @param authenticator: a class containing an implementation of ValidateRequest function @type reqauth: bool @param reqauth: Whether to require authentication """ # pylint: disable=W0233 # it seems pylint doesn't see the second parent class there http.server.HttpServerHandler.__init__(self) http.auth.HttpServerRequestAuthentication.__init__(self) self._client_cls = _client_cls self._resmap = connector.Mapper() self._authenticator = authenticator self._reqauth = reqauth @staticmethod def FormatErrorMessage(values): """Formats the body of an error message. @type values: dict @param values: dictionary with keys C{code}, C{message} and C{explain}. @rtype: tuple; (string, string) @return: Content-type and response body """ return (http.HTTP_APP_JSON, serializer.DumpJson(values)) def _GetRequestContext(self, req): """Returns the context for a request. The context is cached in the req.private variable. """ if req.private is None: (HandlerClass, items, args) = \ self._resmap.getController(req.request_path) ctx = RemoteApiRequestContext() ctx.handler = HandlerClass(items, args, req, _client_cls=self._client_cls) method = req.request_method.upper() try: ctx.handler_fn = getattr(ctx.handler, method) except AttributeError: raise http.HttpNotImplemented( "Method %s is unsupported for path %s" % (method, req.request_path)) ctx.handler_access = baserlib.GetHandlerAccess(ctx.handler, method) # Require permissions definition (usually in the base class) if ctx.handler_access is None: raise AssertionError("Permissions definition missing") # This is only made available in HandleRequest ctx.body_data = None req.private = ctx # Check for expected attributes assert req.private.handler assert req.private.handler_fn assert req.private.handler_access is not None return req.private def AuthenticationRequired(self, req): """Determine whether authentication is required. """ # Auth is required if # a) Auth is forced to be enabled always. # b) Auth isn't enforced, but the handler for that path/HTTP method # (GET, PUT etc) requires cluster config read/write access. An # example of this can be seen in rapi.rlib2.R_2_jobs_id_wait.GET_ACCESS. return self._reqauth or bool( self._GetRequestContext(req).handler_access) def Authenticate(self, req): """Checks whether a user can access a resource. @return: username of an authenticated user or None otherwise """ ctx = self._GetRequestContext(req) auth_user = self._authenticator.ValidateRequest( req, ctx.handler_access, self.GetAuthRealm(req)) if auth_user is None: return False ctx.handler.auth_user = auth_user return True def HandleRequest(self, req): """Handles a request. """ ctx = self._GetRequestContext(req) # Deserialize request parameters if req.request_body: # RFC2616, 7.2.1: Any HTTP/1.1 message containing an entity-body SHOULD # include a Content-Type header field defining the media type of that # body. [...] If the media type remains unknown, the recipient SHOULD # treat it as type "application/octet-stream". req_content_type = req.request_headers.get( http.HTTP_CONTENT_TYPE, http.HTTP_APP_OCTET_STREAM) if req_content_type.lower() != http.HTTP_APP_JSON.lower(): raise http.HttpUnsupportedMediaType() try: ctx.body_data = serializer.LoadJson(req.request_body) except Exception: raise http.HttpBadRequest(message="Unable to parse JSON data") else: ctx.body_data = None try: result = ctx.handler_fn() except rpcerr.TimeoutError: raise http.HttpGatewayTimeout() except rpcerr.ProtocolError, err: raise http.HttpBadGateway(str(err)) req.resp_headers[http.HTTP_CONTENT_TYPE] = http.HTTP_APP_JSON return serializer.DumpJson(result)
def testQueryAuth(self): username = "******" password = "******" header_fn = compat.partial(self._MakeAuthHeaders, username, password) def _LookupUserNoWrite(name): if name == username: return http.auth.PasswordFileUser(name, password, []) else: return None for access in [rapi.RAPI_ACCESS_WRITE, rapi.RAPI_ACCESS_READ]: def _LookupUserWithWrite(name): if name == username: return http.auth.PasswordFileUser(name, password, [ access, ]) else: return None for qr in constants.QR_VIA_RAPI: # The /2/query resource has somewhat special rules for authentication as # it can be used to retrieve critical information path = "/2/query/%s" % qr for method in rapi.baserlib._SUPPORTED_METHODS: # No authorization (code, _, _) = self._Test(method, path, "", "") if method in (http.HTTP_DELETE, http.HTTP_POST): self.assertEqual(code, http.HttpNotImplemented.code) continue self.assertEqual(code, http.HttpUnauthorized.code) # Incorrect user (code, _, _) = self._Test(method, path, header_fn(True), "", user_fn=self._LookupWrongUser) self.assertEqual(code, http.HttpUnauthorized.code) # User has no write access, but the password is correct (code, _, _) = self._Test(method, path, header_fn(True), "", user_fn=_LookupUserNoWrite) self.assertEqual(code, http.HttpForbidden.code) # Wrong password and no write access (code, _, _) = self._Test(method, path, header_fn(False), "", user_fn=_LookupUserNoWrite) self.assertEqual(code, http.HttpUnauthorized.code) # Wrong password with write access (code, _, _) = self._Test(method, path, header_fn(False), "", user_fn=_LookupUserWithWrite) self.assertEqual(code, http.HttpUnauthorized.code) # Prepare request information if method == http.HTTP_PUT: reqpath = path body = serializer.DumpJson({ "fields": ["name"], }) elif method == http.HTTP_GET: reqpath = "%s?fields=name" % path body = "" else: self.fail("Unknown method '%s'" % method) # User has write access, password is correct (code, _, data) = self._Test(method, reqpath, header_fn(True), body, user_fn=_LookupUserWithWrite, luxi_client=_FakeLuxiClientForQuery) self.assertEqual(code, http.HTTP_OK) self.assertTrue(objects.QueryResponse.FromDict(data))
def TestOutOfBand(): """gnt-node power""" master = qa_config.GetMasterNode() node = qa_config.AcquireNode(exclude=master) master_name = master.primary node_name = node.primary full_node_name = qa_utils.ResolveNodeName(node) (oob_path, verify_path, data_path, exit_code_path) = _CreateOobScriptStructure() try: AssertCommand([ "gnt-cluster", "modify", "--node-parameters", "oob_program=%s" % oob_path ]) # No data, exit 0 _UpdateOobFile(exit_code_path, "0") AssertCommand(["gnt-node", "power", "on", node_name]) _AssertOobCall(verify_path, "power-on %s" % full_node_name) AssertCommand(["gnt-node", "power", "-f", "off", node_name]) _AssertOobCall(verify_path, "power-off %s" % full_node_name) # Power off on master without options should fail AssertCommand(["gnt-node", "power", "-f", "off", master_name], fail=True) # With force master it should still fail AssertCommand( ["gnt-node", "power", "-f", "--ignore-status", "off", master_name], fail=True) # Verify we can't transform back to online when not yet powered on AssertCommand(["gnt-node", "modify", "-O", "no", node_name], fail=True) # Now reset state AssertCommand([ "gnt-node", "modify", "-O", "no", "--node-powered", "yes", node_name ]) AssertCommand(["gnt-node", "power", "-f", "cycle", node_name]) _AssertOobCall(verify_path, "power-cycle %s" % full_node_name) # Those commands should fail as they expect output which isn't provided yet # But they should have called the oob helper nevermind AssertCommand(["gnt-node", "power", "status", node_name], fail=True) _AssertOobCall(verify_path, "power-status %s" % full_node_name) AssertCommand(["gnt-node", "health", node_name], fail=True) _AssertOobCall(verify_path, "health %s" % full_node_name) AssertCommand(["gnt-node", "health"], fail=True) # Correct Data, exit 0 _UpdateOobFile(data_path, serializer.DumpJson({"powered": True})) AssertCommand(["gnt-node", "power", "status", node_name]) _AssertOobCall(verify_path, "power-status %s" % full_node_name) _UpdateOobFile( data_path, serializer.DumpJson([["temp", "OK"], ["disk0", "CRITICAL"]])) AssertCommand(["gnt-node", "health", node_name]) _AssertOobCall(verify_path, "health %s" % full_node_name) AssertCommand(["gnt-node", "health"]) # Those commands should fail as they expect no data regardless of exit 0 AssertCommand(["gnt-node", "power", "on", node_name], fail=True) _AssertOobCall(verify_path, "power-on %s" % full_node_name) try: AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True) _AssertOobCall(verify_path, "power-off %s" % full_node_name) finally: AssertCommand(["gnt-node", "modify", "-O", "no", node_name]) AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True) _AssertOobCall(verify_path, "power-cycle %s" % full_node_name) # Data, exit 1 (all should fail) _UpdateOobFile(exit_code_path, "1") AssertCommand(["gnt-node", "power", "on", node_name], fail=True) _AssertOobCall(verify_path, "power-on %s" % full_node_name) try: AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True) _AssertOobCall(verify_path, "power-off %s" % full_node_name) finally: AssertCommand(["gnt-node", "modify", "-O", "no", node_name]) AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True) _AssertOobCall(verify_path, "power-cycle %s" % full_node_name) AssertCommand(["gnt-node", "power", "status", node_name], fail=True) _AssertOobCall(verify_path, "power-status %s" % full_node_name) AssertCommand(["gnt-node", "health", node_name], fail=True) _AssertOobCall(verify_path, "health %s" % full_node_name) AssertCommand(["gnt-node", "health"], fail=True) # No data, exit 1 (all should fail) _UpdateOobFile(data_path, "") AssertCommand(["gnt-node", "power", "on", node_name], fail=True) _AssertOobCall(verify_path, "power-on %s" % full_node_name) try: AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True) _AssertOobCall(verify_path, "power-off %s" % full_node_name) finally: AssertCommand(["gnt-node", "modify", "-O", "no", node_name]) AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True) _AssertOobCall(verify_path, "power-cycle %s" % full_node_name) AssertCommand(["gnt-node", "power", "status", node_name], fail=True) _AssertOobCall(verify_path, "power-status %s" % full_node_name) AssertCommand(["gnt-node", "health", node_name], fail=True) _AssertOobCall(verify_path, "health %s" % full_node_name) AssertCommand(["gnt-node", "health"], fail=True) # Different OOB script for node verify_path2 = qa_utils.UploadData(master.primary, "") oob_script = ("#!/bin/sh\n" "echo \"$@\" > %s\n") % verify_path2 oob_path2 = qa_utils.UploadData(master.primary, oob_script, mode=0700) try: AssertCommand([ "gnt-node", "modify", "--node-parameters", "oob_program=%s" % oob_path2, node_name ]) AssertCommand(["gnt-node", "power", "on", node_name]) _AssertOobCall(verify_path2, "power-on %s" % full_node_name) finally: AssertCommand([ "gnt-node", "modify", "--node-parameters", "oob_program=default", node_name ]) AssertCommand(["rm", "-f", oob_path2, verify_path2]) finally: AssertCommand( ["gnt-cluster", "modify", "--node-parameters", "oob_program="]) AssertCommand( ["rm", "-f", oob_path, verify_path, data_path, exit_code_path])
def testValidData(self): raw = serializer.DumpJson({}) self.assertEqual(node_daemon_setup.LoadData(raw), {})
def testValidData(self): raw = serializer.DumpJson({}) self.assertEqual(common.LoadData(raw, Exception), {})
# this case, err.message == err.args[0] == str(err) result = (False, str(err)) except errors.QuitGanetiException, err: # Tell parent to quit logging.info("Shutting down the node daemon, arguments: %s", str(err.args)) os.kill(self.noded_pid, signal.SIGTERM) # And return the error's arguments, which must be already in # correct tuple format result = err.args except Exception, err: logging.exception("Error in RPC call") result = (False, "Error while executing backend function: %s" % str(err)) return serializer.DumpJson(result) # the new block devices -------------------------- @staticmethod def perspective_blockdev_create(params): """Create a block device. """ (bdev_s, size, owner, on_primary, info, excl_stor) = params bdev = objects.Disk.FromDict(bdev_s) if bdev is None: raise ValueError("can't unserialize data!") return backend.BlockdevCreate(bdev, size, owner, on_primary, info, excl_stor)
def _VerifyRequest(req): req.success = True req.resp_status_code = http.HTTP_OK req.resp_body = serializer.DumpJson((True, req.post_data))
def __str__(self): # The protocol expects the JSON object to be sent as a single line. return serializer.DumpJson(self.data)
def RunNodeSetupCmd(cluster_name, node, basecmd, debug, verbose, use_cluster_key, ask_key, strict_host_check, port, data): """Runs a command to configure something on a remote machine. @type cluster_name: string @param cluster_name: Cluster name @type node: string @param node: Node name @type basecmd: string @param basecmd: Base command (path on the remote machine) @type debug: bool @param debug: Enable debug output @type verbose: bool @param verbose: Enable verbose output @type use_cluster_key: bool @param use_cluster_key: See L{ssh.SshRunner.BuildCmd} @type ask_key: bool @param ask_key: See L{ssh.SshRunner.BuildCmd} @type strict_host_check: bool @param strict_host_check: See L{ssh.SshRunner.BuildCmd} @type port: int @param port: The SSH port of the remote machine or None for the default @param data: JSON-serializable input data for script (passed to stdin) """ cmd = [basecmd] # Pass --debug/--verbose to the external script if set on our invocation if debug: cmd.append("--debug") if verbose: cmd.append("--verbose") logging.debug("Node setup command: %s", cmd) version = constants.DIR_VERSION all_cmds = [["test", "-d", os.path.join(pathutils.PKGLIBDIR, version)]] if constants.HAS_GNU_LN: all_cmds.extend([["ln", "-s", "-f", "-T", os.path.join(pathutils.PKGLIBDIR, version), os.path.join(pathutils.SYSCONFDIR, "ganeti/lib")], ["ln", "-s", "-f", "-T", os.path.join(pathutils.SHAREDIR, version), os.path.join(pathutils.SYSCONFDIR, "ganeti/share")]]) else: all_cmds.extend([["rm", "-f", os.path.join(pathutils.SYSCONFDIR, "ganeti/lib")], ["ln", "-s", "-f", os.path.join(pathutils.PKGLIBDIR, version), os.path.join(pathutils.SYSCONFDIR, "ganeti/lib")], ["rm", "-f", os.path.join(pathutils.SYSCONFDIR, "ganeti/share")], ["ln", "-s", "-f", os.path.join(pathutils.SHAREDIR, version), os.path.join(pathutils.SYSCONFDIR, "ganeti/share")]]) all_cmds.append(cmd) if port is None: port = netutils.GetDaemonPort(constants.SSH) family = ssconf.SimpleStore().GetPrimaryIPFamily() srun = ssh.SshRunner(cluster_name, ipv6=(family == netutils.IP6Address.family)) scmd = srun.BuildCmd(node, constants.SSH_LOGIN_USER, utils.ShellQuoteArgs( utils.ShellCombineCommands(all_cmds)), batch=False, ask_key=ask_key, quiet=False, strict_host_check=strict_host_check, use_cluster_key=use_cluster_key, port=port) tempfh = tempfile.TemporaryFile() try: tempfh.write(serializer.DumpJson(data)) tempfh.seek(0) result = utils.RunCmd(scmd, interactive=True, input_fd=tempfh) finally: tempfh.close() if result.failed: raise errors.OpExecError("Command '%s' failed: %s" % (result.cmd, result.fail_reason)) _WaitForSshDaemon(node, port, family)
def RunSshCmdWithStdin(cluster_name, node, basecmd, port, data, debug=False, verbose=False, use_cluster_key=False, ask_key=False, strict_host_check=False, ensure_version=False): """Runs a command on a remote machine via SSH and provides input in stdin. @type cluster_name: string @param cluster_name: Cluster name @type node: string @param node: Node name @type basecmd: string @param basecmd: Base command (path on the remote machine) @type port: int @param port: The SSH port of the remote machine or None for the default @param data: JSON-serializable input data for script (passed to stdin) @type debug: bool @param debug: Enable debug output @type verbose: bool @param verbose: Enable verbose output @type use_cluster_key: bool @param use_cluster_key: See L{ssh.SshRunner.BuildCmd} @type ask_key: bool @param ask_key: See L{ssh.SshRunner.BuildCmd} @type strict_host_check: bool @param strict_host_check: See L{ssh.SshRunner.BuildCmd} """ cmd = [basecmd] # Pass --debug/--verbose to the external script if set on our invocation if debug: cmd.append("--debug") if verbose: cmd.append("--verbose") if ensure_version: all_cmds = _EnsureCorrectGanetiVersion(cmd) else: all_cmds = [cmd] if port is None: port = netutils.GetDaemonPort(constants.SSH) srun = SshRunner(cluster_name) scmd = srun.BuildCmd(node, constants.SSH_LOGIN_USER, utils.ShellQuoteArgs( utils.ShellCombineCommands(all_cmds)), batch=False, ask_key=ask_key, quiet=False, strict_host_check=strict_host_check, use_cluster_key=use_cluster_key, port=port) tempfh = tempfile.TemporaryFile() try: tempfh.write(serializer.DumpJson(data)) tempfh.seek(0) result = utils.RunCmd(scmd, interactive=True, input_fd=tempfh) finally: tempfh.close() if result.failed: raise errors.OpExecError("Command '%s' failed: %s" % (result.cmd, result.fail_reason))
def testInvalidConfig(self): self._CreateValidConfigDir() # Missing version from config utils.WriteFile(self.config_path, data=serializer.DumpJson({})) self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True)
def testEmptyDict(self): raw = serializer.DumpJson({}) self.assertEqual(common.LoadData(raw, _DATA_CHECK), {})