def _verify_change(desired_state): current_state = state.State(netinfo.show()) verifiable_desired_state = copy.deepcopy(desired_state) verifiable_desired_state.verify_interfaces(current_state) verifiable_desired_state.verify_routes(current_state) verifiable_desired_state.verify_dns(current_state) verifiable_desired_state.verify_route_rule(current_state)
def apply(desired_state, verify_change=True, commit=True, rollback_timeout=60): """ Apply the desired state :param verify_change: Check if the outcome state matches the desired state and rollback if not. :param commit: Commit the changes after verification if the state matches. :param rollback_timeout: Revert the changes if they are not commited within this timeout (specified in seconds). :type verify_change: bool :type commit: bool :type rollback_timeout: int (seconds) :returns: Checkpoint identifier :rtype: str """ desired_state = copy.deepcopy(desired_state) validator.validate(desired_state) validator.validate_capabilities(desired_state, netinfo.capabilities()) validator.validate_dhcp(desired_state) validator.validate_dns(desired_state) checkpoint = _apply_ifaces_state(state.State(desired_state), verify_change, commit, rollback_timeout) if checkpoint: return str(checkpoint.dbuspath)
def test_bonds_with_multiple_slaves(self): desired_state = state.State({ schema.Interface.KEY: [ { Interface.NAME: "slave0" }, { Interface.NAME: "slave1" }, { Interface.NAME: "slave00" }, { Interface.NAME: "slave11" }, { Interface.NAME: "bond0", Bond.CONFIG_SUBTREE: { Bond.SLAVES: ["slave0", "slave00"] }, }, { Interface.NAME: "bond1", Bond.CONFIG_SUBTREE: { Bond.SLAVES: ["slave1", "slave11"] }, }, ] }) libnmstate.validator.validate_link_aggregation_state( desired_state, empty_state())
def _apply_ifaces_state( desired_state, verify_change, commit, rollback_timeout ): current_state = state.State(netinfo.show()) desired_state.sanitize_ethernet(current_state) desired_state.sanitize_dynamic_ip() desired_state.merge_routes(current_state) desired_state.merge_dns(current_state) metadata.generate_ifaces_metadata(desired_state, current_state) validator.validate_interfaces_state(desired_state, current_state) validator.validate_routes(desired_state, current_state) new_interfaces = _list_new_interfaces(desired_state, current_state) try: with nm.checkpoint.CheckPoint( autodestroy=commit, timeout=rollback_timeout ) as checkpoint: with _setup_providers(): _add_interfaces(new_interfaces, desired_state) state2edit = _create_editable_desired_state( desired_state, current_state, new_interfaces ) _edit_interfaces(state2edit) if verify_change: _verify_change(desired_state) if not commit: return checkpoint except nm.checkpoint.NMCheckPointPermissionError: raise NmstatePermissionError('Error creating a check point') except nm.checkpoint.NMCheckPointCreationError: raise NmstateConflictError('Error creating a check point')
def test_ovs_adding_slaves(self): desired_state = state.State({ Interface.KEY: [ { "name": OVS_NAME, "type": TYPE_OVS_BR, "state": "up", "bridge": { "port": [{ "name": "eth0" }, { "name": "eth1" }] }, }, { "name": "eth1", "state": "up", "type": "unknown" }, ] }) current_state = state.State({ Interface.KEY: [{ "name": "eth0", "state": "up", "type": "unknown" }] }) expected_dstate = state.State(desired_state.state) expected_cstate = state.State(current_state.state) expected_dstate.interfaces["eth0"] = {"name": "eth0", "state": "up"} expected_dstate.interfaces["eth0"][metadata.MASTER] = OVS_NAME expected_dstate.interfaces["eth1"][metadata.MASTER] = OVS_NAME expected_dstate.interfaces["eth0"][metadata.MASTER_TYPE] = TYPE_OVS_BR expected_dstate.interfaces["eth1"][metadata.MASTER_TYPE] = TYPE_OVS_BR expected_dstate.interfaces["eth0"][ metadata.BRPORT_OPTIONS] = desired_state.interfaces[OVS_NAME][ "bridge"]["port"][0] expected_dstate.interfaces["eth1"][ metadata.BRPORT_OPTIONS] = desired_state.interfaces[OVS_NAME][ "bridge"]["port"][1] metadata.generate_ifaces_metadata(desired_state, current_state) assert desired_state == expected_dstate assert current_state == expected_cstate
def test_merge_unique_states(self): route0_obj = self._create_route0() route0 = route0_obj.to_dict() route1_obj = self._create_route1() route1 = route1_obj.to_dict() s0 = state.State({Route.KEY: {Route.CONFIG: [route0]}}) s1 = state.State({Route.KEY: {Route.CONFIG: [route1]}}) s0.merge_routes(s1) expected_state = { Interface.KEY: [], Route.KEY: {Route.CONFIG: [route0]}, } assert expected_state == s0.state expected_indexed_routes = {"eth1": [route0_obj]} assert expected_indexed_routes == s0.config_iface_routes
def test_state_iface_routes_order(): # Changing all routes to eth1 routes = _get_mixed_test_routes() for route in routes: route[Route.NEXT_HOP_INTERFACE] = "eth1" route_state = state.State( {Route.KEY: { Route.CONFIG: [routes[0], routes[1]] }}) reverse_route_state = state.State( {Route.KEY: { Route.CONFIG: [routes[1], routes[0]] }}) assert (route_state.config_iface_routes == reverse_route_state.config_iface_routes)
def test_valid_route_based_on_current_state(self): iface0 = _create_interface_state("eth1", ipv4=True) route0 = self._create_route0() desired_state = state.State( { schema.Interface.KEY: [], schema.Route.KEY: {schema.Route.CONFIG: [route0]}, } ) current_state = state.State( { schema.Interface.KEY: [iface0], schema.Route.KEY: {schema.Route.CONFIG: []}, } ) validator.validate_routes(desired_state, current_state)
def test_mac_restriction_in_current_mac_in_desire(self): iface_state = self._gen_iface_state("bond0") bond_config = iface_state[Bond.CONFIG_SUBTREE] bond_config[Bond.MODE] = BondMode.ACTIVE_BACKUP bond_config[Bond.OPTIONS_SUBTREE]["fail_over_mac"] = "active" current_state = state.State({Interface.KEY: [iface_state]}) iface_state = self._gen_iface_state("bond0") iface_state[Interface.MAC] = MAC0 bond_config = iface_state[Bond.CONFIG_SUBTREE] bond_config.pop(Bond.MODE) original_desired_state = state.State({Interface.KEY: [iface_state]}) with pytest.raises(NmstateValueError): libnmstate.validator.validate_interfaces_state( original_desired_state, current_state )
def test_ovs_editing_option(self, bonded): if bonded: ports = [_create_lag_port_state((PORT0, PORT1))] else: ports = [ { OVSBridge.Port.NAME: PORT0 }, { OVSBridge.Port.NAME: PORT1 }, ] ports_state = {OVSBridge.PORT_SUBTREE: ports} desired_state = state.State({ Interface.KEY: [{ Interface.NAME: OVS_NAME, Interface.TYPE: TYPE_OVS_BR, Interface.STATE: InterfaceState.DOWN, }] }) current_state = state.State({ Interface.KEY: [ { Interface.NAME: OVS_NAME, Interface.TYPE: TYPE_OVS_BR, Interface.STATE: InterfaceState.UP, OVSBridge.CONFIG_SUBTREE: ports_state, }, { Interface.NAME: PORT0, Interface.TYPE: InterfaceType.UNKNOWN, }, { Interface.NAME: PORT1, Interface.TYPE: InterfaceType.UNKNOWN, }, ] }) expected_desired_state = state.State(desired_state.state) expected_current_state = state.State(current_state.state) metadata.generate_ifaces_metadata(desired_state, current_state) assert desired_state == expected_desired_state assert current_state == expected_current_state
def test_no_routes_with_no_interfaces(self): desired_state = state.State({ Interface.KEY: [], Route.KEY: { Route.CONFIG: [] } }) current_state = state.State({ Interface.KEY: [], Route.KEY: { Route.CONFIG: [] } }) metadata.generate_ifaces_metadata(desired_state, current_state) assert {} == desired_state.interfaces
def test_mode_not_defined(self): iface_state = self._gen_iface_state("bond0") iface_state[Bond.CONFIG_SUBTREE].pop(Bond.MODE) original_desired_state = state.State({Interface.KEY: [iface_state]}) current_state = empty_state() with pytest.raises(NmstateValueError): libnmstate.validator.validate_interfaces_state( original_desired_state, current_state)
def _extra_state(self): return state.State( { Interface.KEY: [ {'name': 'eth0', 'state': 'up', 'type': 'unknown'}, {'name': 'eth1', 'state': 'up', 'type': 'unknown'}, ] } )
def test_ovs_editing_option(self): desired_state = state.State({ Interface.KEY: [{ 'name': OVS_NAME, 'type': TYPE_OVS_BR, 'state': 'down' }] }) current_state = state.State({ Interface.KEY: [ { 'name': OVS_NAME, 'type': TYPE_OVS_BR, 'state': 'up', 'bridge': { 'port': [ { 'name': 'eth0', 'type': OBPortType.SYSTEM }, { 'name': 'eth1', 'type': OBPortType.SYSTEM }, ] }, }, { 'name': 'eth0', 'type': 'unknown' }, { 'name': 'eth1', 'type': 'unknown' }, ] }) expected_desired_state = state.State(desired_state.state) expected_current_state = state.State(current_state.state) metadata.generate_ifaces_metadata(desired_state, current_state) assert desired_state == expected_desired_state assert current_state == expected_current_state
def test_valid_route_based_on_desired_state_but_not_current(self): iface0 = _create_interface_state('eth1', ipv4=True) route0 = self._create_route0() desired_state = state.State({ schema.Interface.KEY: [iface0], schema.Route.KEY: { schema.Route.CONFIG: [route0] }, }) iface0_down = _create_interface_state('eth1', state=schema.InterfaceState.DOWN) current_state = state.State({ schema.Interface.KEY: [iface0_down], schema.Route.KEY: { schema.Route.CONFIG: [] }, }) validator.validate_routes(desired_state, current_state)
def test_swap_slaves_between_bonds(self): BOND2_NAME = "bond88" desired_state = state.State({ Interface.KEY: [ create_bond_state_dict(BOND_NAME, ["eth1"]), create_bond_state_dict(BOND2_NAME, ["eth0"]), ] }) current_state = state.State({ Interface.KEY: [ create_bond_state_dict(BOND_NAME, ["eth0"]), create_bond_state_dict(BOND2_NAME, ["eth1"]), { Interface.NAME: "eth0", Interface.STATE: InterfaceState.UP, Interface.TYPE: InterfaceType.UNKNOWN, }, { Interface.NAME: "eth1", Interface.STATE: InterfaceState.UP, Interface.TYPE: InterfaceType.UNKNOWN, }, ] }) expected_dstate = state.State(desired_state.state) expected_cstate = state.State(current_state.state) expected_dstate.interfaces["eth0"] = { Interface.NAME: "eth0", Interface.STATE: InterfaceState.UP, } expected_dstate.interfaces["eth1"] = { Interface.NAME: "eth1", Interface.STATE: InterfaceState.UP, } expected_dstate.interfaces["eth0"][metadata.MASTER] = BOND2_NAME expected_dstate.interfaces["eth0"][metadata.MASTER_TYPE] = TYPE_BOND expected_dstate.interfaces["eth1"][metadata.MASTER] = BOND_NAME expected_dstate.interfaces["eth1"][metadata.MASTER_TYPE] = TYPE_BOND metadata.generate_ifaces_metadata(desired_state, current_state) assert desired_state == expected_dstate assert current_state == expected_cstate
def test_invalid_route_based_on_desired_state_but_not_current(self): iface0_ipv4_disabled = _create_interface_state("eth1", ipv4=False) route0 = self._create_route0() desired_state = state.State( { schema.Interface.KEY: [iface0_ipv4_disabled], schema.Route.KEY: {schema.Route.CONFIG: [route0]}, } ) iface0_ipv4_enabled = _create_interface_state("eth1", ipv4=True) current_state = state.State( { schema.Interface.KEY: [iface0_ipv4_enabled], schema.Route.KEY: {schema.Route.CONFIG: []}, } ) with pytest.raises(validator.NmstateRouteWithNoIPInterfaceError): validator.validate_routes(desired_state, current_state)
def test_route_with_no_desired_or_current_matching_interface(self): route0 = self._create_route0() desired_state = state.State({ Interface.KEY: [_create_interface_state('foo')], Route.KEY: { Route.CONFIG: [route0.to_dict()] }, }) current_state = state.State({ Interface.KEY: [_create_interface_state('boo')], Route.KEY: { Route.CONFIG: [] }, }) metadata.generate_ifaces_metadata(desired_state, current_state) assert 'foo' in desired_state.interfaces assert metadata.ROUTES not in desired_state.interfaces['foo']
def test_state_iface_routes_with_same_iface(): routes = _get_mixed_test_routes() for route in routes: route[Route.NEXT_HOP_INTERFACE] = "eth1" route_state = state.State({Route.KEY: {Route.CONFIG: routes}}) expected_indexed_route_state = { "eth1": sorted([state.RouteEntry(r) for r in routes]) } assert expected_indexed_route_state == route_state.config_iface_routes
def test_remove_unknown_interfaces(): desired_state = state.State({ Interface.KEY: [{ Interface.NAME: "foo", Interface.TYPE: InterfaceType.UNKNOWN }] }) desired_state.remove_unknown_interfaces() assert not desired_state.interfaces
def test_route_with_matching_current_interface(self): route0 = self._create_route0() desired_state = state.State({ Interface.KEY: [], Route.KEY: { Route.CONFIG: [route0.to_dict()] } }) current_state = state.State({ Interface.KEY: [_create_interface_state('eth1')], Route.KEY: { Route.CONFIG: [] }, }) metadata.generate_ifaces_metadata(desired_state, current_state) iface_state = desired_state.interfaces['eth1'] route_metadata, = iface_state[Interface.IPV4][metadata.ROUTES] assert route0.to_dict() == route_metadata
def test_mac_restriction_without_mac_in_desire(self): iface_state = self._gen_iface_state("bond0") bond_config = iface_state[Bond.CONFIG_SUBTREE] bond_config[Bond.MODE] = BondMode.ACTIVE_BACKUP bond_config[Bond.OPTIONS_SUBTREE]["fail_over_mac"] = "active" original_desired_state = state.State({Interface.KEY: [iface_state]}) current_state = empty_state() libnmstate.validator.validate_interfaces_state(original_desired_state, current_state)
def _create_desired_state(): return state.State( { Interface.KEY: [ { Interface.NAME: IFACE0, Interface.STATE: InterfaceState.ABSENT, } ] } )
def test_state_iface_routes_with_distinct_ifaces(): routes = _get_mixed_test_routes() route_state = state.State({Route.KEY: {Route.CONFIG: routes}}) expected_indexed_route_state = defaultdict(list) for route in routes: iface_name = route[Route.NEXT_HOP_INTERFACE] expected_indexed_route_state[iface_name].append( state.RouteEntry(route)) # No need to sort the routes as there is only 1 route per interface. assert expected_indexed_route_state == route_state.config_iface_routes
def test_ovs_editing_option(self): desired_state = state.State({ Interface.KEY: [{ "name": OVS_NAME, "type": TYPE_OVS_BR, "state": "down" }] }) current_state = state.State({ Interface.KEY: [ { "name": OVS_NAME, "type": TYPE_OVS_BR, "state": "up", "bridge": { "port": [{ "name": "eth0" }, { "name": "eth1" }] }, }, { "name": "eth0", "type": "unknown" }, { "name": "eth1", "type": "unknown" }, ] }) expected_desired_state = state.State(desired_state.state) expected_current_state = state.State(current_state.state) metadata.generate_ifaces_metadata(desired_state, current_state) assert desired_state == expected_desired_state assert current_state == expected_current_state
def test_rule_with_matching_route_table(self): rule0 = self._create_rule0() rule1 = self._create_rule1() route0 = _create_route( "198.51.100.0/24", "192.0.2.1", "eth1", TestRouteRuleMetadata.TEST_ROUTE_TABLE, 103, ) route1 = _create_route( "2001:db8:f::/64", "2001:db8:e::", "eth1", TestRouteRuleMetadata.TEST_ROUTE_TABLE, 103, ) desired_state = state.State({ Interface.KEY: [_create_interface_state("eth1")], Route.KEY: { Route.CONFIG: [route0.to_dict(), route1.to_dict()] }, RouteRule.KEY: { RouteRule.CONFIG: [rule0.to_dict(), rule1.to_dict()] }, }) current_state = state.State({}) metadata.generate_ifaces_metadata(desired_state, current_state) iface_state = desired_state.interfaces["eth1"] (rule0_metadata, ) = iface_state[Interface.IPV4][metadata.ROUTE_RULES_METADATA] (rule1_metadata, ) = iface_state[Interface.IPV6][metadata.ROUTE_RULES_METADATA] assert rule0.to_dict() == rule0_metadata assert rule1.to_dict() == rule1_metadata
def _base_state(self): return state.State({ Interface.KEY: [{ 'name': 'foo-name', 'type': 'foo-type', 'state': 'up', 'bridge': { 'port': [{ 'name': 'eth0', 'type': 'system' }] }, }] })
def _base_state(self): return state.State({ Interface.KEY: [{ "name": "foo-name", "type": "foo-type", "state": "up", "bridge": { "port": [{ "name": "eth0", "type": "system" }] }, }] })
def test_swap_slaves_between_bonds(self): BOND2_NAME = 'bond88' desired_state = state.State({ Interface.KEY: [ create_bond_state_dict(BOND_NAME, ['eth1']), create_bond_state_dict(BOND2_NAME, ['eth0']), ] }) current_state = state.State({ Interface.KEY: [ create_bond_state_dict(BOND_NAME, ['eth0']), create_bond_state_dict(BOND2_NAME, ['eth1']), { 'name': 'eth0', 'state': 'up', 'type': 'unknown' }, { 'name': 'eth1', 'state': 'up', 'type': 'unknown' }, ] }) expected_dstate = state.State(desired_state.state) expected_cstate = state.State(current_state.state) expected_dstate.interfaces['eth0'] = {'name': 'eth0', 'state': 'up'} expected_dstate.interfaces['eth1'] = {'name': 'eth1', 'state': 'up'} expected_dstate.interfaces['eth0'][metadata.MASTER] = BOND2_NAME expected_dstate.interfaces['eth0'][metadata.MASTER_TYPE] = TYPE_BOND expected_dstate.interfaces['eth1'][metadata.MASTER] = BOND_NAME expected_dstate.interfaces['eth1'][metadata.MASTER_TYPE] = TYPE_BOND metadata.generate_ifaces_metadata(desired_state, current_state) assert desired_state == expected_dstate assert current_state == expected_cstate
def test_verify_route_rules(self): self_state = state.State( { RouteRule.KEY: { RouteRule.CONFIG: [ _create_route_rule_dict( "198.51.100.1/24", "192.0.2.1", 50, 103 ), _create_route_rule_dict( "198.51.100.0/24", "192.0.2.1", 50, 103 ), _create_route_rule_dict( "198.51.100.0/24", "192.0.2.1", 10, 104 ), ] } } ) other_state = state.State( { RouteRule.KEY: { RouteRule.CONFIG: [ _create_route_rule_dict( "198.51.100.0/24", "192.0.2.1", 10, 104 ), _create_route_rule_dict( "198.51.100.1/24", "192.0.2.1", 50, 103 ), _create_route_rule_dict( "198.51.100.0/24", "192.0.2.1", 50, 103 ), ] } } ) self_state.verify_route_rule(other_state)