root/branch/UMPA/umpa/protocols/_.py @ 3257

Revision 3257, 12.9 kB (checked in by getxsick, 5 years ago)

IPAddrField improvement.

Now it handles with 2 types of data:
1 - strings as "127.0.0.1" or "0:0:0:0:0:0:0:1"
2 - tuples as (127,0,0,1) or (0,0,0,0,0,0,0,1)

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (C) 2008 Adriano Monteiro Marques.
5#
6# Author: Bartosz SKOWRON <getxsick at gmail dot com>
7#
8# This library is free software; you can redistribute it and/or modify
9# it under the terms of the GNU Lesser General Public License as published
10# by the Free Software Foundation; either version 2.1 of the License, or
11# (at your option) any later version.
12#
13# This library is distributed in the hope that it will be useful, but
14# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16# License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public License
19# along with this library; if not, write to the Free Software Foundation,
20# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
22import struct
23
24from umpa import utils
25from umpa.protocols._consts import *
26from umpa.utils.my_exceptions import *
27
28class Field(object):
29    """Superclass for fields.
30    To implement new fields, create subclass of this.
31
32    IMPORTANT: You should overwrite this __doc__ to get hints in some frontends
33    like the one provided by Umit Project.
34    """
35    bits = 0
36    auto = False
37    def __init__(self, name, value=None, bits=None, auto=None):
38        self.name = name
39        if auto:
40            self.auto = auto
41        elif value:
42            self.auto = True    # if there's default value, auto should be True
43
44        if bits:
45            self.bits = bits
46        self._value = value
47
48    def set(self, value):
49        if self._is_valid(value):
50            self._value = value
51        else:
52            raise UMPAAttributeException, value + ' not allowed'
53
54    def get(self):
55        return self._value
56
57    def clear(self):
58        self._value = None
59
60    def set_doc(self, text):
61        self.__doc__ = text
62
63    def _is_valid(self, val):
64        raise NotImplementedError, "this is abstract class"
65
66    def _pre_fillout(self):
67        pass
68
69    def _raw_value(self):
70        raise NotImplementedError, "this is abstract class"
71
72    def _generate_value(self):
73        raise UMPAException, "value is not defined or _generate_value() \
74                                            method is not implemented."
75
76    def fillout(self):
77        self._pre_fillout()
78
79        # we have to clear self._value if it was not defined
80        # because of later usage
81        if not self._value:
82            self._value = self._generate_value()
83            raw = self._raw_value()
84            self.clear()
85        else:
86            raw = self._raw_value()
87       
88        return raw
89
90class IntField(Field):
91    def _raw_value(self):
92        return self._value
93
94    def _is_valid(self, val):
95        """Check if a value is not bigger than expected.
96        """
97
98        if 2**self.bits > val:
99            return True
100        else:
101            return False
102
103class AddrField(Field):
104    pass
105
106class IPAddrField(AddrField):
107    """Main class for IP-style adresses.
108    It handles with 2 types of data:
109    1 - strings as "127.0.0.1" or "0:0:0:0:0:0:0:1"
110    2 - tuples as (127,0,0,1) or (0,0,0,0,0,0,0,1)
111    """
112    def set(self, value):
113        # convert list to tuple
114        if type(value) is list:
115            value = tuple(value)
116        # validation
117        if self._is_valid(value):
118            self._value = value
119        else:
120            raise UMPAAttributeException, value + ' not allowed'
121
122    def _raw_value(self):
123        if type(self._value) is str:
124            pieces = self._value.split(self.separator)
125        else:
126            pieces = self._value
127
128        raw = 0
129        for b in pieces:
130            raw += int(b, self.base)
131            raw <<= self.piece_size
132        raw >>= self.piece_size
133
134        return raw
135
136    def _is_valid(self, val):
137        if type(val) is str:
138            pieces = val.split(self.separator)
139        elif type(val) is tuple:
140            pieces = val
141        else:
142            return False
143
144        if len(pieces) != self.pieces_amount:
145            return False
146
147        for i in pieces:
148            if int(i, self.base) > 2**self.piece_size or int(i, self.base) < 0:
149                return False
150
151        return True
152
153class IPv4AddrField(IPAddrField):
154    """Address in IPv4 style.
155    """
156    separator = "."
157    piece_size = 8
158    pieces_amount = 4
159    base = 10
160
161class IPv6AddrField(IPAddrField):
162    """Address in IPv6 style.
163    """
164    separator = ":"
165    piece_size = 16
166    pieces_amount = 8
167    base = 16
168
169class PaddingField(IntField):
170    def _is_valid(self, val):
171        if isinstance(val, int):
172            return True
173        return False
174
175    def fillout(self):
176        self._pre_fillout()
177
178        if not self._value:
179            self.bits = self._generate_value()
180        else:
181            self.bits = self._value
182        return self._raw_value()
183   
184    def _raw_value(self):
185        return 0
186
187class Flags(Field):
188    """Most of protocols have a special field with bit-flags.
189    For those fields we use this subclass of Field.
190    """
191
192    def __init__(self, name, names, **preset):
193        """Names has to be in correct order.
194        If you use **preset, check if keys are in names list as well
195        because of order issue.
196        """
197        super(Flags, self).__init__(name, bits=len(names))
198
199        self._ordered_fields = names
200        # we overwrite an attribute self._value
201        # because we need a list instead of simple var here
202        self._value = {}
203        for flag in self._ordered_fields:
204            self._value[flag] = BitField(flag)
205        #self._value = dict.fromkeys(self._ordered_fields, False)
206
207        # if **preset exists then we update values
208        for name in preset:
209            if preset[name] == True:
210                self.set(name)
211            else:
212                self.unset(name)
213
214    def _is_valid(self, name):
215        return self._value.has_key(name)
216
217    def _set_bit(self, names, value):
218        for flag_name in names:
219            if self._is_valid(flag_name):
220                self._value[flag_name].set(value)
221            else:
222                raise UMPAAttributeException, attr + ' not allowed'
223
224    def set(self, *names):
225        self._set_bit(names, True)
226
227    def unset(self, *names):
228        self._set_bit(names, False)
229
230    def get(self, *names):
231        # we check if name of the field in the flag is correct
232        result = [ self._value[val].get() for val in names
233                                                    if self._is_valid(val) ]
234
235        # if no results above we return whole list of values
236        if len(result) < 1:
237            result = self._value
238        return result
239
240    def _pre_fillout(self):
241        pass
242
243    def fillout(self):
244        self._pre_fillout()
245
246        raw = 0
247        for bitname in self._ordered_fields:
248            raw += self._value[bitname].fillout()
249            raw <<= 1
250        raw >>= 1
251        return raw
252
253class BitField(Field):
254    bits = 1
255    def __init__(self, name, value=None, auto=None):
256        super(BitField, self).__init__(name, value, BitField.bits, auto)
257       
258        # we store value as a _default_view, it's necessary by generic
259        # fillout, so for most of cases we don't need to make subclasses with
260        # distinct fillout() method
261        self._default_value = value
262        if self._default_value:
263            self.auto = True
264
265    def _is_valid(self, val):
266        # always True because it's bool type
267        return True
268
269    def get(self):
270        return bool(self._value)
271
272    def fillout(self):
273        if self._value is None:
274            self._value = self._generate_value()
275            raw = self._raw_value()
276            self.clear()
277        else:
278            raw = self._raw_value()
279
280        return raw
281
282    def _generate_value(self):
283        """Generate value of bit.
284       
285        Be default it checks if self._default_value is defined,
286        if so it returns this value.
287       
288        If you need more complex action,
289        create subclass and overwrite this method.
290        """
291        if self._default_value:
292            return bool(self._default_value)
293        else:
294            raise UMPAException, "value is not defined or _generate_value() \
295                                                    method is not implemented."
296
297    def _raw_value(self):
298        if self._value:
299            return 1
300        else:
301            return 0
302
303class Protocol(object):
304    """Superclass for protocols.
305    To implement new protocol, make a subclass.
306   
307    IMPORTANT: You should overwrite this __doc__ to get hints in some frontends
308    like the one provided by Umit Project.
309    """
310    _ordered_fields = ()
311    layer = None
312
313    def __init__(self, fields, **kw):
314        #self._fields = {}
315        super(Protocol, self).__setattr__('_fields', fields)
316
317    def __setattr__(self, attr, val):
318        """Set value of the field."""
319
320        # we can do the same without _is_valid() with just try/except section
321        # but Francesco asked me about this method
322        if self._is_valid(attr):
323            self._get_field(attr).set(val)
324        else:
325            raise UMPAAttributeException, attr + ' not allowed'
326
327    def __getattr__(self, attr):
328        """Return value of the field."""
329        if self._is_valid(attr):
330            return self._get_field(attr).get()
331        else:
332            raise UMPAAttributeException, attr + ' not allowed'
333
334    def get_fields(self):
335        """Generator for ordered fields."""
336        for field in self._ordered_fields:
337            yield self._get_field(field)
338
339    @staticmethod
340    def get_fields_keys():
341        """Generator for ordered names (keys) of header's fields.
342       
343        I don't see any reason to use this method but
344        Francesco asked me to make this.
345
346        NOTE: You should use get_fields() method instead.
347        """
348        for field in Protocol._ordered_fields:
349            yield field
350   
351    def _get_field(self, keyname):
352        if self._is_valid(keyname):
353            return self._fields[keyname]
354        else:
355            raise UMPAAttributeException, keyname + ' not allowed'
356
357    def set_fields(self, *args, **kwargs):
358        """Set fields of the protocol.
359        There are 2 ways to do that with using tuple or dict-style.
360        """
361        # converting args list to the dict and update our kwargs
362        kwargs.update(utils.dict_from_sequence(args))
363
364        for key in kwargs:
365            if self._is_valid(key):
366                setattr(self, key, kwargs[key])
367            self.fields[key].set(kwargs[key])
368
369    def set_flags(self, name, *args, **kw):
370        """Set flags with dict using.
371
372        There are 2 ways to do that with using tuple or dict-style.
373
374        There is no effect if the protocol doesn't have this field.
375        """
376
377        # converting args list to the dict and update our kwargs
378        kw.update(util.dict_from_sequence(args))
379
380        flag_field = self._get_field(name)
381        if isinstance(flag_field, Flags):
382            for flag_name in kw:
383                if kw[flag_name]:
384                    flag_field.set(flag_name)
385                else:
386                    flag_field.unset(flag_name)
387        else:
388            raise UMPAAttributeException, "No Flags instance for " + name
389
390    def get_flags(self, name, *args):
391        flag_field = self._get_field(name)
392        if isinstance(flag_field, Flags):
393            return flag_field.get(*args)
394        else:
395            raise UMPAAttributeException, "No Flags instance for " + name
396
397    def get_raw(self):
398        """Return raw bits of the protocol's object."""
399
400        # The deal: we join all value's fields into one big number
401        # (with taking care about amount of bits).
402        # then we devide the number on byte-chunks
403        # and pack it by struct.pack() function
404        bit, raw_value = self._raw()
405
406        # protocol should return byte-compatible length
407        if bit%BYTE != 0:
408            raise UMPAException, 'odd number of bits in ' + self.__name__
409
410        # check how many bytes we need
411        bytes = bit/BYTE
412        # split the number on byte-chunks
413        l = [ (raw_value & (0xff << (BYTE*i))) >> BYTE*i
414                                    for i in reversed(xrange(bytes)) ]
415        # and pack it
416        header_pack = struct.pack('!' + 'B'*bytes, *l)
417        return header_pack
418
419    def _is_valid(self, field):
420        """Overload it in subclasses."""
421        raise NotImplementedError
422
423    def get_offset(self, field):
424        """Return offset for the field.
425       
426        NOTE: the argument field CAN be key or instance.
427        """
428
429        # checking if argument is a key or instance
430        if isinstance(field, str):
431            field_list = self._ordered_fields
432        elif isinstance(field, Field):
433            field_list = [ f for f in self.get_fields() ]
434        else:
435            raise UMPAException, type(field) + ' unsupported'
436   
437        if field not in field_list:
438            raise UMPAAttributeException, field + ' not allowed'
439
440        offset = 0
441        for i, f in enumerate(field_list):
442            if field == f:
443                break
444            offset += self._get_field(self._ordered_fields[i]).bits
445        return offset
Note: See TracBrowser for help on using the browser.