ovsdb_subordinate: Flip requires and provides

Across the OpenStack Engineering team charms we are using requires
and provides for subordinates in the opposite direction of what
is documented in Juju.

Given the end user experience is unaffected I'll bury this detail
for the sake of consistency across the charms.

Change-Id: Ia304cd25dbdd130338837f149a79efe59681f794
This commit is contained in:
Frode Nordahl 2020-02-05 17:37:31 +01:00
parent 41235cd678
commit 78d6c208b4
No known key found for this signature in database
GPG Key ID: 6A5D59A3BA48373F
4 changed files with 282 additions and 282 deletions

View File

@ -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'))

View File

@ -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'))

View File

@ -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')

View File

@ -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')