Test Failed
Pull Request — master (#603)
by
unknown
02:35
created

PacketOut.pack()   A

Complexity

Conditions 3

Size

Total Lines 9
Code Lines 8

Duplication

Lines 9
Ratio 100 %

Code Coverage

Tests 1
CRAP Score 9.0292

Importance

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