/// <summary> /// Called by a connection when it has received an intact and complete message in wire-format. /// Parses the supplied byte-array to generate a typed message for processing. /// On return from this routine the connection is free to overwrite the buffer contents. /// /// </summary> /// <param name="conn">Connection (think TCP to specific network agent) on which message arrived.</param> /// <param name="buff">Buffer encoding the message.</param> /// <param name="offset">Offset to start of message in the supplied buffer.</param> /// <param name="length">Length of message encoding in supplied buffer</param> public void ReceiveMessage(Connection conn, MessageTypes messageType, byte[] buff, int offset, int length) { bool unblock = false; switch (messageType) { case MessageTypes.MessageTypeAck: { conn.MessageAck.InitFromNetBytes(buff, 0); //Console.WriteLine("SendParallel rcv MessageAck({0},{1})", conn.MessageAck.SeqNo, conn.MessageAck.Result); if (conn.MessageAck.Result != (uint)OktoResultCodes.OKTO_RESULT_SUCCESS) { string msg = String.Format("ACK err: conn {0} ResultCode {1} subytpe {2}", conn.HostName, conn.MessageAck.Result, (MessageTypes)conn.MessageAck.SubType); Console.WriteLine(msg); } MessageTypes AckSubType = (MessageTypes)conn.MessageAck.SubType; unblock = DowndatePendingReplies(AckSubType, conn.MessageAck.SeqNo); conn.BeginReceive(); break; } case MessageTypes.MessageTypeStatsReplyDelta: { MessageStatsReplyDelta mStatsDelta = MessageStatsReplyDelta.CreateFromNetBytes(conn.receiveBuffer, 0, conn, this); //Console.WriteLine("ReceiveRaw MessageStatsReplyDelta({0},{1})", mStatsDelta.SeqNo, mStatsDelta.Result); ulong intervalUSecs = mStatsDelta.IntervalUSecs; foreach (KeyValuePair<ushort, FlowStats> kvp in mStatsDelta.dictFlowStats) { RAP rap = conn.ListRap[kvp.Key]; FlowStats flowStats = kvp.Value; rap.SetFlowStats(flowStats); } unblock = DowndatePendingReplies(messageType, mStatsDelta.SeqNo); conn.BeginReceive(); break; } case MessageTypes.MessageTypeNameToStringSidReply: { MessageVmNameToStringSidReply mNameToSidReply = MessageVmNameToStringSidReply.CreateFromNetBytes(conn.receiveBuffer, 0); string vmKey = conn.HostName + "." + mNameToSidReply.VmName; lock (DictVmNameToSid) { if (!DictVmNameToSid.ContainsKey(vmKey)) DictVmNameToSid.Add(vmKey, mNameToSidReply.SidString); } unblock = DowndatePendingReplies(messageType, mNameToSidReply.SeqNo); conn.BeginReceive(); break; } case MessageTypes.MessageTypeAlert: { MessageAlert mAlert = MessageAlert.CreateFromNetBytes(conn.receiveBuffer, 0); //Console.WriteLine("ReceiveMessage rx alert {0}", mAlert); // Push message to the sequential work queue for upcall into Policy Module. RcWorkItem rcWorkItem = new RcWorkItem(RcWorkItemType.Alert, mAlert, 0, conn); RcWorkQueue.Enqueue(rcWorkItem); conn.BeginReceive(); break; } case MessageTypes.MessageTypeIoFlowStatsReply: { MessageIoFlowStatsReply mIoFlowStats = MessageIoFlowStatsReply.CreateFromNetBytes(conn.receiveBuffer, 0); foreach (IoFlowMessageParams stats in mIoFlowStats.ListParams) conn.DictIoFlows[stats.FlowId].RapD.SetIoFlowStats(stats.ParameterString); unblock = DowndatePendingReplies(messageType, mIoFlowStats.SeqNo); conn.BeginReceive(); break; } case MessageTypes.MessageTypeNameToStringSidBatchReply: { MessageVmNameToStringSidBatchReply mNameToSidReply = MessageVmNameToStringSidBatchReply.CreateFromNetBytes(conn.receiveBuffer, 0); lock (DictVmNameToSid) { foreach (KeyValuePair<string, string> kvp in mNameToSidReply.DictVmNameToSid) { string vmKey = MakeVmKey(conn.hostName, kvp.Key); if (!DictVmNameToSid.ContainsKey(vmKey)) { Console.WriteLine("sidBatchReply adding ({0},{1}", vmKey, kvp.Value); DictVmNameToSid.Add(vmKey, kvp.Value); } } } unblock = DowndatePendingReplies(messageType, mNameToSidReply.SeqNo); conn.BeginReceive(); break; } default: { string msg = string.Format("SendParallel: unexpected message type {0}", messageType); throw new ApplicationException(msg); } //break; } }
/// <summary> /// Called to initialize the list of RAPS. /// </summary> /// <param name="listEndpoints">List of (addr,NodeDetails) pairs typically ex ParseConfigFile().</param> /// <returns></returns> public List<RAP> InitListRap(List<Endpoint> listEndpoints) { ValidateState(RateControllerState.Init, "InitTrafficMatrix"); if (listEndpoints.Count == 0) throw new ArgumentException("err: input cannot have zero length."); if (IsMatrixGenerated) throw new ApplicationException("err: InitListRap() can only be called once."); matrixDimension = listEndpoints.Count; // // Setup TCP connection to each network agent and then register with each network agent. // Each row in table represents a VM, so all entries in a row use the same network agent. // foreach (Endpoint ep in listEndpoints) { Connection conn; string serverName = ep.FilterServer; if (ep.IsOktofsH && !AgentNameToConn.TryGetValue(serverName, out conn)) { conn = new Connection(this, serverName, 0, AgentPort); AgentNameToConn.Add(serverName, conn); MessageRegister mRegister = new MessageRegister(++SeqNo, TenantId, AlertVec); const MessageTypes replyType = MessageTypes.MessageTypeRegisterAck; const int typeIndex = (int)replyType; lock (LockPendingReplies[typeIndex]) { // Registration messages actually sent sequentially, but using parallel paradigm. // This because SendSequential will break if interleaved with alerts. SendParallel(conn, mRegister.Serialize, replyType, mRegister.SeqNo); conn.BeginReceive(); WaitForParallelReplies(replyType, Parameters.DEFAULT_MESSAGE_TIMEOUT_MS); } } } foreach (Endpoint ep in listEndpoints) { Connection conn; string hyperVName = ep.HyperVserver; // // Connections to remote OktofsAgent socket apps. // if ((ep.IsOktofsC || ep.IsOktofsH) && !AgentNameToConn.TryGetValue(hyperVName, out conn)) { conn = new Connection(this, hyperVName, 0, AgentPort); AgentNameToConn.Add(hyperVName, conn); MessageRegister mRegister = new MessageRegister(++SeqNo, TenantId, AlertVec); const MessageTypes replyType = MessageTypes.MessageTypeRegisterAck; const int typeIndex = (int)replyType; lock (LockPendingReplies[typeIndex]) { // Registration messages actually sent sequentially, but using parallel paradigm. // This because SendSequential will break if interleaved with alerts. SendParallel(conn, mRegister.Serialize, replyType, mRegister.SeqNo); conn.BeginReceive(); WaitForParallelReplies(replyType, Parameters.DEFAULT_MESSAGE_TIMEOUT_MS); } } // // Connections to remote IoFlow socket apps. // if (ep.IsIoFlowD && !IoFlowNameToConn.TryGetValue(hyperVName, out conn)) { conn = new Connection(this, hyperVName, 0, Parameters.IOFLOWAGENT_TCP_PORT_NUMBER); IoFlowNameToConn.Add(hyperVName, conn); MessageRegister mRegister = new MessageRegister(++SeqNo, TenantId, AlertVec); const MessageTypes replyType = MessageTypes.MessageTypeRegisterAck; const int typeIndex = (int)replyType; lock (LockPendingReplies[typeIndex]) { // Registration messages actually sent sequentially, but using parallel paradigm. // This because SendSequential will break if interleaved with alerts. SendParallel(conn, mRegister.Serialize, replyType, mRegister.SeqNo); conn.BeginReceive(); WaitForParallelReplies(replyType, Parameters.DEFAULT_MESSAGE_TIMEOUT_MS); } } } // // NT account names in the config file need to be translated into SIDs. // This because SIDS in config files are too unfriendly and error-prone. // Names and SIDS from an untrusted NT domain are legit, e.g. VMs in a workgroup. // Such names cannot be resolved locally, so ask the Hyper-V server to do it for us. // If the Hyper-V server is unknown, e.g. H-only config., config must use valid SIDS. // // // Ask Hyper-V servers to translate VM names to SIDS in case VM in untrusted NT domain. // Dictionary<string, Connection> DictIoFlowVmLookup = new Dictionary<string, Connection>(); // Any not resolved by Oktofs agent. foreach (Endpoint nDtls in listEndpoints) { // Nothing to do if config file claims to know the correct string SID. if (nDtls.SidOrAccount.ToUpper().StartsWith("S-1-5-")) continue; // Only attempt SID translation at Hyper-V servers i.e. at point C in stack. if (!nDtls.Tag.ToUpper().Contains("-VM-SHARE-VOL") && !nDtls.Tag.ToUpper().Contains("-VM-FILE-VOL") ) continue; // Maybe we already queried this SID with the given Hyper-V server. string VmKey = MakeVmKey(nDtls.HyperVserver, nDtls.SidOrAccount); if (DictVmNameToSid.ContainsKey(VmKey)) { nDtls.StringSid = DictVmNameToSid[VmKey]; continue; } // Ask remote Hyper-V server for the sid: our DC may not trust VMs NT domain (e.g. workgroup). Connection conn = null; if (AgentNameToConn.TryGetValue(nDtls.HyperVserver, out conn)) { MessageNameToStringSidQuery mQuerySid = new MessageNameToStringSidQuery(++SeqNo, (uint)nDtls.SidOrAccount.Length, nDtls.SidOrAccount); const MessageTypes replyType = MessageTypes.MessageTypeNameToStringSidReply; const int typeIndex = (int)replyType; lock (LockPendingReplies[typeIndex]) { // For now (dbg) VmName to SID translation messages actually sent sequentially. SendParallel(conn, mQuerySid.Serialize, replyType, mQuerySid.SeqNo); WaitForParallelReplies(replyType, Parameters.DEFAULT_MESSAGE_TIMEOUT_MS); } // Fatal error if SidQuery failed to update DictVmNameToSid. if (!DictVmNameToSid.ContainsKey(VmKey)) throw new ApplicationException(String.Format("Panic: failed to get SID for {0}", VmKey)); continue; } // If we are using IoFlowAgent but not an OktoFsAgent try the IoFlowagent for the sid. if (IoFlowNameToConn.TryGetValue(nDtls.HyperVserver, out conn)) { if (!DictIoFlowVmLookup.ContainsKey(nDtls.SidOrAccount)){ DictIoFlowVmLookup.Add(nDtls.SidOrAccount, conn); } } } // Any unresolved Vm-to-sid lookups are attempted via IoFlow network agent in batches in parallel. if (DictIoFlowVmLookup.Count != 0) { const MessageTypes replyType = MessageTypes.MessageTypeNameToStringSidBatchReply; const int typeIndex = (int)replyType; lock (LockPendingReplies[typeIndex]) { foreach (Connection conn in IoFlowNameToConn.Values) { List<string> listVmNames = new List<string>(); foreach (KeyValuePair<string, Connection> kvp in DictIoFlowVmLookup) if (kvp.Value == conn) listVmNames.Add(kvp.Key); MessageNameToStringSidBatchQuery bq = new MessageNameToStringSidBatchQuery(++SeqNo, listVmNames); SendParallel(conn, bq.Serialize, replyType, bq.SeqNo); } WaitForParallelReplies(replyType, Parameters.DEFAULT_MESSAGE_TIMEOUT_MS); } // lock } // // Set the SID for each RAP. Any trusted account names will be translated to SIDS at agent. // foreach (Endpoint nDtls in listEndpoints) { if (nDtls.SidOrAccount.ToUpper().StartsWith("S-1-5-")) nDtls.StringSid = nDtls.SidOrAccount.ToUpper(); else if (nDtls.Tag.ToUpper().Equals("H-HYPERV-VOL")) nDtls.StringSid = nDtls.SidOrAccount.ToUpper(); else nDtls.StringSid = DictVmNameToSid[MakeVmKey(nDtls.HyperVserver, nDtls.SidOrAccount)]; } // // Initialize RAPs and RAP lookup, and point Connections at each RAP. // More than one Connection may ref a RAP (e.g. Oktofs and IoFlow both). // Note RAPs not yet bound to flows. // ListRap = new List<RAP>(); for (int i = 0; i < listEndpoints.Count; i++) { string locServername = listEndpoints[i].FilterServer; RAP rap = new RAP(listEndpoints[i], listEndpoints[i]); Connection conn = null; if (AgentNameToConn.TryGetValue(locServername, out conn)) conn.ListRap.Add(rap); if (IoFlowNameToConn.TryGetValue(locServername, out conn)) conn.ListRap.Add(rap); DictEpKeyToRap[listEndpoints[i].Key] = rap; ListRap.Add(rap); } Console.WriteLine("InitListRap made {0} RAPs", ListRap.Count); return ListRap; }
/// <summary> /// Called by a connection when it has received an intact and complete message in wire-format. /// Parses the supplied byte-array to generate a typed message for processing. /// On return from this routine the connection is free to overwrite the buffer contents. /// /// </summary> /// <param name="conn">Connection (think TCP to specific network agent) on which message arrived.</param> /// <param name="buff">Buffer encoding the message.</param> /// <param name="offset">Offset to start of message in the supplied buffer.</param> /// <param name="length">Length of message encoding in supplied buffer</param> public void ReceiveMessage(Connection conn, MessageTypes messageType, byte[] buff, int offset, int length) { switch (messageType) { case MessageTypes.MessageTypeRegister: { MessageRegister msgRegister = MessageRegister.CreateFromNetBytes(buff, offset); TenantId = msgRegister.TenantId; client.CallbackMessageRegister(conn, msgRegister.TenantId, msgRegister.AlertVec); MessageAck ack = new MessageAck(msgRegister.SeqNo, MessageTypes.MessageTypeRegisterAck, (uint)OktoResultCodes.OKTO_RESULT_SUCCESS); SendSynchronous(conn, ack.Serialize); conn.BeginReceive(); break; } case MessageTypes.MessageTypeIoFlowCreate: { MessageIoFlowCreate msgFlowc = MessageIoFlowCreate.CreateFromNetBytes(buff, offset); lock (LockLocal) { foreach (IoFlowMessageParams flowc in msgFlowc.ListParams) { Console.WriteLine("Agent MessageIoFlowCreate({0},{1})", flowc.FlowId, flowc.ParameterString); if (DictFlowCreateParams.ContainsKey(flowc.FlowId)) throw new ArgumentOutOfRangeException(string.Format("Agent flowc dup FlowId {0}", flowc.FlowId)); DictFlowCreateParams.Add(flowc.FlowId, flowc); } } MessageAck ack = new MessageAck(msgFlowc.SeqNo, MessageTypes.MessageTypeIoFlowCreateAck, (uint)OktoResultCodes.OKTO_RESULT_SUCCESS); SendSynchronous(conn, ack.Serialize); conn.BeginReceive(); break; } case MessageTypes.MessageTypeRapFsCreate: { MessageRapFsCreate msgRapc = MessageRapFsCreate.CreateFromNetBytes(buff, offset); lock (LockLocal) { foreach (MsgRapArg rapc in msgRapc.ListMsgRapArg) { Console.WriteLine("Agent MessageRapFsCreate({0},{1},{2})", rapc.FlowId, rapc.stringSid, rapc.ShareOrVolume); if (!DictFlowCreateParams.ContainsKey(rapc.FlowId)) throw new ArgumentOutOfRangeException(string.Format("Agent rapc invalid FlowId {0}", rapc.FlowId)); if (DictRapCreateParams.ContainsKey(rapc.FlowId)) throw new ArgumentOutOfRangeException(string.Format("Agent rapc dup FlowId {0}", rapc.FlowId)); if (!DictFlowCreateParams.ContainsKey(rapc.FlowId)) throw new ArgumentOutOfRangeException(string.Format("Agent rapc unmatched FlowId {0}", rapc.FlowId)); DictRapCreateParams.Add(rapc.FlowId, rapc); } } // // Params look reasonable and FlowCreate and RapCreate match up. // Now we can invite the client to create its IoFlows. // OktoResultCodes result = client.CallbackIoFlowCreate(DictFlowCreateParams, DictRapCreateParams); MessageAck ack = new MessageAck(msgRapc.SeqNo, MessageTypes.MessageTypeRapFsCreateAck, (uint)result); SendSynchronous(conn, ack.Serialize); conn.BeginReceive(); break; } case MessageTypes.MessageTypeStatsZero: { MessageStatsZero msgZero = MessageStatsZero.CreateFromNetBytes(buff, offset); OktoResultCodes result = client.CallbackMessageStatsZero(); MessageAck ack = new MessageAck(msgZero.SeqNo, MessageTypes.MessageTypeStatsZeroAck, (uint)result); SendSynchronous(conn, ack.Serialize); conn.BeginReceive(); break; } case MessageTypes.MessageTypeIoFlowUpdate: { MessageIoFlowUpdate msgFlowc = MessageIoFlowUpdate.CreateFromNetBytes(buff, offset); lock (LockLocal) { foreach (IoFlowMessageParams flowu in msgFlowc.ListParams) if (!DictFlowCreateParams.ContainsKey(flowu.FlowId)) throw new ArgumentOutOfRangeException(string.Format("Agent flowu invalid FlowId {0}", flowu.FlowId)); } OktoResultCodes result = client.CallbackMessageIoFlowUpdate(msgFlowc.ListParams); MessageAck ack = new MessageAck(msgFlowc.SeqNo, MessageTypes.MessageTypeIoFlowUpdateAck, (uint)result); SendSynchronous(conn, ack.Serialize); conn.BeginReceive(); break; } case MessageTypes.MessageTypeTenantDelete: { MessageTenantDelete msgTend = MessageTenantDelete.CreateFromNetBytes(buff, offset); OktoResultCodes result = client.CallbackMessageTenantDelete(); MessageAck ack = new MessageAck(msgTend.SeqNo, MessageTypes.MessageTypeTenantDeleteAck, (uint)result); SendSynchronous(conn, ack.Serialize); conn.BeginReceive(); break; } case MessageTypes.MessageTypeIoFlowStatsQuery: { MessageIoFlowStatsQuery msgStatsQ = MessageIoFlowStatsQuery.CreateFromNetBytes(buff, offset); List<IoFlowMessageParams> listStats = client.CallbackMessageIoFlowStatsQuery(); lock (LockLocal) { foreach (IoFlowMessageParams stats in listStats) if (!DictFlowCreateParams.ContainsKey(stats.FlowId)) throw new ArgumentOutOfRangeException(string.Format("Stats reply invalid FlowId {0}", stats.FlowId)); } MessageIoFlowStatsReply msgStatsReply = new MessageIoFlowStatsReply(msgStatsQ.SeqNo, listStats, TenantId); SendSynchronous(conn, msgStatsReply.Serialize); conn.BeginReceive(); break; } case MessageTypes.MessageTypeNameToStringSidBatchQuery: { MessageNameToStringSidBatchQuery msgSidBatchQuery = MessageNameToStringSidBatchQuery.CreateFromNetBytes(buff, offset); Dictionary<string, string> DictVmNameToSid = new Dictionary<string, string>(); System.Security.Principal.SecurityIdentifier sid = null; foreach (string vmName in msgSidBatchQuery.ListVmNames) { //XXXET: following two lines are for account names of type europe\ sid = VmSID.GetVMnameSid(vmName); if (sid == null) { try { NTAccount ntaccount = new NTAccount(vmName); sid = (SecurityIdentifier)ntaccount.Translate(typeof(SecurityIdentifier)); } catch (Exception e) { Debug.Assert(0 == 1, e.Message); } } Console.WriteLine("MessageTypeNameToStringSidBatchQuery: {0} sid {1}", vmName, sid.ToString()); DictVmNameToSid.Add(vmName, sid.ToString()); } MessageVmNameToStringSidBatchReply msgSidReply = new MessageVmNameToStringSidBatchReply(msgSidBatchQuery.SeqNo, (uint)OktoResultCodes.OKTO_RESULT_SUCCESS, DictVmNameToSid); SendSynchronous(conn, msgSidReply.Serialize); conn.BeginReceive(); break; } default: { string msg = string.Format("ReceiveMessage: unexpected message type {0}", messageType); throw new ApplicationException(msg); } } // switch }