{"id":2731,"date":"2012-07-06T10:59:58","date_gmt":"2012-07-06T09:59:58","guid":{"rendered":"http:\/\/www.devco.net\/?p=2731"},"modified":"2012-07-06T11:08:47","modified_gmt":"2012-07-06T10:08:47","slug":"mcollective-pluggable-discovery","status":"publish","type":"post","link":"https:\/\/www.devco.net\/archives\/2012\/07\/06\/mcollective-pluggable-discovery.php","title":{"rendered":"MCollective Pluggable Discovery"},"content":{"rendered":"
This ia a post in a series of posts I am doing about MCollective 2.0 and later<\/a>. <\/p>\n In my previous post I detailed how you can extend the scope of the information MCollective has available to it about a node using Data Plugins<\/a>, this was node side plugins today we’ll look at ones that runs on the client.<\/p>\n These plugins give you the freedom of choice to discover against anything that can give back a list of nodes with mcollective identities. Examples are databases, CMDBs, something like Noah or Zookeeper etc.<\/p>\n To get this to work requires Direct Addressing<\/a>, I’ll recap an example from the linked post:<\/p>\n <\/code><\/p>\n In this example MCollective is reading hosts.txt and using that as the source of truth and attempts to communicate only with the hosts discovered against that file. This, as was covered in the previous post, is in stark contrast with MCollective 1.x that had no choice but to use the network as source of truth.<\/p>\n Building on this we’ve built a plugin system that abstracts this away into plugins that you can use on the CLI, web etc – once activated the MCollective usage on the CLI and any existing code can use these plugins without code change.<\/p>\n <\/code><\/p>\n Notice the discovery message says it is using the “mc”<\/em> method, this is the traditional broadcast mode as before, it’s the default mode and will remain the default mode.<\/p>\n Lets look at the generic usage of the hosts.txt<\/em> above:<\/p>\n <\/code><\/p>\n Note the change in the discovery message, it is now using the flatfile<\/em> discovery method and doesn’t have a timeout. Take a look at the Discovery Time<\/em> statistic, the flatfile example took a fraction of a second vs the usual 2 seconds spent discovering.<\/p>\n There’s a longer form of the above command:<\/p>\n <\/code><\/p>\n So you can pick a discovery method and they can take options. You can figure out what plugins you have available to you using the plugin<\/em> application:<\/p>\n <\/code><\/p>\n And more information about a plugin can be seen:<\/p>\n <\/code><\/p>\n The discovery methods have capabilities that declare what they can do. The flatfile one for example has no idea about classes, facts etc so it’s capabilities would only be identity filters.<\/p>\n If you decide to always use a different plugin than mc<\/em> as your discovery source you can set it in client.cfg<\/em>:<\/p>\n <\/code><\/p>\n The RPC api obviously can also choose method and supply options, below code forces the flatfile mode:<\/p>\n <\/code><\/p>\n This has the same effect as mco rpc service restart service=httpd –dm=flatfile –do=hosts.txt<\/em><\/p>\n This plugin will let you issue commands like:<\/p>\n <\/code><\/p>\n So your basic identity filters with regular expression support or just all hosts.<\/p>\n <\/code><\/p>\n Past the basic boiler plate in lines 5 to 11 we deal with the discovery options, you'll notice discovery options is an array so users can call --disc-option<\/em> many times and each call just gets appended to this array. We'll just take one flat file and raise if you didn't pass a file or if the file can't be read.<\/p>\n Lines 13 and 14 sets up a empty array where the selected nodes will go into and reads all the hosts found in the file.<\/p>\n Lines 16 and 17 checks if we got anything in the identity<\/em> filter, if it was not we set the discovered list to all hosts in the file in line 27. The filters are arrays so in the case of multiple -I passed you will have multiple entries here, line 17 loops all the filters. You do not need to worry about someone accidentally setting a Class filter as MCollective will know from the DDL that you are incapable of doing class filters and will just not call your plugin with those.<\/p>\n The body of the loop in lines 18 to 25 just does regular expression matching or exact matching over the list and if anything is found it gets added to the discovered list.<\/p>\n In the end we just return the list of discovered nodes, you do not need to worry about duplicates in the list or sorting it or anything.<\/p>\n As there were automatic documentation generated and input validation done you need to create a DDL file that describes the plugin and the data it can accept and return, here's the DDL for this plugin:<\/p>\n <\/code><\/p>\n The meta block is familiar - set timeout to 0 if there's no timeout and then MCollective will not inform the user about a timeout in the discovery message. Lines 9 to 11 declares the capabilities, possible capabilities are :classes<\/em>, :facts<\/em>, :identity<\/em>, :agents<\/em>, :compound<\/em>. Technically :compound<\/em> isn't usable by your plugins as MCollective will force the mc<\/em> plugin when you use any -S filters as those might contain references to data plugins that has to be done using the nodes as source of truth.<\/p>\n Finally store this in a directory like below and you can package it into a RPM or a Deb:<\/p>\n <\/code><\/p>\n Install this plugin to all your clients and it will be available to use, if you do not want to use the packages just dump the files in $libdir\/discovery\/<\/em>.<\/p>\n If you use the MongoDB based discovery system<\/a> there is a fully capable discovery plugin that can work against a local MongoDB instance. This plugin has all the capabilities possible with full regular expression support and full sub collective support. I use this as my default discovery method now.<\/p>\n We're also working on a PuppetDB<\/a> one, it is not quite ready to publish as I am waiting for PuppetDB to get wildcard support. And finally there is a community plugin that discovers using Elastic Search<\/a>.<\/p>\n Use the network when appropriate, use databases or flat files when appropriate and you can switch freely between modes during the life of a single application.<\/p>\n Using these plugins is fun as they can be extremely fast. The short 1 minute video embedded below (click here<\/a> if its not shown) shows the mco, puppetdb and mongodb plugins in action.<\/p>\nBackground<\/H3>
\nUsing the network as your source of truth works for a certain style of application but as I pointed out in an earlier post there are kinds of application where that is not appropriate. If you want to build a deployer that rolls out the next version of your software you probably want to provide it with a list of nodes rather than have it discover against the network, this way you know when a deploy failed because a node is down rather than it just not being discovered.<\/p>\n<\/p>\n
\r\nc = rpcclient(\"service\")\r\n\r\nc.discover :nodes => File.readline(\"hosts.txt\").map {|i| i.chomp}\r\n\r\nprintrpc c.restart(:service => \"httpd\")\r\n<\/pre>\n
Using Discovery Plugins<\/H3>
\nUsing these plugins is the same as you’d always do discovery, in fact as of version 2.1.0 if you use mcollective you’re already using this plugin, lets see:<\/p>\n<\/p>\n
\r\n% mco rpc rpcutil ping\r\nDiscovering hosts using the mc method for 2 second(s) .... 26\r\n\r\n * [============================================================> ] 26 \/ 26\r\n.\r\n.\r\n---- rpcutil#ping call stats ----\r\n Nodes: 26 \/ 26\r\n Pass \/ Fail: 26 \/ 0\r\n Start Time: Fri Jul 06 09:47:06 +0100 2012\r\n Discovery Time: 2002.07ms\r\n Agent Time: 311.14ms\r\n Total Time: 2313.21ms\r\n<\/pre>\n
<\/p>\n
\r\n% mco rpc rpcutil ping --nodes hosts.txt -v\r\nDiscovering hosts using the flatfile method .... 9\r\n\r\n * [============================================================> ] 9 \/ 9\r\n.\r\n.\r\n---- rpcutil#ping call stats ----\r\n Nodes: 9 \/ 9\r\n Pass \/ Fail: 9 \/ 0\r\n Start Time: Fri Jul 06 09:48:15 +0100 2012\r\n Discovery Time: 0.40ms\r\n Agent Time: 34.62ms\r\n Total Time: 35.01ms\r\n<\/pre>\n
<\/p>\n
\r\n% mco rpc rpcutil ping --disc-method flatfile --disc-option hosts.txt\r\nDiscovering hosts using the flatfile method .... 9\r\n.\r\n.\r\n<\/pre>\n
<\/p>\n
\r\n% mco plugin doc\r\nPlease specify a plugin. Available plugins are:\r\n.\r\n.\r\nDiscovery Methods:\r\n flatfile Flatfile based discovery for node identities\r\n mc MCollective Broadcast based discovery\r\n mongo MongoDB based discovery for databases built using registration\r\n puppetdb PuppetDB based discovery\r\n<\/pre>\n
<\/p>\n
\r\n% mco plugin doc mc\r\nMCollective Broadcast based discovery\r\n\r\n Author: R.I.Pienaar
<\/p>\n
\r\ndefault_discovery_method = mongo\r\n<\/pre>\n
<\/p>\n
\r\nc = rpcclient(\"service\")\r\n\r\nc.discovery_method = \"flatfile\"\r\nc.discovery_options << \"hosts.txt\"\r\n\r\nprintrpc c.restart(:service => \"httpd\")\r\n<\/pre>\n
Writing a Plugin<\/H3>
\nWe’ll look at the simplest plugin which is the flatfile<\/em> one, this plugin ships with MCollective but it’s a good example.<\/p>\n<\/p>\n
\r\n% mco service restart httpd\r\n% mco service restart httpd -I some.host\r\n% mco service restart httpd -I \/domain\/ -I \/otherdomain\/\r\n<\/pre>\n
<\/p>\n
\r\nmodule MCollective\r\n class Discovery\r\n class Flatfile\r\n def self.discover(filter, timeout, limit=0, client=nil)\r\n unless client.options[:discovery_options].empty?\r\n file = client.options[:discovery_options].first\r\n else\r\n raise \"The flatfile discovery method needs a path to a text file\"\r\n end\r\n\r\n raise \"Cannot read the file %s specified as discovery source\" % file unless File.readable?(file)\r\n\r\n discovered = []\r\n hosts = File.readlines(file).map{|l| l.chomp}\r\n\r\n unless filter[\"identity\"].empty?\r\n filter[\"identity\"].each do |identity|\r\n identity = Regexp.new(identity.gsub(\"\\\/\", \"\")) if identity.match(\"^\/\")\r\n\r\n if identity.is_a?(Regexp)\r\n discovered = hosts.grep(identity)\r\n elsif hosts.include?(identity)\r\n discovered << identity\r\n end\r\n end\r\n else\r\n discovered = hosts\r\n end\r\n\r\n discovered\r\n end\r\n end\r\n end\r\nend\r\n<\/pre>\n
<\/p>\n
\r\nmetadata :name => \"flatfile\",\r\n :description => \"Flatfile based discovery for node identities\",\r\n :author => \"R.I.Pienaar
<\/p>\n
\r\n% tree flatfile\r\nflatfile\r\n\u2514\u2500\u2500 discovery\r\n \u251c\u2500\u2500 flatfile.ddl\r\n \u2514\u2500\u2500 flatfile.rb\r\n% cd flatfile\r\n% mco plugin package\r\nCreated package mcollective-flatfile-discovery\r\n% ls -l *rpm\r\n-rw-rw-r-- 1 rip rip 2893 Jul 6 10:20 mcollective-flatfile-discovery-0.1-1.noarch.rpm\r\n<\/pre>\n
Available Plugins<\/H3>
\nThere are a few plugins available now, you saw the mc<\/em> and flatfile<\/em> ones here. <\/p>\nConclusion<\/H3>
\nThese plugins conclude the big rework done on MCollective discovery. You can now mix and match any source of truth you like even ones we as MCollective developers are not aware of as you can write your own plugin. <\/p>\n