Passed
Push — master ( decbca...4a5a86 )
by Beraldo
04:29 queued 02:41
created

PacketOut.__init__()   A

Complexity

Conditions 2

Size

Total Lines 23
Code Lines 8

Duplication

Lines 23
Ratio 100 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 8
dl 23
loc 23
ccs 6
cts 6
cp 1
rs 10
c 0
b 0
f 0
cc 2
nop 6
crap 2
1
"""For the controller to send a packet out through the datapath."""
2 1
from copy import deepcopy
3
4 1
from pyof.foundation.base import GenericMessage
5 1
from pyof.foundation.basic_types import BinaryData, Pad, UBInt16, UBInt32
6 1
from pyof.foundation.constants import UBINT32_MAX_VALUE
7 1
from pyof.foundation.exceptions import PackException, ValidationError
8 1
from pyof.v0x04.common.action import ListOfActions
9 1
from pyof.v0x04.common.header import Header, Type
10 1
from pyof.v0x04.common.port import PortNo
11
12 1
__all__ = ('PacketOut',)
13
14
# Classes
15
16
#: in_port valid virtual port values, for validation
17 1
_VIRT_IN_PORTS = (PortNo.OFPP_LOCAL, PortNo.OFPP_CONTROLLER, PortNo.OFPP_ANY)
18
19
20 1 View Code Duplication
class PacketOut(GenericMessage):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
21
    """Send packet (controller -> datapath)."""
22
23
    #: Openflow :class:`~pyof.v0x04.common.header.Header`
24 1
    header = Header(message_type=Type.OFPT_PACKET_OUT)
25
    #: ID assigned by datapath (OFP_NO_BUFFER if none).
26 1
    buffer_id = UBInt32()
27
    #: Packet’s input port or OFPP_CONTROLLER.
28 1
    in_port = UBInt32()
29
    #: Size of action array in bytes.
30 1
    actions_len = UBInt16()
31
    #: Padding
32 1
    pad = Pad(6)
33
    #: Action List.
34 1
    actions = ListOfActions()
35
    #: Packet data. The length is inferred from the length field in the header.
36
    #:    (Only meaningful if buffer_id == -1.)
37 1
    data = BinaryData()
38
39 1
    def __init__(self, xid=None, buffer_id=UBINT32_MAX_VALUE,
40
                 in_port=PortNo.OFPP_CONTROLLER, actions=None,
41
                 data=b''):
42
        """Create a PacketOut with the optional parameters below.
43
44
        Args:
45
            xid (int): xid of the message header.
46
            buffer_id (int): ID assigned by datapath (-1 if none). In this case
47
                UBINT32_MAX_VALUE is -1 for the field.
48
            in_port (:class:`int`, :class:`~pyof.v0x04.common.port.Port`):
49
                Packet's input port (:attr:`Port.OFPP_NONE` if none).
50
                Virtual ports OFPP_IN_PORT, OFPP_TABLE, OFPP_NORMAL,
51
                OFPP_FLOOD, and OFPP_ALL cannot be used as input port.
52
            actions (:class:`~pyof.v0x04.common.action.ListOfActions`):
53
                List of Action instances.
54
            data (bytes): Packet data. The length is inferred from the length
55
                field in the header. (Only meaningful if ``buffer_id`` == -1).
56
        """
57 1
        super().__init__(xid)
58 1
        self.buffer_id = buffer_id
59 1
        self.in_port = in_port
60 1
        self.actions = [] if actions is None else actions
61 1
        self.data = data
62
63 1
    def validate(self):
64
        """Validate the entire message."""
65 1
        if not super().is_valid():
66
            raise ValidationError()
67 1
        self._validate_in_port()
68
69 1
    def is_valid(self):
70
        """Answer if this message is valid."""
71 1
        try:
72 1
            self.validate()
73 1
            return True
74
        except ValidationError:
75
            return False
76
77 1
    def pack(self, value=None):
78
        """Update the action_len attribute and call super's pack."""
79
        if value is None:
80
            self._update_actions_len()
81
            return super().pack()
82
        elif isinstance(value, type(self)):
83
            return value.pack()
84
        else:
85
            msg = "{} is not an instance of {}".format(value,
86
                                                       type(self).__name__)
87
            raise PackException(msg)
88
89 1
    def unpack(self, buff, offset=0):
90
        """Unpack a binary message into this object's attributes.
91
92
        Unpack the binary value *buff* and update this object attributes based
93
        on the results. It is an inplace method and it receives the binary data
94
        of the message **without the header**.
95
96
        This class' unpack method is like the :meth:`.GenericMessage.unpack`
97
        one, except for the ``actions`` attribute which has a length determined
98
        by the ``actions_len`` attribute.
99
100
        Args:
101
            buff (bytes): Binary data package to be unpacked, without the
102
                header.
103
            offset (int): Where to begin unpacking.
104
        """
105
        begin = offset
106
        for attribute_name, class_attribute in self.get_class_attributes():
107
            if type(class_attribute).__name__ != "Header":
108
                attribute = deepcopy(class_attribute)
109
                if attribute_name == 'actions':
110
                    length = self.actions_len.value
111
                    attribute.unpack(buff[begin:begin+length])
112
                else:
113
                    attribute.unpack(buff, begin)
114
                setattr(self, attribute_name, attribute)
115
                begin += attribute.get_size()
116
117 1
    def _update_actions_len(self):
118
        """Update the actions_len field based on actions value."""
119
        if isinstance(self.actions, ListOfActions):
120
            self.actions_len = self.actions.get_size()
121
        else:
122
            self.actions_len = ListOfActions(self.actions).get_size()
123
124 1
    def _validate_in_port(self):
125 1
        is_valid_range = self.in_port > 0 and self.in_port <= PortNo.OFPP_MAX
126 1
        is_valid_virtual_in_ports = self.in_port in _VIRT_IN_PORTS
127
128 1
        if (is_valid_range or is_valid_virtual_in_ports) is False:
129
            raise ValidationError(f'{self.in_port} is not a valid input port.')
130