Ejemplo n.º 1
0
        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)))
Ejemplo n.º 2
0
 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))
Ejemplo n.º 3
0
 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")
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
 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))
Ejemplo n.º 6
0
 def encode_string(self, message):
     return (serializer.DumpJson(message) +
             hv_kvm.QmpConnection._MESSAGE_END_TOKEN)
Ejemplo n.º 7
0
    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))
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
    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))
Ejemplo n.º 11
0
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), {})
Ejemplo n.º 13
0
 def testValidData(self):
     raw = serializer.DumpJson({})
     self.assertEqual(common.LoadData(raw, Exception), {})
Ejemplo n.º 14
0
            # 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)
Ejemplo n.º 15
0
 def _VerifyRequest(req):
     req.success = True
     req.resp_status_code = http.HTTP_OK
     req.resp_body = serializer.DumpJson((True, req.post_data))
Ejemplo n.º 16
0
 def __str__(self):
     # The protocol expects the JSON object to be sent as a single line.
     return serializer.DumpJson(self.data)
Ejemplo n.º 17
0
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)
Ejemplo n.º 18
0
Archivo: ssh.py Proyecto: dimara/ganeti
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))
Ejemplo n.º 19
0
 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)
Ejemplo n.º 20
0
 def testEmptyDict(self):
     raw = serializer.DumpJson({})
     self.assertEqual(common.LoadData(raw, _DATA_CHECK), {})