================ Plugins System ================ Introduction ============ How plugins system works in UMPA and how to write your own extension or protocol. Overview ======== UMPA has not a real plugin system. But there is an imitation of this and it works very well. There are 2 independent groups of plugins: 1. protocols 2. extensions Each of them work in different way, a structure of plugins are completely different. The only similar thing is a location for local plugins. So, there are provided some plugins with UMPA. Extensions like XML or schedule. Protocols like IP or TCP. As you have already known, we can use them in very simple way just by importing them. .. code-block:: python import umit.umpa.extensions.XML from umit.umpa.extensions import schedule import umit.umpa.protocols.IP But we can write our owns plugins and store them in our $HOME directory. By importing UMPA first time, it will create a special directory $HOME/.umpa and this directory is important keep some configs and plugins. There is a sub-directory ``$HOME/.umpa/umpa_plugins`` with ``extensions`` and ``protocols`` sub-directories. So you can keep your own plugins there. Because it's just an imitation of the real plugins system, there is no interface, and no real API for it. In fact, we can just write normal modules, like the core code of the UMPA. So, sometimes we need to handle with private "things". I know, it's ugly. I'm going to rewrite it someday. Sorry about this! available plugins ----------------- There are 3 functions for ``umit.umpa.protocols`` and ``umit.umpa.extensions`` to get available protocols/extensions: * ``get_all()`` - return both local and global plugins * ``get_globals()`` - return only global plugins * ``get_locals()`` - return only local plugins .. note:: protocols' plugins are loaded automatically, but extensions' plugins not. Protocols ========= Firstly, just read any already implemented protocols to see how it works. But here is pointed some important issues. There are 3 common modules which we should use to create new protocol implementation: 1. ``umit.umpa.protocols._protocols`` -- with our the super-class ``Protocol`` 2. ``umit.umpa.protocols._fields`` -- with many common classes of fields 3. ``umit.umpa.protocols._consts`` -- with common constansts protocol class in general ------------------------- Ok, let's call our protocol ``Foo`` and create class. .. code-block:: python from umit.umpa.protocols import _protocols from umit.umpa.protocols import _fields class Foo(Protocol): layer = 4 # layer of OSI model where protocol is situated protocol_id = 1234 # read below >> 1 << name = "Foo" # name of the protocol, usually the same as name of the class _ordered_fields = ('field1', 'field2', '_field3') # read below >> 2 << def __init__(self, **kwargs): flags = ('bit1', 'bit2') # read below >> 3 << flags_predefined = dict.fromkeys(flags, 0) fields_list = [ _HField1("Field1"), # read below >> 4 << _fields.Flags("Flags", flags, **flags_predefined), _HField3("Field3", 666) ] super(Foo, self).__init__(fields_list, **kwargs) # read below >> 5 << OK, we've already created our ``Foo`` class. It's not finished yet but...:) The most obvious lines are commented in the line-comment. The rest is below: 1. ``protocol_id`` class attribute is an general attribite for any ID of the protocol. Usually it's used by a lower layer for information what protocol is upper. E.g. in Ethernet protocol there is a field EtherType and there will be 0x0800 if upper protocol is IP. So, in this case protocol_id for IP should be 0x0800. 2. ``_ordered_fields`` -- this is a tuple of fields in the order as we want to see it in the header of our protocol. Items of the tuple are names of fields. User uses these names like ``foo.field1`` if he wants to access to them. Please note about underscored '_' prefix. It says that the field is auto-filling and shouldn't be modify by the user. 3. The second field is a flag-type. Like control bits field in TCP with bit flags like SYN, FIN, ACK etc. Items the tuple are names of each bit-flag. The next line predefined them to default values (0 for every bits in this case). 4. This list contains fields-objects. We can use already written classes provided by ``umit.umpa.protocols._fields`` (like ``Flags``) or create new subclasses as we are going to do with two fields. Arguments for constructor are: ``name, value=None, bits=None, auto=None``. We define a default value form Field3 but omit rest because we will set them in classes directly. Please note, that the order of the list must be same as the order of ``_ordered_fields`` tuple. 5. We call a constructor from ``Protocol``. It handles with \*\*kwargs and set a lot of things for us. We need to implement 2 methods, but let's take care about our 2 fields first. fields classes -------------- ``_H`` prefix for classes names is only a pattern. I like it, you don't have to. Every field's class has to inherit from the ``umit.umpa.protocols.fields.Field`` class or subclasses. Our fields are number-type. Field1 should be set by user, and Field3 is auto-filling. .. code-block:: python import random class _HField1(_fields.IntField): bits = 8 auto = False class _HField3(_fields.IntField): bits = 4 auto = True def _generate_value(self): return random.randint(0, 15) As you see the first field is pretty simple. We set a length of it in bits, and auto attribute. Because it has to be set manually, we don't have to do anything more. If user doesn't set the value, the ``UMPAException`` will raise. It will happen because: 1) we don't have any value (default or set by user), 2) we don't have overridden ``_generate_value()`` method. Pretty simple isn't? For the Field3 we implemented ``_generate_value()`` method and it returns random numbers. But, please remember that we set default value of the field to 666. So, it won't call ``_generate_value()`` unless user clear the current value of the field. pre/post raw methods -------------------- Ok, we left two methods from our ``Foo`` class, so let's implement them now. They are called ``_pre_raw()`` and ``_post_raw()``. Usually we can just return 2 arguments and skip them but for some cases we need to write a bit more. Some fields depend on other fields or on protocols from other layers. For example checksum header fields handles with other fields. This issue is a bit advanced, so if you are interest how to solve it, read the code of IP and TCP implementations and about ``SpecialIntField`` class. These protocols have several fields which handle with others and they cover most of the cases. If you need a help, please contact with us. Perhaps, we will write an example of this as well. For our example - this code is enough: .. code-block:: python def _pre_raw(self, raw_value, bit, protocol_container, protocol_bits): return raw_value, bit def _post_raw(self, raw_value, bit, protocol_container, protocol_bits): return raw_value, bit last lines of the code ---------------------- Ok, we have already implemented successfully our Foo protocol. We only need to add 2 lines at the end of the file: .. code-block:: python protocols = [ Foo, ] __all__ = [ "Foo", ] The ``protocols`` list is used by the plugin mechanism. If we store our module in ``$HOME/.umpa/umpa_plugins/protocols``, it will be automatically loaded by ``import umit.umpa.protocols``. We can just import the class by ``from umit.umpa.protocols import Foo``. Also, we can check our protocols' plugins by calling ``umit.umpa.protocols.get_locals()``. Extensions ========== This kind of plugins is fairly simple. And in fact, there is no any API. new extension: the hacker ------------------------- Let's create an extenstion which will print into STDERR "Hello <name>, you are the real hacker!" phrase. All we really need is: .. code-block:: python import sys def say_hello(name): print >> sys.stderr, "Hello %s, you are the real hacker!" % name Yep, that it! loading extensions ------------------ If we store the extension in ``$HOME/.umpa/umpa_plugins/extensions/hacker.py``, we have 2 ways to import it. .. code-block:: python # first way import umpa_plugins.extensions.hacker # second way import umit.umpa.extensions umit.umpa.extensions.load_extension('hacker') After that, we can simple call the new extension if we want it. .. code-block:: python umpa_plugins.extensions.hacker.say_hello("Alice") # for the former importing style umit.umpa.extensions.hacker.say_hello("Bob") # for the second one extending the hacker -------------------- Because UMPA is written in the Python we can dynamically add something to our objects. So let's our ``say_hello()`` function as a ``Packet`` method! .. code-block:: python import umit.umpa def _say_hello_method(self, name): say_hello(name) umit.umpa.Packet.say_hello = _say_hello_method Now, we can call our method for Packet's objects! As you should noticed, you can override some methods by this way! So your extension can be completely integrated with UMPA!
