Beispiel #1
0
        /// <summary>
        /// Gets the exception associated to the result code.
        /// </summary>
        /// <param name="code">The rc.</param>
        /// <returns>Exception, or null if none.</returns>
        public static Exception GetException(Code code)
        {
            string message = $"error came from RM server: {code}";

            switch (code)
            {
            case Code.Ok:
                return(null);

            case Code.Nodeexists:
                return(null);

            case Code.Unimplemented:
                return(new NotSupportedException(message));

            case Code.Badarguments:
                return(new ArgumentException(message));

            case Code.Authfailed:
            case Code.Noauth:
                return(new AuthenticationException(message));

            case Code.Connectionloss:
                return(new IOException(message));

            case Code.Operationtimeout:
            case Code.Sessionexpired:
                return(new TimeoutException(message));

            case Code.Marshallingerror:
                return(new SerializationException(message));

            case Code.Badversion:
            case Code.Invalidacl:
            case Code.Nochildrenforephemerals:
                return(new InvalidDataException(message));

            case Code.Notempty:
                return(new ArgumentException(message));

            case Code.Nonode:
                return(new KeyNotFoundException(message));

            case Code.Invalidcallback:
                return(new InvalidOperationException(message));

            case Code.Apierror:
            case Code.Systemerror:
            case Code.Unknown:
            case Code.Runtimeinconsistency:
            case Code.Datainconsistency:
            case Code.Sessionmoved:
                return(new SessionMovedException(message));

            default:
                return(new UnknownException(message));
            }
        }
            /// <summary>
            /// Tries to run the command associated to the node, and encoded in the byte[]
            /// </summary>
            /// <param name="node">the node representing who will run the command</param>
            /// <param name="data">the data encoding the command. data must be a string encoded in UTF8, where the string is a RM command. RM commands are "$/[command]?arguments".</param>
            /// <param name="txtime">the time of the tx which contains the command</param>
            /// <param name="xid">the id of the tx which contains the command</param>
            /// <returns>trus if the command was executed. False otherwise</returns>
            internal bool TryRunCommand(IPersistedData node, byte[] data, long txtime, long xid)
            {
                if (node == null)
                {
                    throw new ArgumentNullException(nameof(node));
                }

                bool   didrun = false;
                string path   = Node.BuildPath(node);

                if (data == null || !this.CanRunInThisReplica(path))
                {
                    return(didrun);
                }

                this.DoActionsOnApply(
                    txtime,
                    xid,
                    (t, i) =>
                {
                    didrun = true;

                    string requestedCommand = null;
                    object content          = null;
                    string command          = null;

                    try
                    {
                        string datastr      = Encoding.UTF8.GetString(data);
                        byte[] argumentdata = null;
                        int p = datastr.IndexOf('?');

                        if (p == -1)
                        {
                            command = datastr;
                        }
                        else
                        {
                            command      = datastr.Substring(0, p);
                            argumentdata = Encoding.UTF8.GetBytes(datastr.Substring(p + 1));
                        }

                        Code res = this.backend.RmCommands.RunCommandPath(command, argumentdata, null, null, out requestedCommand, out content);
                        Trace.TraceInformation("command {0} was run at {1} with result {2}. content={3}", command, this.thisCommandPath, res, content);
                    }
                    catch (Exception e)
                    {
                        Trace.TraceInformation("command {0} was run at {1} (content={2}) with exception {3}. ", command, this.thisCommandPath, content, e);
                    }
                },
                    false);

                return(didrun);
            }
Beispiel #3
0
        /// <summary>
        /// Gets the code.
        /// </summary>
        /// <param name="rc">The rc.</param>
        /// <returns>Code.</returns>
        public static Code GetCode(int rc)
        {
            Code code = Code.Unknown;

            if (Enum.IsDefined(typeof(Code), rc))
            {
                code = (Code)rc;
            }

            return(code);
        }
Beispiel #4
0
            /// <summary>
            /// runs the command from the path
            /// </summary>
            /// <param name="req">the request containing the command</param>
            /// <param name="session">the session this command was run from</param>
            /// <param name="lockList">the lock list of the invocation</param>
            /// <returns>the repsonse to the command</returns>
            internal virtual RequestResponse RunCommand(RequestCreate req, ClientSession session, ILockListTransaction lockList)
            {
                if (req == null)
                {
                    throw new ArgumentNullException(nameof(req));
                }

                if (session == null)
                {
                    throw new ArgumentNullException(nameof(session));
                }

                MutableStat stat = new MutableStat(new FirstStat(0, MutableStat.ConvertTime(DateTime.UtcNow), 0));

                // we require the digest 'Commander'
                if (!string.Equals(session.Auth.ClientDigest, CommanderDigest, StringComparison.InvariantCultureIgnoreCase))
                {
                    return(new RequestResponse()
                    {
                        Content = "not executed",
                        ResponsePath = "command",
                        ResultCode = (int)Code.Authfailed,
                        Stat = stat,
                    });
                }

                string requestedCommand;
                object content;
                Code   result = this.RunCommandPath(req.Path, req.Data, session, lockList, out requestedCommand, out content);

                return(new RequestResponse()
                {
                    Content = content,
                    ResponsePath = requestedCommand,
                    ResultCode = (int)result,
                    Stat = stat,
                });
            }
Beispiel #5
0
            /// <summary>
            /// runs the command encoded in the given path
            /// </summary>
            /// <param name="path">the command encoded in the path</param>
            /// <param name="data">optionally, the data associated with the request</param>
            /// <param name="session">Client session</param>
            /// <param name="lockList">the lock list of the invocation</param>
            /// <param name="requestedCommand">Requested command on return</param>
            /// <param name="content">More info on the command on return</param>
            /// <returns>code for possible error</returns>
            public Code RunCommandPath(string path, byte[] data, ClientSession session, ILockListTransaction lockList, out string requestedCommand, out object content)
            {
                if (path == null)
                {
                    throw new ArgumentNullException(nameof(path));
                }

                Code result = Code.Apierror;

                requestedCommand = "unknown";
                content          = "unknown command";

                if (!this.IsCommand(path))
                {
                    return(result);
                }

                string[] pieces = path.Split('/');

                if (pieces.Length >= 2)
                {
                    requestedCommand = pieces[1].ToLower();

                    if (!this.IsCommandAllowed(session, requestedCommand))
                    {
                        content = "command not allowed now";
                        return(Code.Apierror);
                    }

                    RingMasterEventSource.Log.RunCommand(requestedCommand);
                    this.backend.auditConsumer?.OnRunCommand(requestedCommand);

                    switch (requestedCommand)
                    {
                    case "getsetting":
                    {
                        content = RingMasterBackendCore.GetSetting(pieces[2]);
                        result  = Code.Ok;
                        break;
                    }

                    case "gc":
                    {
                        GC.Collect();
                        content = "GC scheduled";
                        result  = Code.Ok;
                        break;
                    }

                    case "takecheckpoint":
                    {
                        bool ok = this.backend.Factory.TakeCheckpoint();
                        content = string.Format("Checkpoint {0}scheduled", ok ? string.Empty : "not ");
                        result  = Code.Ok;
                        break;
                    }

                    case "buildtreefile":
                    {
                        string   commandResult;
                        string[] args = Encoding.UTF8.GetString(data).Split('|');
                        try
                        {
                            int             ver  = int.Parse(args[2]);
                            RequestResponse resp = this.backend.BuildTreeFile(nodepath: args[0], filename: args[1], version: ver);
                            commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed";
                            result        = (Code)resp.ResultCode;
                        }
                        catch (Exception e)
                        {
                            commandResult = "failed:" + e.Message;
                            result        = Code.Systemerror;
                        }

                        content = string.Format("buildtreefile: nodepath={0} filename={1} result={2}", args[0], args[1], commandResult);

                        break;
                    }

                    case "mount":
                    {
                        string   commandResult;
                        string[] args = Encoding.UTF8.GetString(data).Split('|');
                        try
                        {
                            RequestResponse resp = this.backend.Mount(nodepath: args[0], filename: args[1], allowremount: false);
                            commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed";
                            result        = (Code)resp.ResultCode;
                        }
                        catch (Exception e)
                        {
                            commandResult = "failed:" + e.Message;
                            result        = Code.Unknown;
                        }

                        content = string.Format("mount: nodepath={0} filename={1} result={2}", args[0], args[1], commandResult);

                        break;
                    }

                    case "remountroot":
                    {
                        string commandResult;
                        string filepath = Encoding.UTF8.GetString(data);
                        try
                        {
                            RequestResponse resp = this.backend.Mount(nodepath: "/", filename: filepath, allowremount: true, mountRoot: true);
                            commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed";
                            result        = (Code)resp.ResultCode;
                        }
                        catch (Exception e)
                        {
                            commandResult = "failed:" + e.Message;
                            result        = Code.Unknown;
                        }

                        content = string.Format("mountroot: nodepath=/ filename={0} result={1}", filepath, commandResult);

                        break;
                    }

                    case "unmount":
                    {
                        string   commandResult;
                        string[] args = Encoding.UTF8.GetString(data).Split('|');

                        try
                        {
                            RequestResponse resp = this.backend.Unmount(args[0]);
                            commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed";
                            result        = (Code)resp.ResultCode;
                        }
                        catch (Exception e)
                        {
                            commandResult = "failed:" + e.Message;
                            result        = Code.Unknown;
                        }

                        content = string.Format("mount: nodepath={0} result={1}", args[0], commandResult);

                        break;
                    }

                    case "remount":
                    {
                        string   commandResult;
                        string[] args = Encoding.UTF8.GetString(data).Split('|');
                        try
                        {
                            RequestResponse resp = this.backend.Mount(nodepath: args[0], filename: args[1], allowremount: true);
                            commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed";
                            result        = (Code)resp.ResultCode;
                        }
                        catch (Exception e)
                        {
                            commandResult = "failed:" + e.Message;
                            result        = Code.Unknown;
                        }

                        content = string.Format("remount: nodepath={0} result={1}", args[0], commandResult);

                        break;
                    }

                    case "measure":
                    {
                        StringBuilder sb = new StringBuilder();

                        Dictionary <string, object> results;
                        try
                        {
                            this.backend.CanMeasure = true;
                            ((IUnsafeTreeAccess)this.backend).LockRootNoSync();

                            results = this.backend.MeasureFullTree(PlatformHelper.ProcessorCount, maxTimeForMeasurementInMillis);
                        }
                        finally
                        {
                            this.backend.CanMeasure = false;
                            ((IUnsafeTreeAccess)this.backend).ReleaseRoot();
                        }

                        if (results != null)
                        {
                            foreach (KeyValuePair <string, object> line in results)
                            {
                                string text;
                                string child_i = line.Key;

                                Exception e = line.Value as Exception;

                                if (e != null)
                                {
                                    text = string.Format("Metrics for {0}: {1}", child_i, e);
                                }
                                else
                                {
                                    ulong metrics = (ulong)line.Value;
                                    text = string.Format("Metrics for {0} is {1:X}", child_i, metrics);
                                }

                                sb.AppendLine(text);
                            }
                        }

                        content = sb.ToString();
                        result  = Code.Ok;
                        break;
                    }

                    case "getlocaltime":
                    {
                        content = string.Format("local time: " + DateTime.UtcNow.ToString("o"));
                        result  = Code.Ok;
                        break;
                    }

                    case "getcommands":
                    {
                        content = this.GetCommands();
                        result  = Code.Ok;
                        break;
                    }

                    case "trace":
                    {
                        string message = "<null>";
                        if (data != null)
                        {
                            message = Encoding.UTF8.GetString(data);
                        }

                        content = "traced: " + message;
                        result  = Code.Ok;
                        Trace.TraceInformation(message);
                        break;
                    }

                    case "failoverinto":
                    {
                        string replica = "<any>";
                        if (data != null)
                        {
                            replica = Encoding.UTF8.GetString(data);
                        }

                        content = "Failing over into: " + replica + " in 10 seconds";
                        this.backend.FailoverInto(replica, 10000);
                        result = Code.Ok;
                        break;
                    }

                    case "downloadurlintolocation":
                    {
                        string arguments = null;

                        if (data != null)
                        {
                            arguments = Encoding.UTF8.GetString(data);
                        }

                        // we run this one command asynchronously with any potential replication
                        bool hasStarted = this.backend.DownloadUrlIntoLocation(arguments);
                        if (hasStarted)
                        {
                            content = string.Format("DownloadUrlIntoLocation started arguments={0}", arguments);
                            result  = Code.Ok;
                        }
                        else
                        {
                            content = string.Format("DownloadUrlIntoLocation functionality is not implemented");
                            result  = Code.Unimplemented;
                        }

                        break;
                    }

                    case "poisonpill":
                    {
                        string spec = null;

                        if (data != null)
                        {
                            spec = Encoding.UTF8.GetString(data);
                        }

                        RequestResponse res = this.GeneratePoisonPill(spec, session, lockList);
                        if (res != null)
                        {
                            content = string.Format("poison pill executed. Result = {0}", res.Content);
                            result  = (Code)res.ResultCode;
                        }
                        else
                        {
                            content = "Poison pills not allowed from config";
                            result  = Code.Authfailed;
                        }

                        break;
                    }

                    case "importcheckpoint":
                    {
                        string location = null;

                        if (data != null)
                        {
                            location = Encoding.UTF8.GetString(data);
                        }

                        RingMasterThreadPool.Instance.QueueUserWorkItem(_ =>
                            {
                                Thread.Sleep(5000);
                                bool ok = this.backend.ImportCheckpoint(location);
                                Trace.TraceWarning("Import checkpoint {0}: {1}", location, ok ? "Succeeded" : "Failed");
                                if (ok)
                                {
                                    Trace.TraceWarning("This instance will die now");
                                    Environment.Exit(1);
                                }
                            });

                        content = "Importing checkpoint " + location + "file and restarting";
                        result  = Code.Ok;

                        break;
                    }

                    case "getstats":
                    {
                        lock (this.SyncObject)
                        {
                            content = string.Format("PersistentData.TotalNodes={0} PersistentData.TotalData={1} EphemeralData.TotalNodes={2} EphemeralData.TotalData={3}", this.backend.Factory.TotalNodes, this.backend.Factory.TotalData, this.backend.EphemeralFactory.TotalNodes, this.backend.EphemeralFactory.TotalData);
                        }

                        result = Code.Ok;

                        break;
                    }

                    case "dumpnodes":
                    {
                        bool scanEphemerals = pieces.Length > 2 && string.Equals(pieces[2], "scanephemerals", StringComparison.InvariantCultureIgnoreCase);
                        content = this.backend.Factory.DumpAllNodesForDebug();

                        if (scanEphemerals)
                        {
                            lock (this.SyncObject)
                            {
                                content = content + Environment.NewLine + "---Eph:" + Environment.NewLine + this.backend.ScanForEphemeral() + Environment.NewLine + "---";
                            }
                        }

                        result = Code.Ok;

                        break;
                    }

                    case "getverbositylevel":
                    {
                        content = "verbosity level is " + this.backend.GetVerbosityLevel();
                        result  = Code.Ok;
                        break;
                    }

                    case "setverbositylevel":
                    {
                        int vlev = int.Parse(pieces[2]);
                        content = string.Format("verbosity level was {0} now is set to {1}", this.backend.GetVerbosityLevel(), vlev);
                        this.backend.SetupVerbosityLevel(vlev);
                        result = Code.Ok;
                        break;
                    }

                    case "cleanuprsl":
                    {
                        RingMasterThreadPool.Instance.QueueUserWorkItem(_ =>
                            {
                                Thread.Sleep(10000);
                                Trace.TraceWarning("This instance will wipe its state {0} now", this.backend.GetType().Name);
                                this.backend.Factory.WipeAllDataAndShutdown();
                                Trace.TraceWarning("This instance will die now");
                                Environment.Exit(1);
                            });
                        content = "cleaning up RSL folder and restarting";
                        result  = Code.Ok;
                        break;
                    }

                    case "restart":
                    {
                        uint waitMS = 10000;

                        if (pieces.Length > 2)
                        {
                            if (!uint.TryParse(pieces[2], out waitMS))
                            {
                                waitMS = 10000;
                            }
                        }

                        // 2 minutes max wait
                        waitMS = Math.Min(waitMS, 120000);

                        RingMasterThreadPool.Instance.QueueUserWorkItem(_ =>
                            {
                                Thread.Sleep((int)waitMS);
                                Trace.TraceInformation("This instance will stop {0} now", this.backend.GetType().Name);
                                this.backend.Stop();

                                Thread.Sleep(2000);
                                Trace.TraceInformation("This instance will restart {0} now", this.backend.GetType().Name);
                                this.backend.Start(CancellationToken.None);
                            });

                        content = "restarted in " + waitMS + " miliseconds";
                        result  = Code.Ok;
                        break;
                    }

                    case "die":
                    {
                        RingMasterThreadPool.Instance.QueueUserWorkItem(_ =>
                            {
                                Thread.Sleep(10000);
                                Trace.TraceWarning("This instance will die now");
                                Environment.Exit(1);
                            });

                        if (pieces.Length > 2)
                        {
                            this.backend.FailoverInto(pieces[2]);
                        }

                        content = "die in 10 seconds";
                        result  = Code.Ok;
                        break;
                    }
                    }
                }

                return(result);
            }
Beispiel #6
0
        /// <summary>
        /// Processes a request in the backend
        /// </summary>
        /// <param name="backend">Backend for the operation</param>
        /// <param name="session">Session for the operation</param>
        /// <param name="request">Request to execute</param>
        /// <param name="expectedResponseCode">Expected response code for the request</param>
        private static void ProcessRequest(RingMasterBackendCore backend, ClientSession session, IRingMasterBackendRequest request, Code expectedResponseCode)
        {
            var evt = ManualResetEventPool.InstancePool.GetOne();

            backend.ProcessMessage(
                request,
                session,
                (response, ex) =>
            {
                response.ResultCode.Should().Be((int)expectedResponseCode);
                evt.Set();
            });

            ManualResetEventPool.InstancePool.WaitOneAndReturn(ref evt);
        }
Beispiel #7
0
 /// <summary>
 /// Sets an ACL on a node in the backend
 /// </summary>
 /// <param name="backend">Backend for the operation</param>
 /// <param name="session">Session for the operation</param>
 /// <param name="nodePath">Path of the node</param>
 /// <param name="acls">Data to set</param>
 /// <param name="expectedResponseCode">Expected result code of the operation</param>
 private static void SetAcl(RingMasterBackendCore backend, ClientSession session, string nodePath, List <Acl> acls, Code expectedResponseCode)
 {
     ProcessRequest(backend, session, new RequestSetAcl(nodePath, null, acls, -1, null), expectedResponseCode);
 }
Beispiel #8
0
 /// <summary>
 /// Sets data on a node in the backend
 /// </summary>
 /// <param name="backend">Backend for the operation</param>
 /// <param name="session">Session for the operation</param>
 /// <param name="nodePath">Path of the node</param>
 /// <param name="data">Data to set</param>
 /// <param name="expectedResponseCode">Expected result code of the operation</param>
 private static void SetData(RingMasterBackendCore backend, ClientSession session, string nodePath, byte[] data, Code expectedResponseCode)
 {
     ProcessRequest(backend, session, new RequestSetData(nodePath, null, data, -1, null), expectedResponseCode);
 }
Beispiel #9
0
 /// <summary>
 /// Creates a new node in the backend
 /// </summary>
 /// <param name="backend">Backend to create the node in</param>
 /// <param name="session">Session to use for creating the node</param>
 /// <param name="nodePath">Path of the node</param>
 /// <param name="data">Data for the node</param>
 /// <param name="expectedResponseCode">Expected response for the operation</param>
 /// <param name="createMode">Creation mode</param>
 /// <param name="acls">ACLs for the node</param>
 private static void CreateNode(RingMasterBackendCore backend, ClientSession session, string nodePath, byte[] data, Code expectedResponseCode, CreateMode createMode = CreateMode.Persistent, List <Acl> acls = null)
 {
     ProcessRequest(backend, session, new RequestCreate(nodePath, null, data, acls, createMode, null), expectedResponseCode);
 }
Beispiel #10
0
        /// <summary>
        /// Creates a new node with random characters in the node path
        /// </summary>
        /// <param name="backend">The backend to create the node in</param>
        /// <param name="session">The session to use for the create</param>
        /// <param name="segments">Amount of path segments</param>
        /// <param name="segmentLength">Length of each path segment</param>
        /// <param name="expectedResponseCode">Expected response code for the create request</param>
        /// <param name="endingSegmentLength">If non-zero, will add an extra segment on the end with the specified length</param>
        /// <param name="createMode">Mode for creating the node</param>
        private static void CreateNode(RingMasterBackendCore backend, ClientSession session, int segments, int segmentLength, Code expectedResponseCode, int endingSegmentLength = 0, CreateMode createMode = CreateMode.Persistent)
        {
            var stringBuilder = new StringBuilder();

            for (int i = 0; i < segments; ++i)
            {
                stringBuilder.AppendFormat("/{0}", CreateRandomString(segmentLength));
            }

            if (endingSegmentLength > 0)
            {
                stringBuilder.AppendFormat("/{0}", CreateRandomString(endingSegmentLength));
            }

            CreateNode(backend, session, stringBuilder.ToString(), null, expectedResponseCode, createMode);
        }