diff --git a/src/ovsdb_subordinate/provides.py b/src/ovsdb_subordinate/provides.py index affd9cf..b8d374f 100644 --- a/src/ovsdb_subordinate/provides.py +++ b/src/ovsdb_subordinate/provides.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import subprocess + import charms.reactive as reactive # the reactive framework unfortunately does not grok `import as` in conjunction @@ -27,107 +29,75 @@ from .ovsdb_subordinate_common import hash_hexdigest class OVSDBSubordinateProvides(Endpoint): - """This interface is used on a principle charm to connect to subordinate - """ + """This interface is used on the subordinate side of the relation""" - @property - def chassis_name(self): - """Retrieve chassis-name from relation data + def _get_ovs_value(self, tbl, col, rec=None): + """Get value of column in record in table - :returns: Chassis name as provided on subordinate relation - :rtype: str + :param tbl: Name of table + :type tbl: str + :param col: Name of column + :type col: str + :param rec: Record ID + :type rec: Optional[str] + :raises: subprocess.CalledProcessError """ - return self.all_joined_units.received.get('chassis-name', '') + cp = subprocess.run(('ovs-vsctl', 'get', tbl, rec or '.', col), + stdout=subprocess.PIPE, + check=True, universal_newlines=True) + return cp.stdout.rstrip().replace('"', '').replace("'", '') - @property - def ovn_configured(self): - """Retrieve whether OVN is configured from relation data - - :returns: True or False - :rtype: bool - """ - return self.all_joined_units.received.get('ovn-configured', False) - - def _add_interface_request(self, bridge, ifname, ifdata): - """Retrieve interface requests from relation and add/update requests - - :param bridge: Name of bridge - :type bridge: str - :param ifname: Name of interface - :type ifname: str - :param ifdata: Data to be attached to interface in Open vSwitch - :type ifdata: Dict[str,Union[str,Dict[str,str]]] - """ + def publish_chassis_name(self): + """Publish chassis name""" + ovs_hostname = self._get_ovs_value('Open_vSwitch', + 'external_ids:hostname') for relation in self.relations: - relation_ifs = relation.to_publish.get('create-interfaces', {}) - relation_ifs.update({bridge: {ifname: ifdata}}) - relation.to_publish['create-interfaces'] = relation_ifs + relation.to_publish['chassis-name'] = ovs_hostname - def _interface_requests(self): - """Retrieve interface requests from relation + def publish_ovn_configured(self): + """Publish whether OVN is configured in the local OVSDB""" + ovn_configured = False + try: + self._get_ovs_value('Open_vSwitch', 'external_ids:ovn-remote') + ovn_configured = True + except subprocess.CalledProcessError: + # No OVN + pass + + for relation in self.relations: + relation.to_publish['ovn-configured'] = ovn_configured + + @property + def interface_requests(self): + """Retrieve current interface requests :returns: Current interface requests - :rtype: Optional[Dict[str,Union[str,Dict[str,str]]]] + :rtype: Dict[str,Union[str,Dict[str,str]]] """ + return self.all_joined_units.received.get('create-interfaces', {}) + + def interface_requests_handled(self): + """Notify peer that interface requests has been dealt with + + Sets a hash of request data back on relation to signal to the other end + it has been dealt with so it can proceed. + + Note that we do not use the reactive request response pattern library + as we do not have use for per-unit granularity and we do not have + actual useful data to return. + """ + # The raw data is a json dump using sorted keys + ifreq_hexdigest = hash_hexdigest( + self.all_joined_units.received_raw['create-interfaces']) for relation in self.relations: - return relation.to_publish_raw.get('create-interfaces') - - def create_interface(self, bridge, ifname, ethaddr, ifid, - iftype=None, ifstatus=None): - """Request system interface created and attach it to CMS - - Calls to this function are additive so a principle charm can request to - have multiple interfaces created and maintained. - - The flag {endpoint_name}.{interface_name}.created will be set when - ready. - - :param bridge: Bridge the new interface should be created on - :type bridge: str - :param ifname: Interface name we want the new netdev to get - :type ifname: str - :param ethaddr: Ethernet address we want to attach to the netdev - :type ethaddr: str - :param ifid: Unique identifier for port from CMS - :type ifid: str - :param iftype: Interface type, defaults to 'internal' - :type iftype: Optional[str] - :param ifstatus: Interface status, defaults to 'active' - :type ifstatus: Optional[str] - """ - # The keys in the ifdata dictionary map directly to column names in the - # OpenvSwitch Interface table as defined in DB-SCHEMA [0] referenced in - # RFC 7047 [1] - # - # There are some established conventions for keys in the external-ids - # column of various tables, consult the OVS Integration Guide [2] for - # more details. - # - # NOTE(fnordahl): Technically the ``external-ids`` column is called - # ``external_ids`` (with an underscore) and we rely on ``ovs-vsctl``'s - # behaviour of transforming dashes to underscores for us [3] so we can - # have a more pleasant data structure. - # - # 0: http://www.openvswitch.org/ovs-vswitchd.conf.db.5.pdf - # 1: https://tools.ietf.org/html/rfc7047 - # 2: http://docs.openvswitch.org/en/latest/topics/integration/ - # 3: https://github.com/openvswitch/ovs/blob/ - # 20dac08fdcce4b7fda1d07add3b346aa9751cfbc/ - # lib/db-ctl-base.c#L189-L215 - ifdata = { - 'type': iftype or 'internal', - 'external-ids': { - 'iface-id': ifid, - 'iface-status': ifstatus or 'active', - 'attached-mac': ethaddr, - }, - } - self._add_interface_request(bridge, ifname, ifdata) + relation.to_publish['interfaces-created'] = ifreq_hexdigest reactive.clear_flag( - self.expand_name('{endpoint_name}.interfaces.created')) + self.expand_name('{endpoint_name}.interfaces.new_requests')) @when('endpoint.{endpoint_name}.joined') def joined(self): + self.publish_chassis_name() + self.publish_ovn_configured() reactive.set_flag(self.expand_name('{endpoint_name}.connected')) reactive.set_flag(self.expand_name('{endpoint_name}.available')) @@ -136,14 +106,10 @@ class OVSDBSubordinateProvides(Endpoint): reactive.clear_flag(self.expand_name('{endpoint_name}.available')) reactive.clear_flag(self.expand_name('{endpoint_name}.connected')) - @when('endpoint.{endpoint_name}.changed.interfaces-created') + @when('endpoint.{endpoint_name}.changed.create-interfaces') def new_requests(self): - ifreq = self._interface_requests() - - if ifreq is not None and self.all_joined_units.received[ - 'interfaces-created'] == hash_hexdigest(ifreq): - reactive.set_flag( - self.expand_name('{endpoint_name}.interfaces.created')) - reactive.clear_flag( - self.expand_name( - 'endpoint.{endpoint_name}.changed.interfaces-created')) + reactive.set_flag( + self.expand_name('{endpoint_name}.interfaces.new_requests')) + reactive.clear_flag( + self.expand_name( + 'endpoint.{endpoint_name}.changed.create-interfaces')) diff --git a/src/ovsdb_subordinate/requires.py b/src/ovsdb_subordinate/requires.py index 0df3d5e..ba30ebd 100644 --- a/src/ovsdb_subordinate/requires.py +++ b/src/ovsdb_subordinate/requires.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import subprocess - import charms.reactive as reactive # the reactive framework unfortunately does not grok `import as` in conjunction @@ -29,75 +27,107 @@ from .ovsdb_subordinate_common import hash_hexdigest class OVSDBSubordinateRequires(Endpoint): - """This interface is used on the subordinate side of the relation""" - - def _get_ovs_value(self, tbl, col, rec=None): - """Get value of column in record in table - - :param tbl: Name of table - :type tbl: str - :param col: Name of column - :type col: str - :param rec: Record ID - :type rec: Optional[str] - :raises: subprocess.CalledProcessError - """ - cp = subprocess.run(('ovs-vsctl', 'get', tbl, rec or '.', col), - stdout=subprocess.PIPE, - check=True, universal_newlines=True) - return cp.stdout.rstrip().replace('"', '').replace("'", '') - - def publish_chassis_name(self): - """Publish chassis name""" - ovs_hostname = self._get_ovs_value('Open_vSwitch', - 'external_ids:hostname') - for relation in self.relations: - relation.to_publish['chassis-name'] = ovs_hostname - - def publish_ovn_configured(self): - """Publish whether OVN is configured in the local OVSDB""" - ovn_configured = False - try: - self._get_ovs_value('Open_vSwitch', 'external_ids:ovn-remote') - ovn_configured = True - except subprocess.CalledProcessError: - # No OVN - pass - - for relation in self.relations: - relation.to_publish['ovn-configured'] = ovn_configured + """This interface is used on a principle charm to connect to subordinate + """ @property - def interface_requests(self): - """Retrieve current interface requests + def chassis_name(self): + """Retrieve chassis-name from relation data + + :returns: Chassis name as provided on subordinate relation + :rtype: str + """ + return self.all_joined_units.received.get('chassis-name', '') + + @property + def ovn_configured(self): + """Retrieve whether OVN is configured from relation data + + :returns: True or False + :rtype: bool + """ + return self.all_joined_units.received.get('ovn-configured', False) + + def _add_interface_request(self, bridge, ifname, ifdata): + """Retrieve interface requests from relation and add/update requests + + :param bridge: Name of bridge + :type bridge: str + :param ifname: Name of interface + :type ifname: str + :param ifdata: Data to be attached to interface in Open vSwitch + :type ifdata: Dict[str,Union[str,Dict[str,str]]] + """ + for relation in self.relations: + relation_ifs = relation.to_publish.get('create-interfaces', {}) + relation_ifs.update({bridge: {ifname: ifdata}}) + relation.to_publish['create-interfaces'] = relation_ifs + + def _interface_requests(self): + """Retrieve interface requests from relation :returns: Current interface requests - :rtype: Dict[str,Union[str,Dict[str,str]]] + :rtype: Optional[Dict[str,Union[str,Dict[str,str]]]] """ - return self.all_joined_units.received.get('create-interfaces', {}) - - def interface_requests_handled(self): - """Notify peer that interface requests has been dealt with - - Sets a hash of request data back on relation to signal to the other end - it has been dealt with so it can proceed. - - Note that we do not use the reactive request response pattern library - as we do not have use for per-unit granularity and we do not have - actual useful data to return. - """ - # The raw data is a json dump using sorted keys - ifreq_hexdigest = hash_hexdigest( - self.all_joined_units.received_raw['create-interfaces']) for relation in self.relations: - relation.to_publish['interfaces-created'] = ifreq_hexdigest + return relation.to_publish_raw.get('create-interfaces') + + def create_interface(self, bridge, ifname, ethaddr, ifid, + iftype=None, ifstatus=None): + """Request system interface created and attach it to CMS + + Calls to this function are additive so a principle charm can request to + have multiple interfaces created and maintained. + + The flag {endpoint_name}.{interface_name}.created will be set when + ready. + + :param bridge: Bridge the new interface should be created on + :type bridge: str + :param ifname: Interface name we want the new netdev to get + :type ifname: str + :param ethaddr: Ethernet address we want to attach to the netdev + :type ethaddr: str + :param ifid: Unique identifier for port from CMS + :type ifid: str + :param iftype: Interface type, defaults to 'internal' + :type iftype: Optional[str] + :param ifstatus: Interface status, defaults to 'active' + :type ifstatus: Optional[str] + """ + # The keys in the ifdata dictionary map directly to column names in the + # OpenvSwitch Interface table as defined in DB-SCHEMA [0] referenced in + # RFC 7047 [1] + # + # There are some established conventions for keys in the external-ids + # column of various tables, consult the OVS Integration Guide [2] for + # more details. + # + # NOTE(fnordahl): Technically the ``external-ids`` column is called + # ``external_ids`` (with an underscore) and we rely on ``ovs-vsctl``'s + # behaviour of transforming dashes to underscores for us [3] so we can + # have a more pleasant data structure. + # + # 0: http://www.openvswitch.org/ovs-vswitchd.conf.db.5.pdf + # 1: https://tools.ietf.org/html/rfc7047 + # 2: http://docs.openvswitch.org/en/latest/topics/integration/ + # 3: https://github.com/openvswitch/ovs/blob/ + # 20dac08fdcce4b7fda1d07add3b346aa9751cfbc/ + # lib/db-ctl-base.c#L189-L215 + ifdata = { + 'type': iftype or 'internal', + 'external-ids': { + 'iface-id': ifid, + 'iface-status': ifstatus or 'active', + 'attached-mac': ethaddr, + }, + } + self._add_interface_request(bridge, ifname, ifdata) reactive.clear_flag( - self.expand_name('{endpoint_name}.interfaces.new_requests')) + self.expand_name('{endpoint_name}.interfaces.created')) @when('endpoint.{endpoint_name}.joined') def joined(self): - self.publish_chassis_name() - self.publish_ovn_configured() reactive.set_flag(self.expand_name('{endpoint_name}.connected')) reactive.set_flag(self.expand_name('{endpoint_name}.available')) @@ -106,10 +136,14 @@ class OVSDBSubordinateRequires(Endpoint): reactive.clear_flag(self.expand_name('{endpoint_name}.available')) reactive.clear_flag(self.expand_name('{endpoint_name}.connected')) - @when('endpoint.{endpoint_name}.changed.create-interfaces') + @when('endpoint.{endpoint_name}.changed.interfaces-created') def new_requests(self): - reactive.set_flag( - self.expand_name('{endpoint_name}.interfaces.new_requests')) - reactive.clear_flag( - self.expand_name( - 'endpoint.{endpoint_name}.changed.create-interfaces')) + ifreq = self._interface_requests() + + if ifreq is not None and self.all_joined_units.received[ + 'interfaces-created'] == hash_hexdigest(ifreq): + reactive.set_flag( + self.expand_name('{endpoint_name}.interfaces.created')) + reactive.clear_flag( + self.expand_name( + 'endpoint.{endpoint_name}.changed.interfaces-created')) diff --git a/unit_tests/test_ovsdb_subordinate_provides.py b/unit_tests/test_ovsdb_subordinate_provides.py index c0d7f12..8f94409 100644 --- a/unit_tests/test_ovsdb_subordinate_provides.py +++ b/unit_tests/test_ovsdb_subordinate_provides.py @@ -54,64 +54,73 @@ class TestOVSDBSubordinateProvides(test_utils.PatchHelper): self._relations.__iter__.return_value = [relation] return relation.to_publish - def test_chassis_name(self): - self.patch_target('_all_joined_units') - self._all_joined_units.received.get.return_value = 'fakename' - self.assertEquals(self.target.chassis_name, 'fakename') - self._all_joined_units.received.get.assert_called_once_with( - 'chassis-name', '') + def test__get_ovs_value(self): + self.patch_object(provides.subprocess, 'run') + cp = mock.MagicMock() + cp.stdout = '"hostname-42"\n' + self.run.return_value = cp + self.assertEquals( + self.target._get_ovs_value('tbl', 'col'), + 'hostname-42') + self.run.assert_called_once_with( + ('ovs-vsctl', 'get', 'tbl', '.', 'col'), + stdout=mock.ANY, check=True, universal_newlines=True) + self.run.reset_mock() + self.target._get_ovs_value('tbl', 'col', rec='rec') + self.run.assert_called_once_with( + ('ovs-vsctl', 'get', 'tbl', 'rec', 'col'), + stdout=mock.ANY, check=True, universal_newlines=True) - def test_ovn_configured(self): - self.patch_target('_all_joined_units') - self._all_joined_units.received.get.return_value = True - self.assertEquals(self.target.ovn_configured, True) - self._all_joined_units.received.get.assert_called_once_with( - 'ovn-configured', False) - - def test__add_interface_request(self): + def test_publish_chassis_name(self): + self.patch_target('_get_ovs_value') to_publish = self.patch_topublish() - to_publish.get.return_value = {} - self.target._add_interface_request('br-ex', 'eth0', {'data': ''}) + self._get_ovs_value.return_value = 'aHostname' + self.target.publish_chassis_name() to_publish.__setitem__.assert_called_once_with( - 'create-interfaces', {'br-ex': {'eth0': {'data': ''}}}) + 'chassis-name', 'aHostname') - def test__interfrace_requests(self): - self.patch_target('_relations') - relation = mock.MagicMock() - self._relations.__iter__.return_value = [relation] - relation.to_publish_raw.get.return_value = 'aValue' - self.assertEquals(self.target._interface_requests(), 'aValue') - relation.to_publish_raw.get.assert_called_once_with( - 'create-interfaces') + def test_publish_ovn_configured(self): + self.patch_object(provides, 'subprocess') + self.subprocess.CalledProcessError = Exception + self.patch_target('_get_ovs_value') + to_publish = self.patch_topublish() + self._get_ovs_value.side_effect = Exception + self.target.publish_ovn_configured() + to_publish.__setitem__.assert_called_once_with('ovn-configured', False) + self._get_ovs_value.assert_called_once_with( + 'Open_vSwitch', 'external_ids:ovn-remote') + to_publish.__setitem__.reset_mock() + self._get_ovs_value.side_effect = None + self.target.publish_ovn_configured() + to_publish.__setitem__.assert_called_once_with('ovn-configured', True) - def test_create_interface(self): - self.patch_target('_add_interface_request') + def test_interface_requests(self): + self.patch_target('_all_joined_units') + self._all_joined_units.received.get.return_value = 'fakereq' + self.assertEquals( + self.target.interface_requests, 'fakereq') + + def test_interface_requests_handled(self): + self.patch_object(provides, 'hash_hexdigest') + self.hash_hexdigest.return_value = 'fakehash' + self.patch_target('_all_joined_units') + self._all_joined_units.received_raw.__getitem__.return_value = 'ifreq' + to_publish = self.patch_topublish() self.patch_object(provides.reactive, 'clear_flag') - ifdata = { - 'type': 'internal', - 'external-ids': { - 'iface-id': 'fakeuuid', - 'iface-status': 'active', - 'attached-mac': 'fakemac', - }, - } - self.target.create_interface('br-ex', 'eth0', 'fakemac', 'fakeuuid') - self._add_interface_request.assert_called_once_with( - 'br-ex', 'eth0', ifdata) + self.target.interface_requests_handled() + self.hash_hexdigest.assert_called_once_with('ifreq') + to_publish.__setitem__.assert_called_once_with( + 'interfaces-created', 'fakehash') self.clear_flag.assert_called_once_with( - 'some-relation.interfaces.created') - self._add_interface_request.reset_mock() - ifdata['type'] = 'someothervalue' - ifdata['external-ids']['iface-status'] = 'inactive' - self.target.create_interface('br-ex', 'eth0', 'fakemac', 'fakeuuid', - iftype='someothervalue', - ifstatus='inactive') - self._add_interface_request.assert_called_once_with( - 'br-ex', 'eth0', ifdata) + 'some-relation.interfaces.new_requests') def test_joined(self): + self.patch_target('publish_chassis_name') + self.patch_target('publish_ovn_configured') self.patch_object(provides.reactive, 'set_flag') self.target.joined() + self.publish_chassis_name.assert_called_once_with() + self.publish_ovn_configured.assert_called_once_with() self.set_flag.assert_has_calls([ mock.call('some-relation.connected'), mock.call('some-relation.available'), @@ -126,20 +135,10 @@ class TestOVSDBSubordinateProvides(test_utils.PatchHelper): ]) def test_new_requests(self): - self.patch_target('_interface_requests') - self.patch_target('_all_joined_units') - self.patch_object(provides, 'hash_hexdigest') - self.hash_hexdigest.return_value = 'fakehash' - self._interface_requests.return_value = 'fakerequests' self.patch_object(provides.reactive, 'set_flag') self.patch_object(provides.reactive, 'clear_flag') self.target.new_requests() - self.hash_hexdigest.assert_called_once_with('fakerequests') - self.assertFalse(self.set_flag.called) - self.assertFalse(self.clear_flag.called) - self._all_joined_units.received.__getitem__.return_value = 'fakehash' - self.target.new_requests() self.set_flag.assert_called_once_with( - 'some-relation.interfaces.created') + 'some-relation.interfaces.new_requests') self.clear_flag.assert_called_once_with( - 'endpoint.some-relation.changed.interfaces-created') + 'endpoint.some-relation.changed.create-interfaces') diff --git a/unit_tests/test_ovsdb_subordinate_requires.py b/unit_tests/test_ovsdb_subordinate_requires.py index 0d56c56..a2cccab 100644 --- a/unit_tests/test_ovsdb_subordinate_requires.py +++ b/unit_tests/test_ovsdb_subordinate_requires.py @@ -22,7 +22,7 @@ import charms_openstack.test_utils as test_utils _hook_args = {} -class TestOVSDBSubordinateProvides(test_utils.PatchHelper): +class TestOVSDBSubordinateRequires(test_utils.PatchHelper): def setUp(self): super().setUp() @@ -54,73 +54,64 @@ class TestOVSDBSubordinateProvides(test_utils.PatchHelper): self._relations.__iter__.return_value = [relation] return relation.to_publish - def test__get_ovs_value(self): - self.patch_object(requires.subprocess, 'run') - cp = mock.MagicMock() - cp.stdout = '"hostname-42"\n' - self.run.return_value = cp - self.assertEquals( - self.target._get_ovs_value('tbl', 'col'), - 'hostname-42') - self.run.assert_called_once_with( - ('ovs-vsctl', 'get', 'tbl', '.', 'col'), - stdout=mock.ANY, check=True, universal_newlines=True) - self.run.reset_mock() - self.target._get_ovs_value('tbl', 'col', rec='rec') - self.run.assert_called_once_with( - ('ovs-vsctl', 'get', 'tbl', 'rec', 'col'), - stdout=mock.ANY, check=True, universal_newlines=True) + def test_chassis_name(self): + self.patch_target('_all_joined_units') + self._all_joined_units.received.get.return_value = 'fakename' + self.assertEquals(self.target.chassis_name, 'fakename') + self._all_joined_units.received.get.assert_called_once_with( + 'chassis-name', '') - def test_publish_chassis_name(self): - self.patch_target('_get_ovs_value') + def test_ovn_configured(self): + self.patch_target('_all_joined_units') + self._all_joined_units.received.get.return_value = True + self.assertEquals(self.target.ovn_configured, True) + self._all_joined_units.received.get.assert_called_once_with( + 'ovn-configured', False) + + def test__add_interface_request(self): to_publish = self.patch_topublish() - self._get_ovs_value.return_value = 'aHostname' - self.target.publish_chassis_name() + to_publish.get.return_value = {} + self.target._add_interface_request('br-ex', 'eth0', {'data': ''}) to_publish.__setitem__.assert_called_once_with( - 'chassis-name', 'aHostname') + 'create-interfaces', {'br-ex': {'eth0': {'data': ''}}}) - def test_publish_ovn_configured(self): - self.patch_object(requires, 'subprocess') - self.subprocess.CalledProcessError = Exception - self.patch_target('_get_ovs_value') - to_publish = self.patch_topublish() - self._get_ovs_value.side_effect = Exception - self.target.publish_ovn_configured() - to_publish.__setitem__.assert_called_once_with('ovn-configured', False) - self._get_ovs_value.assert_called_once_with( - 'Open_vSwitch', 'external_ids:ovn-remote') - to_publish.__setitem__.reset_mock() - self._get_ovs_value.side_effect = None - self.target.publish_ovn_configured() - to_publish.__setitem__.assert_called_once_with('ovn-configured', True) + def test__interfrace_requests(self): + self.patch_target('_relations') + relation = mock.MagicMock() + self._relations.__iter__.return_value = [relation] + relation.to_publish_raw.get.return_value = 'aValue' + self.assertEquals(self.target._interface_requests(), 'aValue') + relation.to_publish_raw.get.assert_called_once_with( + 'create-interfaces') - def test_interface_requests(self): - self.patch_target('_all_joined_units') - self._all_joined_units.received.get.return_value = 'fakereq' - self.assertEquals( - self.target.interface_requests, 'fakereq') - - def test_interface_requests_handled(self): - self.patch_object(requires, 'hash_hexdigest') - self.hash_hexdigest.return_value = 'fakehash' - self.patch_target('_all_joined_units') - self._all_joined_units.received_raw.__getitem__.return_value = 'ifreq' - to_publish = self.patch_topublish() + def test_create_interface(self): + self.patch_target('_add_interface_request') self.patch_object(requires.reactive, 'clear_flag') - self.target.interface_requests_handled() - self.hash_hexdigest.assert_called_once_with('ifreq') - to_publish.__setitem__.assert_called_once_with( - 'interfaces-created', 'fakehash') + ifdata = { + 'type': 'internal', + 'external-ids': { + 'iface-id': 'fakeuuid', + 'iface-status': 'active', + 'attached-mac': 'fakemac', + }, + } + self.target.create_interface('br-ex', 'eth0', 'fakemac', 'fakeuuid') + self._add_interface_request.assert_called_once_with( + 'br-ex', 'eth0', ifdata) self.clear_flag.assert_called_once_with( - 'some-relation.interfaces.new_requests') + 'some-relation.interfaces.created') + self._add_interface_request.reset_mock() + ifdata['type'] = 'someothervalue' + ifdata['external-ids']['iface-status'] = 'inactive' + self.target.create_interface('br-ex', 'eth0', 'fakemac', 'fakeuuid', + iftype='someothervalue', + ifstatus='inactive') + self._add_interface_request.assert_called_once_with( + 'br-ex', 'eth0', ifdata) def test_joined(self): - self.patch_target('publish_chassis_name') - self.patch_target('publish_ovn_configured') self.patch_object(requires.reactive, 'set_flag') self.target.joined() - self.publish_chassis_name.assert_called_once_with() - self.publish_ovn_configured.assert_called_once_with() self.set_flag.assert_has_calls([ mock.call('some-relation.connected'), mock.call('some-relation.available'), @@ -135,10 +126,20 @@ class TestOVSDBSubordinateProvides(test_utils.PatchHelper): ]) def test_new_requests(self): + self.patch_target('_interface_requests') + self.patch_target('_all_joined_units') + self.patch_object(requires, 'hash_hexdigest') + self.hash_hexdigest.return_value = 'fakehash' + self._interface_requests.return_value = 'fakerequests' self.patch_object(requires.reactive, 'set_flag') self.patch_object(requires.reactive, 'clear_flag') self.target.new_requests() + self.hash_hexdigest.assert_called_once_with('fakerequests') + self.assertFalse(self.set_flag.called) + self.assertFalse(self.clear_flag.called) + self._all_joined_units.received.__getitem__.return_value = 'fakehash' + self.target.new_requests() self.set_flag.assert_called_once_with( - 'some-relation.interfaces.new_requests') + 'some-relation.interfaces.created') self.clear_flag.assert_called_once_with( - 'endpoint.some-relation.changed.create-interfaces') + 'endpoint.some-relation.changed.interfaces-created')