1 /*  $Id: ns_automation.cpp 572261 2018-10-10 19:24:55Z sadyrovr $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *   Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors:  Dmitry Kazimirov
27  *
28  * File Description: NetSchedule automation implementation.
29  *
30  */
31 
32 #include <ncbi_pch.hpp>
33 
34 #include "ns_automation.hpp"
35 
36 USING_NCBI_SCOPE;
37 
38 using namespace NAutomation;
39 
40 const string SNetScheduleService::kName = "nssvc";
41 const string SNetScheduleServer::kName = "nssrv";
42 
SNetScheduleService(CAutomationProc * automation_proc,CNetScheduleAPI ns_api)43 SNetScheduleService::SNetScheduleService(
44         CAutomationProc* automation_proc,
45         CNetScheduleAPI ns_api) :
46     SNetService(automation_proc),
47     m_NetScheduleAPI(ns_api)
48 {
49     auto warning_handler = [&](const string& m, CNetServer s) {
50         auto o = m_AutomationProc->ReturnNetScheduleServerObject(m_NetScheduleAPI, s);
51         m_AutomationProc->SendWarning(m, o);
52         return true;
53     };
54 
55     GetService().SetWarningHandler(warning_handler);
56 }
57 
SNetScheduleServer(CAutomationProc * automation_proc,CNetScheduleAPIExt ns_api,CNetServer::TInstance server)58 SNetScheduleServer::SNetScheduleServer(
59         CAutomationProc* automation_proc,
60         CNetScheduleAPIExt ns_api, CNetServer::TInstance server) :
61     SNetScheduleService(automation_proc, ns_api.GetServer(server))
62 {
63     if (GetService().IsLoadBalanced()) {
64         NCBI_THROW(CAutomationException, eCommandProcessingError,
65                 "NetScheduleServer constructor: "
66                 "'server_address' must be a host:port combination");
67     }
68 }
69 
NewCommand()70 CCommand SNetScheduleService::NewCommand()
71 {
72     return CCommand(kName, ExecNew<TSelf>, {
73             { "service_name", "", },
74             { "queue_name", "", },
75             { "client_name", "", },
76         });
77 }
78 
Create(const TArguments & args,CAutomationProc * automation_proc)79 CAutomationObject* SNetScheduleService::Create(const TArguments& args, CAutomationProc* automation_proc)
80 {
81     _ASSERT(args.size() == 3);
82 
83     const auto service_name = args["service_name"].AsString();
84     const auto queue_name   = args["queue_name"].AsString();
85     const auto client_name  = args["client_name"].AsString();
86 
87     CNetScheduleAPIExt ns_api(CNetScheduleAPIExt::CreateNoCfgLoad(
88                 service_name, client_name, queue_name));
89 
90     return new SNetScheduleService(automation_proc, ns_api);
91 }
92 
NewCommand()93 CCommand SNetScheduleServer::NewCommand()
94 {
95     return CCommand(kName, ExecNew<TSelf>, {
96             { "service_name", "", },
97             { "queue_name", "", },
98             { "client_name", "", },
99         });
100 }
101 
Create(const TArguments & args,CAutomationProc * automation_proc)102 CAutomationObject* SNetScheduleServer::Create(const TArguments& args, CAutomationProc* automation_proc)
103 {
104     _ASSERT(args.size() == 3);
105 
106     const auto service_name = args["service_name"].AsString();
107     const auto queue_name   = args["queue_name"].AsString();
108     const auto client_name  = args["client_name"].AsString();
109 
110     CNetScheduleAPIExt ns_api(CNetScheduleAPIExt::CreateNoCfgLoad(
111                 service_name, client_name, queue_name));
112 
113     CNetServer server = ns_api.GetService().Iterate().GetServer();
114     return new SNetScheduleServer(automation_proc, ns_api, server);
115 }
116 
ReturnNetScheduleServerObject(CNetScheduleAPI::TInstance ns_api,CNetServer::TInstance server)117 TAutomationObjectRef CAutomationProc::ReturnNetScheduleServerObject(
118         CNetScheduleAPI::TInstance ns_api,
119         CNetServer::TInstance server)
120 {
121     TAutomationObjectRef object(new SNetScheduleServer(this, ns_api, server));
122     AddObject(object);
123     return object;
124 }
125 
CallCommand()126 CCommand SNetScheduleServer::CallCommand()
127 {
128     return CCommand(kName, TCommandGroup(CallCommands(), CheckCall<TSelf>));
129 }
130 
CallCommands()131 TCommands SNetScheduleServer::CallCommands()
132 {
133     TCommands cmds =
134     {
135         { "server_status",               ExecMethod<TSelf, &TSelf::ExecServerStatus>, {
136                 { "verbose", false, },
137             }},
138         { "job_group_info",              ExecMethod<TSelf, &TSelf::ExecJobGroupInfo>, {
139                 { "verbose", false, },
140             }},
141         { "client_info",                 ExecMethod<TSelf, &TSelf::ExecClientInfo>, {
142                 { "verbose", false, },
143             }},
144         { "notification_info",           ExecMethod<TSelf, &TSelf::ExecNotificationInfo>, {
145                 { "verbose", false, },
146             }},
147         { "affinity_info",               ExecMethod<TSelf, &TSelf::ExecAffinityInfo>, {
148                 { "verbose", false, },
149             }},
150         { "change_preferred_affinities", ExecMethod<TSelf, &TSelf::ExecChangePreferredAffinities>, {
151                 { "affs_to_add", CJsonNode::eArray, },
152                 { "affs_to_del", CJsonNode::eArray, },
153             }},
154     };
155 
156     TCommands base_cmds = SNetScheduleService::CallCommands();
157     cmds.insert(cmds.end(), base_cmds.begin(), base_cmds.end());
158 
159     return cmds;
160 }
161 
ExecServerStatus(const TArguments & args,SInputOutput & io)162 void SNetScheduleServer::ExecServerStatus(const TArguments& args, SInputOutput& io)
163 {
164     _ASSERT(args.size() == 1);
165 
166     auto& reply = io.reply;
167     const auto verbose = args["verbose"].AsBoolean();
168     reply.Append(g_LegacyStatToJson(GetServer(), verbose));
169 }
170 
ExecJobGroupInfo(const TArguments & args,SInputOutput & io)171 void SNetScheduleServer::ExecJobGroupInfo(const TArguments& args, SInputOutput& io)
172 {
173     _ASSERT(args.size() == 1);
174 
175     auto& reply = io.reply;
176     const auto verbose = args["verbose"].AsBoolean();
177     reply.Append(g_GenericStatToJson(GetServer(), eNetScheduleStatJobGroups, verbose));
178 }
179 
ExecClientInfo(const TArguments & args,SInputOutput & io)180 void SNetScheduleServer::ExecClientInfo(const TArguments& args, SInputOutput& io)
181 {
182     _ASSERT(args.size() == 1);
183 
184     auto& reply = io.reply;
185     const auto verbose = args["verbose"].AsBoolean();
186     reply.Append(g_GenericStatToJson(GetServer(), eNetScheduleStatClients, verbose));
187 }
188 
ExecNotificationInfo(const TArguments & args,SInputOutput & io)189 void SNetScheduleServer::ExecNotificationInfo(const TArguments& args, SInputOutput& io)
190 {
191     _ASSERT(args.size() == 1);
192 
193     auto& reply = io.reply;
194     const auto verbose = args["verbose"].AsBoolean();
195     reply.Append(g_GenericStatToJson(GetServer(), eNetScheduleStatNotifications, verbose));
196 }
197 
ExecAffinityInfo(const TArguments & args,SInputOutput & io)198 void SNetScheduleServer::ExecAffinityInfo(const TArguments& args, SInputOutput& io)
199 {
200     _ASSERT(args.size() == 1);
201 
202     auto& reply = io.reply;
203     const auto verbose = args["verbose"].AsBoolean();
204     reply.Append(g_GenericStatToJson(GetServer(), eNetScheduleStatAffinities, verbose));
205 }
206 
s_ExtractVectorOfStrings(CJsonNode & arg)207 vector<string> s_ExtractVectorOfStrings(CJsonNode& arg)
208 {
209     vector<string> result;
210 
211     if (!arg.IsNull()) {
212         for (CJsonIterator it = arg.Iterate(); it; ++it) {
213             CArgument affinity("affinity", CJsonNode::eString);
214             affinity.Exec("change_preferred_affinities", it);
215             result.push_back(affinity.AsString());
216         }
217     }
218 
219     return result;
220 }
221 
ExecChangePreferredAffinities(const TArguments & args,SInputOutput &)222 void SNetScheduleServer::ExecChangePreferredAffinities(const TArguments& args, SInputOutput&)
223 {
224     _ASSERT(args.size() == 2);
225 
226     auto affs_to_add = args["affs_to_add"].Value();
227     auto affs_to_del = args["affs_to_del"].Value();
228 
229     auto to_add = s_ExtractVectorOfStrings(affs_to_add);
230     auto to_del = s_ExtractVectorOfStrings(affs_to_del);
231 
232     m_NetScheduleAPI.GetExecutor().ChangePreferredAffinities(&to_add, &to_del);
233 }
234 
CallCommand()235 CCommand SNetScheduleService::CallCommand()
236 {
237     return CCommand(kName, TCommandGroup(CallCommands(), CheckCall<TSelf>));
238 }
239 
CallCommands()240 TCommands SNetScheduleService::CallCommands()
241 {
242     TCommands cmds =
243     {
244         { "set_client_type",  ExecMethod<TSelf, &TSelf::ExecSetClientType>, {
245                 { "client_type", 0, },
246             }},
247         { "set_node_session", ExecMethod<TSelf, &TSelf::ExecSetNodeSession>, {
248                 { "node", "", },
249                 { "session", "", },
250             }},
251         { "queue_info",       ExecMethod<TSelf, &TSelf::ExecQueueInfo>, {
252                 { "queue_name", "", },
253             }},
254         { "queue_class_info", ExecMethod<TSelf, &TSelf::ExecQueueClassInfo>, },
255         { "reconf",           ExecMethod<TSelf, &TSelf::ExecReconf>, },
256         { "suspend",          ExecMethod<TSelf, &TSelf::ExecSuspend>, {
257                 { "pullback_mode", false, },
258             }},
259         { "resume",           ExecMethod<TSelf, &TSelf::ExecResume>, },
260         { "shutdown",         ExecMethod<TSelf, &TSelf::ExecShutdown>, {
261                 { "do_not_drain", false, },
262             }},
263         { "parse_key",        ExecMethod<TSelf, &TSelf::ExecParseKey>, {
264                 { "job_key", CJsonNode::eString, },
265             }},
266         { "job_info",         ExecMethod<TSelf, &TSelf::ExecJobInfo>, {
267                 { "job_key", CJsonNode::eString, },
268                 { "verbose", true, },
269             }},
270         { "job_counters",     ExecMethod<TSelf, &TSelf::ExecJobCounters>, {
271                 { "affinity", "", },
272                 { "job_group", "", },
273             }},
274         { "get_servers",      ExecMethod<TSelf, &TSelf::ExecGetServers>, },
275     };
276 
277     TCommands base_cmds = SNetService::CallCommands();
278     cmds.insert(cmds.end(), base_cmds.begin(), base_cmds.end());
279 
280     return cmds;
281 }
282 
ExecSetClientType(const TArguments & args,SInputOutput &)283 void SNetScheduleService::ExecSetClientType(const TArguments& args, SInputOutput&)
284 {
285     _ASSERT(args.size() == 1);
286 
287     const auto client_type = args["client_type"].AsInteger<CNetScheduleAPI::EClientType>();
288     m_NetScheduleAPI.SetClientType(client_type);
289 }
290 
ExecSetNodeSession(const TArguments & args,SInputOutput &)291 void SNetScheduleService::ExecSetNodeSession(const TArguments& args, SInputOutput&)
292 {
293     _ASSERT(args.size() == 2);
294 
295     const auto node    = args["node"].AsString();
296     const auto session = args["session"].AsString();
297     m_NetScheduleAPI.ReSetClientNode(node);
298     m_NetScheduleAPI.ReSetClientSession(session);
299 }
300 
ExecQueueInfo(const TArguments & args,SInputOutput & io)301 void SNetScheduleService::ExecQueueInfo(const TArguments& args, SInputOutput& io)
302 {
303     _ASSERT(args.size() == 1);
304 
305     auto& reply = io.reply;
306     const auto queue_name = args["queue_name"].AsString();
307     reply.Append(g_QueueInfoToJson(m_NetScheduleAPI, queue_name));
308 }
309 
ExecQueueClassInfo(const TArguments &,SInputOutput & io)310 void SNetScheduleService::ExecQueueClassInfo(const TArguments&, SInputOutput& io)
311 {
312     auto& reply = io.reply;
313     reply.Append(g_QueueClassInfoToJson(m_NetScheduleAPI));
314 }
315 
ExecReconf(const TArguments &,SInputOutput & io)316 void SNetScheduleService::ExecReconf(const TArguments&, SInputOutput& io)
317 {
318     auto& reply = io.reply;
319     reply.Append(g_ReconfAndReturnJson(m_NetScheduleAPI));
320 }
321 
ExecSuspend(const TArguments & args,SInputOutput &)322 void SNetScheduleService::ExecSuspend(const TArguments& args, SInputOutput&)
323 {
324     _ASSERT(args.size() == 1);
325 
326     const auto pullback_mode = args["pullback_mode"].AsBoolean();
327     g_SuspendNetSchedule(m_NetScheduleAPI, pullback_mode);
328 }
329 
ExecResume(const TArguments &,SInputOutput &)330 void SNetScheduleService::ExecResume(const TArguments&, SInputOutput&)
331 {
332     g_ResumeNetSchedule(m_NetScheduleAPI);
333 }
334 
ExecShutdown(const TArguments & args,SInputOutput &)335 void SNetScheduleService::ExecShutdown(const TArguments& args, SInputOutput&)
336 {
337     _ASSERT(args.size() == 1);
338 
339     const auto do_not_drain = args["do_not_drain"].AsBoolean();
340     auto level = do_not_drain ? CNetScheduleAdmin::eNormalShutdown : CNetScheduleAdmin::eDrain;
341     m_NetScheduleAPI.GetAdmin().ShutdownServer(level);
342 }
343 
ExecParseKey(const TArguments & args,SInputOutput & io)344 void SNetScheduleService::ExecParseKey(const TArguments& args, SInputOutput& io)
345 {
346     _ASSERT(args.size() == 1);
347 
348     auto& reply = io.reply;
349     const auto job_key = args["job_key"].AsString();
350     CJobInfoToJSON job_key_to_json;
351     job_key_to_json.ProcessJobMeta(CNetScheduleKey(job_key, m_NetScheduleAPI.GetCompoundIDPool()));
352     reply.Append(job_key_to_json.GetRootNode());
353 }
354 
ExecJobInfo(const TArguments & args,SInputOutput & io)355 void SNetScheduleService::ExecJobInfo(const TArguments& args, SInputOutput& io)
356 {
357     _ASSERT(args.size() == 2);
358 
359     auto& reply = io.reply;
360     const auto job_key = args["job_key"].AsString();
361     const auto verbose = args["verbose"].AsBoolean();
362     CJobInfoToJSON job_info_to_json;
363     g_ProcessJobInfo(m_NetScheduleAPI, job_key, &job_info_to_json, verbose, m_NetScheduleAPI.GetCompoundIDPool());
364     reply.Append(job_info_to_json.GetRootNode());
365 }
366 
ExecJobCounters(const TArguments & args,SInputOutput & io)367 void SNetScheduleService::ExecJobCounters(const TArguments& args, SInputOutput& io)
368 {
369     _ASSERT(args.size() == 2);
370 
371     auto& reply = io.reply;
372     const auto affinity  = args["affinity"].AsString();
373     const auto job_group = args["job_group"].AsString();
374     CNetScheduleAdmin::TStatusMap status_map;
375     m_NetScheduleAPI.GetAdmin().StatusSnapshot(status_map, affinity, job_group);
376     CJsonNode jobs_by_status(CJsonNode::NewObjectNode());
377 
378     ITERATE(CNetScheduleAdmin::TStatusMap, it, status_map) {
379         jobs_by_status.SetInteger(it->first, it->second);
380     }
381     reply.Append(jobs_by_status);
382 }
383 
ExecGetServers(const TArguments &,SInputOutput & io)384 void SNetScheduleService::ExecGetServers(const TArguments&, SInputOutput& io)
385 {
386     auto& reply = io.reply;
387 
388     CJsonNode object_ids(CJsonNode::NewArrayNode());
389     for (CNetServiceIterator it = m_NetScheduleAPI.GetService().Iterate(
390             CNetService::eIncludePenalized); it; ++it)
391         object_ids.AppendInteger(m_AutomationProc->
392                 ReturnNetScheduleServerObject(m_NetScheduleAPI, *it)->
393                 GetID());
394     reply.Append(object_ids);
395 }
396