Re: Func Facts and extreme branch is becoming normal

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



makkalot wrote:
********* CAUTION A VERY LONG POST ********************

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.

Python API :
To use Func Facts you can use OverlordQueryProxy (i dont like the name if you have better please tell), as it name says it is a proxy object which acts like Overlord and a little bit more. When writing the facts code i didnt touch the Overlord class because it is already overloaded, and i think that should be the future behaviour of new added bits to func. Keeping Overlord as a little bit abstract may be a good idea. Examples are better:
Subclassing the Overlord class is probably a good idea. At least for the common cases, it wouldn't hurt
to have some canned subclasses ready to use.

   I'm drawing a blank on better names at the moment.



#using object as Overlord In [1]: from func.minion.facts.overlord_query import OverlordQueryProxy

In [2]: fc = OverlordQueryProxy("*")

In [3]: fc.echo.run_string("func blips")
Out[3]: {'localhost.localdomain': 'func blips'}

#to see the availible fact methods i have written a little minion module to show them ..
[root@fedorabig func]# func "*" call fact list_fact_modules
{'localhost.localdomain': ['hardware', 'fact_module']}

[root@fedorabig func]# 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']}

[root@fedorabig func]# func "*" call fact show_fact_module "hardware"
{'localhost.localdomain': {'description': 'A modules that supplies hardware facts',
                           'name': 'hardware',
                           'version': '0.0.1'}}


[root@fedorabig func]# 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'}}
Sounds reasonable API to me, mirrors the normal func module/method introspection.
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

[root@fedorabig func]# func "*" call fact call_fact "runlevel"
{'localhost.localdomain': '5'}
   What would be an example of fact methods that need args?

The ORM queries:

When Michael mentioned about facts idea i wanted to have something that can let me do some crazy chaining queries so i now i should explain a little bit the query stuff of the facts. With func facts you can run very complex queries. But lets go from easaier to hard :

In [3]: fc = OverlordQueryProxy("*")
In [6]: result=fc.filter(runlevel=4).echo.run_string("Hey func") #run that on machine that has runlevel 4
In [8]: fc.display_active(result)
Out[8]: {}

We have no results because my runlevel is different.

In [11]: fc = OverlordQueryProxy("*")

In [12]: result=fc.filter(runlevel__gt=4,runlevel__lt=6).echo.run_string("Hey func")

In [13]: fc.display_active(result)
Out[13]: {'localhost.localdomain': 'Hey func'}

The query above is for facts that are greater than 4 and less than 5 :) and we have a match

Lets do the same thing with async :

In [14]: fc = OverlordQueryProxy("*",async=True)

In [15]: result=fc.filter(runlevel__gt=4,runlevel__lt=6).echo.run_string("Hey func")

In [16]: result
Out[16]: '*-echo-run_string-1236893793.6649389'

In [17]: fc.job_status(result)
Out[17]: (1, {'localhost.localdomain': 'Hey func'})

Also you can do chaining when querying like that
In [11]: fc = OverlordQueryProxy("*")

In [12]: result = fc.filter(runlevel=5).filter(os__icontains="fedora").echo.run_string("Hey func")

In [13]: fc.display_active(result)
Out[13]: {'localhost.localdomain': 'Hey func'}
Whats the difference between passing two facts to check to filter, and chaining two fact checks together?

Sometimes you may need some more complex queries by OR'in and 'AND'in the stuff deeply , facts try to support that :

In [20]: fc = OverlordQueryProxy("*")

In [21]: result = fc.set_complexq(Q(os__icontains="fedora")|
Q(os__icontains="ubuntu")).echo.run_string("Hey func")

In [23]: fc.display_active(result)
Out[23]: {'localhost.localdomain': 'Hey func'}

A last example with heavy chaining :)
In [24]: fc = OverlordQueryProxy("*")

In [25]: result = fc.set_complexq(Q(os__icontains="fedora")|
Q(os__icontains="ubuntu")).filter(runlevel=5).echo.run_string("Hey func")

In [26]: fc.display_active(result)
Out[26]: {'localhost.localdomain': 'Hey func'}
Not sure what I think about the Q (query? ) api. My first thought is that maybe it should just be a little domain specific language for that, but thats probably a bad idea. The approach you take lets you construct the queries a little more programmatically than just query strings. What do the filter/queries look like over the wire? (shouldn't matter much, just curious)

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_method__keyword = some_value --> so os__icontains = "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.
   Cool.


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"
    run_level.description = "Shows the runlevel of the system"
    run_level.usage = "Can be used with all keywords"

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_value__keyword = blip if you dont assing the "tag" attribute it is ok again just when calling you should do it that way : hardware.run_level__keyword = blip Btw, i like the tag idea, be careful not to have 2 tags with same name, the last one will be availible :)
Hmm, kind of worried about the namespace issues there. Those kind of things tend to cause hard
to find bugs.

For more info you can check the examples ...


Under The Hood :
Well how the facts is actually working? What i did was really simle if you use OverlordQueryProxy and used the magical methods like filter and set_complexq , OverlordQueryProxy serializes the current query (ORs and ANDs) and inserts it as an argument to be sent to the minions. The server side (minion) deserializes it executes the logic parts (True and True etc) and if the result is True it continues to executes the actual parts (minion methods) if not returns back to overlord as signing __fact__ as False, so we know that minion doesnt match our query. By doing that we can do lots of thing with one connection or HIT.
   Cool.


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


Todo :
  -Solving bugs (fixing when writing that post :) )
  - Implement it so it can work from commandline, not sure about syntax
  - Maybe making Python Api more sweet
I would like to see the api a little sweeter. Could we do something like implement __gt__, __lt__, etc on the filter/query objects? We still have a serializing step in the middle though. That may require implementing minion and overlord versions of the filter/query types though, which might make adding types on the fly
a bit weird though.

Adrian

_______________________________________________
Func-list mailing list
Func-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/func-list

[Index of Archives]     [Fedora Users]     [Linux Networking]     [Fedora Legacy List]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]

  Powered by Linux