wiki:FactsFarmework
Last modified 7 years ago Last modified on 08/10/09 17:47:23

Func Facts

What is it ?

The Facts are modules like minion modules but a little bit different. Facts

are like variables of database tables. name,temperature,kernel_version etc. Why to need facts ? The main idea behind facts is to be able to send queries to minions like you do to database or ORM. Example , 'run update method on machines which are f9 >= ... You can do that with current minion modules of course but you have to send at least 2 queries to your minions. Another idea behind fact is to do more things with less connection hits.

Facts on Command Line :

ANDING THE PARAMETERS

#first format

--filter "keyword[operator]value,keyword2[operator]value2"

#second format

--filter "value in keyword,value ini keyword"

ORING THE PARAMETERS

#first format

--filteror "keyword[operator]value,keyword2[operator]value2"

#second format

--filteror "value in keyword,value ini keyword"

Examples :

And Examples :

func "*" call --filter "kernel>=2.4,runlevel=5" service status "httpd"
{'localhost.localdomain': 3}
func "*" call --filter "FeDoRa ini os,runlevel<=5" service status "httpd"
{'localhost.localdomain': 3}
func "*" call --filter "Fedora in os,INTEL ini cpumodel,runlevel=5" service status "httpd"
{'localhost.localdomain': 3}

OR Examples :

func "*" call --filteror "ubuntu in os,AMD ini cpumodel" service status "httpd"
{}
func "*" call --filteror "Fedora in os,AMD ini cpumodel" service status "httpd"
{'localhost.localdomain': 3}

func "*" call --filteror "Fedora in os,AMD ini cpumodel,runlevel<4" service status "httpd"
{'localhost.localdomain': 3}

Well that was just the begginning of what you can do with 'Facts Framework'. The next section will show you how to list and get information about availible facts modules on minion side :

func "*" call fact list_fact_modules
{'localhost.localdomain': ['hardware', 'fact_module']}

func "*" call fact list_fact_methods
{'localhost.localdomain': ['hardware.cpu_model',
                           'kernel',
                           'cpumodel',
                           'hardware.kernel_version',
                           'cpuvendor',
                           'hardware.run_level',
                           'hardware.cpu_vendor',
                           'hardware.os_name',
                           'runlevel',
                           'os']}
func "*" call fact show_fact_module "hardware"
{'localhost.localdomain': {'description': 'A modules that supplies hardware 
facts',
                           'name': 'hardware',
                           'version': '0.0.1'}}

func "*" call fact show_fact_method "runlevel"
{'localhost.localdomain': {'description': 'Shows the runlevel of the system',
                           'name': 'runlevel',
                           'tag': 'runlevel',
                           'usage': 'Can be used with all keywords'}}

The facts are methods that mostly (at least now) doesnt accept any arguments, so if you want to see the value of an existing fact just

func "*" call fact call_fact "runlevel"
{'localhost.localdomain': '5'}

Python API

One of the main ideas behind 'facts' were to be able to have a Python Api that will let us to do crazy chaining like Django ORM. With func facts you can run very complex queries.

Python Api Examples :

In [1]: import func.overlord.client as fc

In [2]: f = fc.Overlord("*")

In [4]: f.filter(runlevel=5,os__icontains="fedora").echo.run_string("ho ho ")
Out[4]: {}

In [5]: f = fc.Overlord("*")

In [6]: f.filter(runlevel=5,os__icontains="fedora").echo.run_string("ho ho ")
Out[6]: {'localhost.localdomain': 'ho ho '}

For OR operation we have a different method :

In [9]: f = fc.Overlord("*")
In [10]: f.filter_or(runlevel=5,os__icontains="ubuntu").echo.run_string("ho ho 
")
Out[10]: {'localhost.localdomain': 'ho ho '}

For chaining purposes we created those methods and_and,and_or,or_and,or_or. They work as follow: The first keyword in the method name means the outside connector which will connect 2 queries,and second keyword is for inside operation. Examples are easier to understand:

#chaining examples outsideConnector_insideConnector

In [12]: f = fc.Overlord("*")

In [13]:
f.and_and(runlevel__gte=3,runlevel__lte=6).or_and(os__icontains="fedora",kernel__icontains="2.16").echo.run_string("hey")
The final query is like :  (OR: (AND: ('runlevel__gte', 3),
('runlevel__lte',6)), (AND: ('kernel__icontains', '2.16'), 
('os__icontains','fedora')))

In [15]: f = fc.Overlord("*")

In [16]: 
f.and_and(runlevel__gte=3,runlevel__lte=6).or_or(os__icontains="fedora",kernel__icontains="2.16").echo.run_string("hey")
The final query is like :  (OR: (AND: ('runlevel__gte', 3), ('runlevel__lte', 
6)), (OR: ('kernel__icontains', '2.16'), ('os__icontains', 'fedora')))

In [17]: f = fc.Overlord("*")

In [18]: 
f.and_and(runlevel__gte=3,runlevel__lte=6).and_or(os__icontains="fedora",kernel__icontains="2.16").echo.run_string("hey")
The final query is like :  (AND: ('runlevel__gte', 3), ('runlevel__lte', 6),
(OR: ('kernel__icontains', '2.16'), ('os__icontains', 'fedora')))

In [19]: f = fc.Overlord("*")

In [20]: 
f.and_or(runlevel__gte=3,runlevel__lte=6).and_or(os__icontains="fedora",kernel__icontains="2.16").echo.run_string("hey")
The final query is like :  (AND: (OR: ('runlevel__gte', 3), ('runlevel__lte', 
6)), (OR: ('kernel__icontains', '2.16'), ('os__icontains', 'fedora')))

Therefore when you need something simple you can use filter and filter_or methods and when need more chaining operations use the 4 methods above.

Func Keywords :

Func Facts support some keywords that are really useful and makes the world really easy (as you saw above). The "" is very important when using facts because that is how the things are parsed on other side. For example if you want to have the os that contains name fedora You write

fact_methodkeyword = some_value --> so osicontains = "fedora"

The current keywords are as follow :

"","gt","gte","lt","lte",'contains','icontains','iexact','startswith'

Adding new keywords is pretty easy just go to : func.minion.facts.minion_query.QueryKeyword? and add a method in that format def keyword_nameOFyouKeyword(self,overlord_value,fact_value) that is all you need to do.

Writing Fact Modules:

Fact modules are loaded as almost the same way as minion modules so writing them is also that easy. Go to func.minion.facts.modules and add your module there if there is no problems it should loaded next time you restart your server. IMPORTANT : facts methods doesnt accept currently any arguments, think about them as properties of your system ...

Example :

class HardwareFacts(fact_module.BaseFactModule):
    
    version = "0.0.1"
    description = "A modules that supplies hardware facts"

    ##snip snip ..

    def run_level(self):
        """
        The runlevel of the system
        """
        return str(self.host.defaultRunlevel)

    #for easier acces be creful should be unique
    run_level.tag = "runlevel"
   

An important thing to note here is the run_level.tag value ,by assigning that value you are able to use your method as tag_valuekeyword = blip if you dont assing the "tag" attribute it is ok again just when calling you should do it that way : hardware.run_levelkeyword = blip Btw,be careful not to have 2 tags with same name, the last one will be availible :)

For more info you can check the examples ...

If you are not sure that you have conflicts with tag names you can simply run that to see if everything is ok on your minions :

[root@fedorabig func]# func "*" call fact list_fact_methods True
{'localhost.localdomain': {'__conflict__': {'kernel': ['kernel_version',
                                                       'cpu_model'],
                                            'runlevel': ['run_level',
                                                         'os_name']}}}

Note : If you have lots of queries it may useful to run the query "async" you may get timeout.