{"id":176,"date":"2016-11-15T15:38:14","date_gmt":"2016-11-15T20:38:14","guid":{"rendered":"http:\/\/www.millamilla.com\/?p=176"},"modified":"2016-11-16T09:25:22","modified_gmt":"2016-11-16T14:25:22","slug":"salt-reactors-and-events","status":"publish","type":"post","link":"https:\/\/www.millamilla.com:443\/?p=176","title":{"rendered":"Salt Reactor : Having a minion ask another minion to execute a module with events and reactors"},"content":{"rendered":"<p>&nbsp;<\/p>\n<p>I needed to solve the task\u00a0of having a minion execute a job on another minion and this is how I accomplished the task. \u00a0This stemmed from having a minion that runs a web applications which just needed to execute an execution module that already existed, but needed to run on a different minion.<\/p>\n<p>My solution depends on Salt events and reactors to orchestrate the request from the web server minion to the worker minion and have the worker minion give a response of the work it completed back to the web server minion.<\/p>\n<p>Salt version:\u00a0salt 2016.3.4<\/p>\n<p>Machines:<\/p>\n<table style=\"width: 510.333px;\" border=\"1\" cellspacing=\"0\" cellpadding=\"2\">\n<thead>\n<tr>\n<td style=\"width: 100px;\">Host<\/td>\n<td style=\"width: 106px;\">Salt Role<\/td>\n<td style=\"width: 293.333px;\">Use Case<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"width: 100px;\">saltmast16<\/td>\n<td style=\"width: 106px;\">Salt Master<\/td>\n<td style=\"width: 293.333px;\">Orchestrator<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 100px;\">saltmina16<\/td>\n<td style=\"width: 106px;\">Salt Minion<\/td>\n<td style=\"width: 293.333px;\">Where execution module will run<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 100px;\">saltminb16<\/td>\n<td style=\"width: 106px;\">Salt Minion<\/td>\n<td style=\"width: 293.333px;\">Web server that initiates work<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>\/srv\/salt\/_modules\/rhtest.py<\/p>\n<p>The event.fire_master call sends the com\/millamilla\/test\/event\/finish event to the master<\/p>\n<table class=\"alignleft\" border=\"1\" cellspacing=\"0\" cellpadding=\"2\">\n<thead>\n<tr style=\"height: 24px;\">\n<td style=\"width: 78.6667px; height: 24px; text-align: left; vertical-align: top;\"><\/td>\n<td style=\"width: 238.667px; height: 24px;\">saltminb16<\/p>\n<p>Web Server<\/td>\n<td style=\"width: 344px; height: 24px;\">\u00a0Web Server\u00a0Files\/ Code<\/td>\n<td style=\"width: 234.667px; height: 24px;\">salmast16<\/p>\n<p>Master<\/td>\n<td style=\"width: 389.333px; height: 24px;\">\u00a0Master Files\/ Code<\/td>\n<td style=\"width: 237.333px; height: 24px;\">saltmina16<\/p>\n<p>Worker<\/td>\n<td style=\"width: 237.333px; height: 24px;\">Worker Files\/ Code<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr style=\"height: 24px;\">\n<td style=\"width: 78.6667px; height: 24px;\">Send an\u00a0event from the web server to the master.<\/td>\n<td style=\"width: 238.667px; height: 24px;\">Send event<br \/>\ncom\/millamilla\/test\/event\/start<\/td>\n<td style=\"width: 344px; height: 24px;\"><span style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;\"><span style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;\"><span style=\"white-space: normal;\">Python: send_event.py\u00a0<\/span><\/span><\/span><\/p>\n<pre>import salt.client\r\nimport uuid\r\n\r\ncaller = salt.client.Caller()\r\nevent_uuid = str(uuid.uuid1())\r\n\r\ncaller.sminion.functions['event.send'](\r\n    'com\/millamilla\/test\/event\/start',\r\n    {\r\n      'target': \"saltmina16\",\r\n      'site': \"moodle\",\r\n      'user': \"hedrickbt\",\r\n      'uuid': event_uuid,\r\n    }\r\n)\r\n<\/pre>\n<\/td>\n<td style=\"width: 234.667px; height: 24px;\">Receive event<br \/>\ncom\/millamilla\/test\/event\/start<\/td>\n<td style=\"width: 389.333px; height: 24px;\">\u00a0\/etc\/salt\/master.d\/reactor.conf<\/p>\n<pre>reactor:\r\n  # ...\r\n  - 'com\/millamilla\/test\/event\/start':        \r\n    - salt:\/\/reactor\/rhtesteventstart.sls   \r\n  # ...\r\n<\/pre>\n<pre>#...<\/pre>\n<\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 78.6667px; height: 24px;\"><\/td>\n<td style=\"width: 238.667px; height: 24px; background-color: #ff0000;\"><span style=\"color: #ffffff;\">\u00a0&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;<\/span><\/td>\n<td style=\"width: 238.667px; height: 24px; background-color: #ff0000;\"><span style=\"color: #ffffff;\">\u00a0\u00a0&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;<\/span><\/td>\n<td style=\"width: 234.667px; height: 24px;\"><\/td>\n<td style=\"width: 389.333px; height: 24px;\"><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 78.6667px; height: 24px;\">\u00a0Master handles event and causes execuction module to run on worker minion<\/td>\n<td style=\"width: 238.667px; height: 24px;\"><\/td>\n<td style=\"width: 344px; height: 24px;\"><\/td>\n<td style=\"width: 234.667px; height: 24px;\">Handle com\/millamilla\/test\/event\/start Event. \u00a0Call execution module on worker.<\/td>\n<td style=\"width: 389.333px; height: 24px;\">\u00a0\/srv\/salt\/reactor\/rhtesteventstart.sls<\/p>\n<pre>run_it:\r\n  local.rhtest.rhfunc:\r\n    - tgt: {{ data['data']['target'] }}\r\n    - arg:\r\n      - {{ data['data']['site'] }}\r\n      - {{ data['data']['user'] }}\r\n      - {{ data['id'] }}\r\n      - {{ data['data']['uuid'] }}\r\n<\/pre>\n<p>&nbsp;<\/td>\n<td style=\"width: 237.333px; height: 24px;\">Execute module<\/td>\n<td style=\"width: 237.333px; height: 24px;\">Created on master:<br \/>\n\/srv\/salt\/_modules\/rhtest.py<br \/>\nand sync&#8217;d to minions from master via:<br \/>\nsalt &#8216;*&#8217; saltutil.sync_all<\/p>\n<pre>import salt\r\n\r\n__outputter__ = {\r\n  'rhfunc': 'yaml'\r\n}\r\n\r\ndef rhfunc(site, user, requesting_host, event_uuid):\r\n  result = {\r\n    \"site\":site,\r\n    \"user\":user,\r\n    \"requesting_host\":requesting_host,\r\n    \"uuid\":event_uuid\r\n  }\r\n\r\n  __salt__['event.fire_master'](\r\n    {\r\n      \"site\":site,\r\n      \"user\":user,\r\n      \"requesting_host\":requesting_host,\r\n      \"uuid\":event_uuid,\r\n    },\r\n    'com\/millamilla\/test\/event\/finish',\r\n  )\r\n\r\n  return result\r\n<\/pre>\n<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 78.6667px; height: 24px;\"><\/td>\n<td style=\"width: 238.667px; height: 24px;\"><\/td>\n<td style=\"width: 344px; height: 24px;\"><\/td>\n<td style=\"width: 234.667px; height: 24px; background-color: #ff0000;\">\u00a0<span style=\"color: #ffffff;\">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;<\/span><\/td>\n<td style=\"width: 234.667px; height: 24px; background-color: #ff0000;\"><span style=\"color: #ffffff;\">\u00a0&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;<\/span><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 78.6667px; height: 24px;\">\u00a0After worker minion executes modules send event back to master<\/td>\n<td style=\"width: 238.667px; height: 24px;\"><\/td>\n<td style=\"width: 344px; height: 24px;\"><\/td>\n<td style=\"width: 234.667px; height: 24px;\">Receive event<br \/>\ncom\/millamilla\/test\/event\/finish<\/td>\n<td style=\"width: 389.333px; height: 24px;\">\/etc\/salt\/master.d\/reactor.conf<\/p>\n<pre>\r\nreactor:\r\n  # ...\r\n  - 'com\/millamilla\/test\/event\/finish':\r\n    - salt:\/\/reactor\/rhtesteventfinish.sls \r\n  # ...\r\n<\/pre>\n<\/td>\n<td><\/td>\n<td><\/td>\n<\/tr>\n<tr style=\"height: 24.8359px;\">\n<td style=\"width: 78.6667px; height: 24.8359px;\"><\/td>\n<td style=\"width: 238.667px; height: 24.8359px;\"><\/td>\n<td style=\"width: 344px; height: 24.8359px;\"><\/td>\n<td style=\"width: 234.667px; height: 24.8359px; background-color: #000000;\"><span style=\"color: #ffffff;\">&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;<\/span><\/td>\n<td style=\"width: 234.667px; height: 24.8359px; background-color: #000000;\"><span style=\"color: #ffffff;\">&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;<\/span><\/td>\n<td style=\"width: 237.333px; height: 24.8359px;\"><\/td>\n<td style=\"width: 237.333px; height: 24.8359px;\"><\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 78.6667px; height: 24px;\">\u00a0Forward event Master received to the web minion.<\/td>\n<td style=\"width: 238.667px; height: 24px;\">Receive\/Handle Event<\/p>\n<p>com\/millamilla\/test\/event\/finish<\/td>\n<td style=\"width: 344px; height: 24px;\">Python: receive_event.py<\/p>\n<p>This needs to run just after sending the message so it can be listening. \u00a0You will want to add code that has a timeout in the for&#8230;print loop. \u00a0Otherwise, it could run forever.<\/p>\n<p>You will also want to add code to look at the data[&#8216;uuid&#8217;] to make sure it is the one you sent out.<\/p>\n<p>Typically, you wouldn&#8217;t have a separate send\/receive program, but you can. \u00a0I plant to create a single script that\u00a0generates the uuid, sends the request, and waits for a message to return within a timeout period. \u00a0If the timeout is exceeded an error would be thrown.<\/p>\n<pre>import salt.config\r\nimport salt.utils.event\r\nimport socket\r\nimport os\r\n\r\n# Import 3rd-party libs\r\nimport salt.ext.six as six\r\n\r\nmy_hostname = socket.gethostname()\r\n\r\n#opts = salt.config.client_config('\/etc\/salt\/minion')\r\nopts = {}\r\nopts['node'] = 'minion'\r\nopts['sock_dir'] = '\/var\/run\/salt'\r\nopts['sock_dir'] = os.path.join(opts['sock_dir'], opts['node'])\r\nopts['id'] = my_hostname\r\nopts['transport'] = 'zeromq'\r\n\r\nevent = salt.utils.event.get_event(\r\n        opts['node'],\r\n        sock_dir=opts['sock_dir'],\r\n        transport=opts['transport'],\r\n        opts=opts)\r\n\r\nfor data in event.iter_events(tag='com\/millamilla\/test\/event\/finish'):\r\n    print(data)\r\n<\/pre>\n<\/td>\n<td style=\"width: 234.667px; height: 24px;\">Handle Event<\/p>\n<p>com\/millamilla\/test\/event\/finish<\/td>\n<td style=\"width: 389.333px; height: 24px;\">\u00a0\/srv\/salt\/reactor\/rhtesteventfinish.sls<\/p>\n<pre>notify_it:\r\n  local.event.fire:\r\n  - tgt: {{ data['data']['requesting_host'] }}\r\n  - arg:\r\n    - {{ data['data'] }}\r\n    - com\/millamilla\/test\/event\/finish\r\n<\/pre>\n<\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 78.6667px; height: 24px;\"><\/td>\n<td style=\"width: 238.667px; height: 24px; background-color: #000000;\"><span style=\"color: #ffffff;\">\u00a0&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;<\/span><\/td>\n<td style=\"width: 238.667px; height: 24px; background-color: #000000;\"><span style=\"color: #ffffff;\">\u00a0&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;<\/span><\/td>\n<td style=\"width: 234.667px; height: 24px;\"><\/td>\n<td style=\"width: 389.333px; height: 24px;\"><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 78.6667px; height: 24px;\"><\/td>\n<td style=\"width: 238.667px; height: 24px;\"><\/td>\n<td style=\"width: 344px; height: 24px;\"><\/td>\n<td style=\"width: 234.667px; height: 24px;\"><\/td>\n<td style=\"width: 389.333px; height: 24px;\"><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<td style=\"width: 237.333px; height: 24px;\"><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp; I needed to solve the task\u00a0of having a minion execute a job on another minion and this is how I accomplished the task. \u00a0This stemmed from having a minion that runs a web applications which just needed to execute an execution module that already existed, but needed to run on a different minion. My [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-176","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=\/wp\/v2\/posts\/176","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=176"}],"version-history":[{"count":18,"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=\/wp\/v2\/posts\/176\/revisions"}],"predecessor-version":[{"id":194,"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=\/wp\/v2\/posts\/176\/revisions\/194"}],"wp:attachment":[{"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=176"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=176"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.millamilla.com:443\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=176"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}