class ActionSetField(ActionHeader): """Action structure for OFPAT_SET_FIELD.""" field = OxmTLV() _allowed_types = (ActionType.OFPAT_SET_FIELD, ) def __init__(self, field=None): """Create a ActionSetField with the optional parameters below. Args: length (int): length padded to 64 bits, followed by exactly oxm_len bytes containing a single OXM TLV, then exactly ((oxm_len + 4) + 7)/8*8 - (oxm_len + 4) (between 0 and 7) bytes of all-zero bytes field (:class:`OxmTLV`): OXM field and value. """ super().__init__(action_type=ActionType.OFPAT_SET_FIELD) self.field = OxmTLV() if field is None else field def __repr__(self): return (f"{type(self).__name__}({self.field.oxm_field!s}, " f"{self.field.oxm_value})") def pack(self, value=None): """Pack this structure updating the length and padding it.""" self._update_length() packet = super().pack() return self._complete_last_byte(packet) def _update_length(self): """Update the length field of the struct.""" action_length = 4 + len(self.field.pack()) overflow = action_length % 8 self.length = action_length if overflow: self.length = action_length + 8 - overflow def _complete_last_byte(self, packet): """Pad until the packet length is a multiple of 8 (bytes).""" padded_size = self.length padding_bytes = padded_size - len(packet) if padding_bytes > 0: packet += Pad(padding_bytes).pack() return packet
class ActionSetField(ActionHeader): """Action structure for OFPAT_SET_FIELD.""" field = OxmTLV() _allowed_types = ActionType.OFPAT_SET_FIELD, def __init__(self, field=None): """Create a ActionSetField with the optional parameters below. Args: length (int): length padded to 64 bits, followed by exactly oxm_len bytes containing a single OXM TLV, then exactly ((oxm_len + 4) + 7)/8*8 - (oxm_len + 4) (between 0 and 7) bytes of all-zero bytes field (:class:`OxmTLV`): OXM field and value. """ super().__init__(action_type=ActionType.OFPAT_SET_FIELD) self.field = OxmTLV() if field is None else field def pack(self, value=None): """Pack this structure updating the length and padding it.""" self._update_length() packet = super().pack() return self._complete_last_byte(packet) def _update_length(self): """Update the length field of the struct.""" action_length = 4 + len(self.field.pack()) overflow = action_length % 8 self.length = action_length if overflow: self.length = action_length + 8 - overflow def _complete_last_byte(self, packet): """Pad until the packet length is a multiple of 8 (bytes).""" padded_size = self.length padding_bytes = padded_size - len(packet) if padding_bytes > 0: packet += Pad(padding_bytes).pack() return packet
class TestOxmTLV(TestCase): """Test OXM TLV pack and unpack.""" def setUp(self): """Instantiate an OXM TLV struct.""" self.tlv = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_IN_PHY_PORT, oxm_hasmask=False, oxm_value=b'') def test_different_class_types(self): """Pack, unpack the result and assert the values are equal.""" for oxm_class in (OxmClass.OFPXMC_OPENFLOW_BASIC, OxmClass.OFPXMC_EXPERIMENTER): self.tlv.oxm_class = oxm_class unpacked = self._create_from_pack() self.assertEqual(oxm_class, unpacked.oxm_class) def test_different_fields(self): """Pack, unpack the result and assert the values are equal.""" for oxm_field in (OxmOfbMatchField.OFPXMT_OFB_IN_PORT, OxmOfbMatchField.OFPXMT_OFB_IPV6_EXTHDR): self.tlv.oxm_field = oxm_field unpacked = self._create_from_pack() self.assertEqual(oxm_field, unpacked.oxm_field) def test_hasmask_bit(self): """Pack, unpack the result and assert the values are equal.""" for oxm_hasmask in True, False: self.tlv.oxm_hasmask = oxm_hasmask unpacked = self._create_from_pack() self.assertEqual(oxm_hasmask, unpacked.oxm_hasmask) def test_different_values(self): """Pack, unpack the result and assert the values are equal.""" for oxm_value in b'', b'abc': self.tlv.oxm_value = oxm_value unpacked = self._create_from_pack() self.assertEqual(oxm_value, unpacked.oxm_value) def _create_from_pack(self): """Return a new instance by unpacking self.tlv.pack().""" unpacked = OxmTLV() unpacked.unpack(self.tlv.pack()) return unpacked def test_pack_overflowed_field(self): """Raise PackException if field is bigger than 7 bit.""" self.tlv.oxm_class = OxmClass.OFPXMC_EXPERIMENTER self.tlv.oxm_field = 2**7 with self.assertRaises(PackException): self.tlv.pack() def test_pack_invalid_field(self): """Raise PackException if field is invalid for a class. Example: field 42 is invalid for oxm_class OFPXMC_OPENFLOW_BASIC. """ self.tlv.oxm_class = OxmClass.OFPXMC_OPENFLOW_BASIC self.tlv.oxm_field = 42 with self.assertRaises(PackException): self.tlv.pack() def test_unpack_invalid_field(self): """Raise UnpackException if field is invalid for a class. Example: field 42 is invalid for oxm_class OFPXMC_OPENFLOW_BASIC. """ field42 = b'\x80\x00T\x00' tlv = OxmTLV() with self.assertRaises(UnpackException): tlv.unpack(field42) def test_max_field_value(self): """Use all bits of oxm_field.""" self.tlv.oxm_class = OxmClass.OFPXMC_EXPERIMENTER self.tlv.oxm_field = 127 unpacked = OxmTLV() unpacked.unpack(self.tlv.pack()) self.assertEqual(self.tlv, unpacked)