def create_pending_actions_db(): """Create database objects including a pending NetworkingAction. The first version of this function was used to create the dump 'pending-networking-actions.sql'. """ # At a minimum we need a project, node, nic, switch, port, and network: api.project_create('runway') api.node_register( 'node-1', obm={ 'type': MOCK_OBM_TYPE, 'user': '******', 'host': 'host', 'password': '******', }, ) api.node_register_nic('node-1', 'pxe', 'de:ad:be:ef:20:16') api.switch_register( 'sw0', type=MOCK_SWITCH_TYPE, username='******', hostname='host', password='******', ) api.switch_register_port('sw0', 'gi1/0/4') api.port_connect_nic('sw0', 'gi1/0/4', 'node-1', 'pxe') api.project_connect_node('runway', 'node-1') api.network_create('runway_pxe', 'runway', 'runway', '') # Queue up a networking action. Importantly, we do *not* call # deferred.apply_networking, as that would flush the action and # remove it from the database. api.node_connect_network('node-1', 'pxe', 'runway_pxe')
def test_duplicate_user_add_project(self): """Test that adding a user that already exists fails.""" self.dbauth.user_create('charlie', 'secret') api.project_create('acme-corp') self.dbauth.user_add_project('charlie', 'acme-corp') with pytest.raises(errors.DuplicateError): self.dbauth.user_add_project('charlie', 'acme-corp')
def mock_node(obmd_cfg): """Register a node wth obmd & hil. returns the node's label. The node will be attached to a project called 'anvil-nextgen'. """ obmd_uri = 'http://localhost' + obmd_cfg['ListenAddr'] + '/node/node-99' # register a node with obmd: requests.put( obmd_uri, auth=('admin', obmd_cfg['AdminToken']), data=json.dumps({ "type": "mock", "info": { "addr": "10.0.0.4", "NumWrites": 0, }, })) # and then with hil: api.node_register( node='node-99', obmd={ 'uri': obmd_uri, 'admin_token': obmd_cfg['AdminToken'], }, ) # Create a project, and attach the node. api.project_create('anvil-nextgen') api.project_connect_node('anvil-nextgen', 'node-99') return 'node-99'
def test_ensure_legal_operations(): """Test to ensure that ensure_legal_operations works as expected""" # create a project and a network api.project_create('anvil-nextgen') network_create_simple('hammernet', 'anvil-nextgen') network_create_simple('pineapple', 'anvil-nextgen') # register a switch of type dellnos9 and add a port to it api.switch_register('s3048', type=SWITCH_TYPE, username="******", password="******", hostname="switchname", interface_type="GigabitEthernet") api.switch_register_port('s3048', '1/3') switch = api.get_or_404(model.Switch, 's3048') # register a ndoe and a nic api.node_register( node='compute-01', obmd={ 'uri': 'http://obmd.example.com', 'admin_token': 'secret', }, ) api.project_connect_node('anvil-nextgen', 'compute-01') api.node_register_nic('compute-01', 'eth0', 'DE:AD:BE:EF:20:14') nic = api.get_or_404(model.Nic, 'eth0') api.port_connect_nic('s3048', '1/3', 'compute-01', 'eth0') # connecting a trunked network wihtout having a native should fail. # call the method directly and test the API too. with pytest.raises(BlockedError): switch.ensure_legal_operation(nic, 'connect', 'vlan/1212') with pytest.raises(BlockedError): api.node_connect_network('compute-01', 'eth0', 'hammernet', 'vlan/40') # doing these operations in the correct order, that is native network first # and then trunked, should work. api.node_connect_network('compute-01', 'eth0', 'hammernet', 'vlan/native') mock_networking_action() api.node_connect_network('compute-01', 'eth0', 'pineapple', 'vlan/41') mock_networking_action() # removing these networks in the wrong order should not work. with pytest.raises(BlockedError): switch.ensure_legal_operation(nic, 'detach', 'vlan/native') with pytest.raises(BlockedError): api.node_detach_network('compute-01', 'eth0', 'hammernet') # removing networks in the right order should work api.node_detach_network('compute-01', 'eth0', 'pineapple') mock_networking_action() api.node_detach_network('compute-01', 'eth0', 'hammernet') mock_networking_action() db.session.close()
def test_many_http_queries(): """Put a few objects in the db, then bombard the api with queries. This is intended to shake out problems like the resource leak discussed in issue #454. """ # NOTE: Now that the session is managed by Flask-SQLAlchemy, failures here # are unlikely to be regressions of the issue that #454 fixed; we're no # longer managing the lifecycle of the session ourselves. It's not obvious # that this is more than clutter now, but let's not be too trigger happy # about deleting tests. with rest.app.test_request_context(): rest.init_auth() api.node_register('node-99', obm={ "type": "http://schema.massopencloud.org/haas/v0/obm/ipmi", "host": "ipmihost", "user": "******", "password": "******"}) api.node_register('node-98', obm={ "type": "http://schema.massopencloud.org/haas/v0/obm/ipmi", "host": "ipmihost", "user": "******", "password": "******"}) api.node_register('node-97', obm={ "type": "http://schema.massopencloud.org/haas/v0/obm/ipmi", "host": "ipmihost", "user": "******", "password": "******"}) api.node_register_nic('node-99', 'eth0', 'DE:AD:BE:EF:20:14') api.node_register_nic('node-98', 'eth0', 'DE:AD:BE:EF:20:15') api.node_register_nic('node-97', 'eth0', 'DE:AD:BE:EF:20:16') api.project_create('anvil-nextgen') api.project_create('anvil-legacy') api.project_connect_node('anvil-nextgen', 'node-99') api.project_connect_node('anvil-legacy', 'node-98') client = rest.app.test_client() def _show_nodes(path): """Helper for the loop below. This does a GET on path, which must return a json list of names of nodes. It will then query the state of each node. If any request does not return 200 or has a body which is not valid json, the test will fail. """ resp = client.get(path) assert resp.status_code == 200 for node in json.loads(resp.get_data()): resp = client.get('/nodes/%s' % node) assert resp.status_code == 200 # At least make sure the body parses: json.loads(resp.get_data()) for i in range(100): _show_nodes('/nodes/free') resp = client.get('/projects') assert resp.status_code == 200 for project in json.loads(resp.get_data()): _show_nodes('/project/%s/nodes' % project)
def test_many_http_queries(): """Put a few objects in the db, then bombard the api with queries. This is intended to shake out problems like the resource leak discussed in issue #454. """ # NOTE: Now that the session is managed by Flask-SQLAlchemy, failures here # are unlikely to be regressions of the issue that #454 fixed; we're no # longer managing the lifecycle of the session ourselves. It's not obvious # that this is more than clutter now, but let's not be too trigger happy # about deleting tests. with rest.app.test_request_context(): rest.init_auth() api.node_register('node-99', obm={ "type": "http://schema.massopencloud.org/haas/v0/obm/ipmi", "host": "ipmihost", "user": "******", "password": "******"}) api.node_register('node-98', obm={ "type": "http://schema.massopencloud.org/haas/v0/obm/ipmi", "host": "ipmihost", "user": "******", "password": "******"}) api.node_register('node-97', obm={ "type": "http://schema.massopencloud.org/haas/v0/obm/ipmi", "host": "ipmihost", "user": "******", "password": "******"}) api.node_register_nic('node-99', 'eth0', 'DE:AD:BE:EF:20:14') api.node_register_nic('node-98', 'eth0', 'DE:AD:BE:EF:20:15') api.node_register_nic('node-97', 'eth0', 'DE:AD:BE:EF:20:16') api.project_create('anvil-nextgen') api.project_create('anvil-legacy') api.project_connect_node('anvil-nextgen', 'node-99') api.project_connect_node('anvil-legacy', 'node-98') client = rest.app.test_client() def _show_nodes(path): """Helper for the loop below. This does a GET on path, which must return a json list of names of nodes. It will then query the state of each node. If any request does not return 200 or has a body which is not valid json, the test will fail. """ resp = client.get(path) assert resp.status_code == 200 for node in json.loads(resp.get_data()): resp = client.get('/nodes/%s' % node) assert resp.status_code == 200 # At least make sure the body parses: json.loads(resp.get_data()) for _i in range(100): _show_nodes('/nodes/free') resp = client.get('/projects') assert resp.status_code == 200 for project in json.loads(resp.get_data()): _show_nodes('/project/%s/nodes' % project)
def test_saving_config_file(self): """Test saving the switch config to flash.""" api.project_create('anvil-nextgen') nodes = self.collect_nodes() # Create two networks network_create_simple('net-0', 'anvil-nextgen') network_create_simple('net-1', 'anvil-nextgen') # save the old startup config before performing a networking action old_startup_config = self.get_config('startup') # Connect n0 and n1 to net-0 and net-1 respectively api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') api.node_connect_network(nodes[1].label, nodes[1].nics[0].label, 'net-1') deferred.apply_networking() # get the running config, and the new startup config running_config = self.get_config('running') new_startup_config = self.get_config('startup') assert new_startup_config == running_config assert new_startup_config != old_startup_config # cleanup api.node_detach_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') api.node_detach_network(nodes[1].label, nodes[1].nics[0].label, 'net-1') deferred.apply_networking()
def create_pending_actions_db(): """Create database objects including a pending NetworkingAction. The first version of this function was used to create the dump 'pending-networking-actions.sql'. """ # At a minimum we need a project, node, nic, switch, port, and network: api.project_create('runway') api.node_register( 'node-1', obm={ 'type': MOCK_OBM_TYPE, 'user': '******', 'host': 'host', 'password': '******', }, ) api.node_register_nic('node-1', 'pxe', 'de:ad:be:ef:20:16') api.switch_register('sw0', type=MOCK_SWITCH_TYPE, username='******', hostname='host', password='******', ) api.switch_register_port('sw0', 'gi1/0/4') api.port_connect_nic('sw0', 'gi1/0/4', 'node-1', 'pxe') api.project_connect_node('runway', 'node-1') api.network_create('runway_pxe', 'runway', 'runway', '') # Queue up a networking action. Importantly, we do *not* call # deferred.apply_networking, as that would flush the action and # remove it from the database. api.node_connect_network('node-1', 'pxe', 'runway_pxe')
def test_user_add_project(self): self.dbauth.user_create('charlie', 'secret') api.project_create('acme-corp') self.dbauth.user_add_project('charlie', 'acme-corp') user = api._must_find(self.dbauth.User, 'charlie') project = api._must_find(model.Project, 'acme-corp') assert project in user.projects assert user in project.users
def test_user_add_project(self): """Test that user_add_project correctly adds the user.""" self.dbauth.user_create('charlie', 'secret') api.project_create('acme-corp') self.dbauth.user_add_project('charlie', 'acme-corp') user = api.get_or_404(self.dbauth.User, 'charlie') project = api.get_or_404(model.Project, 'acme-corp') assert project in user.projects assert user in project.users
def test_user_remove_project(self): """Test that user_remove_project correctly removes the user.""" self.dbauth.user_create('charlie', 'secret') api.project_create('acme-corp') self.dbauth.user_add_project('charlie', 'acme-corp') self.dbauth.user_remove_project('charlie', 'acme-corp') user = api._must_find(self.dbauth.User, 'charlie') project = api._must_find(model.Project, 'acme-corp') assert project not in user.projects assert user not in project.users
def test_headnode(self): api.project_create('anvil-nextgen') network_create_simple('spider-web', 'anvil-nextgen') api.headnode_create('hn-0', 'anvil-nextgen', 'base-headnode') api.headnode_create_hnic('hn-0', 'hnic-0') api.headnode_connect_network('hn-0', 'hnic-0', 'spider-web') if have_dry_run(): pytest.xfail("Running in dry-run mode; can't talk to libvirt.") assert json.loads(api.show_headnode('hn-0'))['vncport'] is None api.headnode_start('hn-0') assert json.loads(api.show_headnode('hn-0'))['vncport'] is not None api.headnode_stop('hn-0') api.headnode_delete('hn-0')
def test_project_detach_node_maintenance(self, maintenance_proj_init): """Test that project_detach_node removes the node from the project. Note that the maintenance server has a fake url. We expect it to fail during the connection.""" api.project_create('anvil-nextgen') new_node('node-99') api.project_connect_node('anvil-nextgen', 'node-99') # Should raise error due to arbitrary POST url: with pytest.raises(LoggedWarningError): api.project_detach_node('anvil-nextgen', 'node-99') maintenance_proj = api.get_or_404(model.Project, 'maintenance') node = api.get_or_404(model.Node, 'node-99') assert node.project == maintenance_proj
def test_enable_disable_obm(obmd_cfg): """Test enabling and disabling the obm of a node via the api.""" obmd_uri = 'http://localhost' + obmd_cfg['ListenAddr'] + '/node/node-99' # register a node with obmd: requests.put( obmd_uri, auth=('admin', obmd_cfg['AdminToken']), data=json.dumps({ "type": "ipmi", "info": { "addr": "10.0.0.4", "user": "******", "pass": "******", }, })) # and then with hil: api.node_register( node='node-99', obm={ "type": 'http://schema.massopencloud.org/haas/v0/obm/mock', "host": "ipmihost", "user": "******", "password": "******", }, obmd={ 'uri': obmd_uri, 'admin_token': obmd_cfg['AdminToken'], }, ) # Then create a project, and attach the node. api.project_create('anvil-nextgen') api.project_connect_node('anvil-nextgen', 'node-99') # now the test proper: # First, enable the obm api.node_enable_disable_obm('node-99', enabled=True) # Obm is enabled; we shouldn't be able to detach the node: with pytest.raises(errors.BlockedError): api.project_detach_node('anvil-nextgen', 'node-99') # ...so disable it first: api.node_enable_disable_obm('node-99', enabled=False) # ...and then it should work: api.project_detach_node('anvil-nextgen', 'node-99')
def test_saving_config_file(self): api.project_create('anvil-nextgen') nodes = self.collect_nodes() # Create two networks network_create_simple('net-0', 'anvil-nextgen') network_create_simple('net-1', 'anvil-nextgen') # save the old startup config before performing a networking action old_startup_config = self.get_config('startup') # Connect n0 and n1 to net-0 and net-1 respectively api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') api.node_connect_network(nodes[1].label, nodes[1].nics[0].label, 'net-1') deferred.apply_networking() # get the running config, and the new startup config running_config = self.get_config('running') new_startup_config = self.get_config('startup') assert new_startup_config == running_config assert new_startup_config != old_startup_config # cleanup api.node_detach_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') api.node_detach_network(nodes[1].label, nodes[1].nics[0].label, 'net-1') deferred.apply_networking()
def test_headnode(self): """Test each of the headnode related operations * create * connect_hnic, * connect_network * start * stop * delete """ api.project_create('anvil-nextgen') network_create_simple('spider-web', 'anvil-nextgen') api.headnode_create('hn-0', 'anvil-nextgen', 'base-headnode') api.headnode_create_hnic('hn-0', 'hnic-0') api.headnode_connect_network('hn-0', 'hnic-0', 'spider-web') if have_dry_run(): pytest.xfail("Running in dry-run mode; can't talk to libvirt.") assert json.loads(api.show_headnode('hn-0'))['vncport'] is None api.headnode_start('hn-0') assert json.loads(api.show_headnode('hn-0'))['vncport'] is not None api.headnode_stop('hn-0') api.headnode_delete('hn-0')
def test_create_network_with_id_from_pool(self): """Test creation of networks with IDs from the pool.""" api.project_create('nuggets') # create a project owned network and get its network_id api.network_create('hammernet', 'nuggets', 'nuggets', '') network = api._must_find(model.Network, 'hammernet') net_id = int(network.network_id) assert network.allocated is True # create an admin owned network with net_id from pool api.network_create('nailnet', 'admin', '', 103) network = api._must_find(model.Network, 'nailnet') assert network.allocated is True # creating a network with the same network id should raise an error with pytest.raises(errors.BlockedError): api.network_create('redbone', 'admin', '', 103) with pytest.raises(errors.BlockedError): api.network_create('starfish', 'admin', '', net_id) # free the network ids by deleting the networks api.network_delete('hammernet') api.network_delete('nailnet') api._assert_absent(model.Network, 'hammernet') api._assert_absent(model.Network, 'nailnet') # after deletion we should be able to create admin networks with those # network_ids api.network_create('redbone', 'admin', '', 103) network = api._must_find(model.Network, 'redbone') assert int(network.network_id) == 103 api.network_create('starfish', 'admin', '', net_id) network = api._must_find(model.Network, 'starfish') assert int(network.network_id) == net_id
def test_create_network_with_id_from_pool(self): """Test creation of networks with IDs from the pool.""" api.project_create('nuggets') # create a project owned network and get its network_id api.network_create('hammernet', 'nuggets', 'nuggets', '') network = api.get_or_404(model.Network, 'hammernet') net_id = int(network.network_id) assert network.allocated is True # create an admin owned network with net_id from pool api.network_create('nailnet', 'admin', '', 103) network = api.get_or_404(model.Network, 'nailnet') assert network.allocated is True # creating a network with the same network id should raise an error with pytest.raises(errors.BlockedError): api.network_create('redbone', 'admin', '', 103) with pytest.raises(errors.BlockedError): api.network_create('starfish', 'admin', '', net_id) # free the network ids by deleting the networks api.network_delete('hammernet') api.network_delete('nailnet') api.absent_or_conflict(model.Network, 'hammernet') api.absent_or_conflict(model.Network, 'nailnet') # after deletion we should be able to create admin networks with those # network_ids api.network_create('redbone', 'admin', '', 103) network = api.get_or_404(model.Network, 'redbone') assert int(network.network_id) == 103 api.network_create('starfish', 'admin', '', net_id) network = api.get_or_404(model.Network, 'starfish') assert int(network.network_id) == net_id
def test_headnode_deletion_while_running(self): """Test deleting a headnode while it's running.""" api.project_create('anvil-nextgen') api.headnode_create('hn-0', 'anvil-nextgen', 'base-headnode-2') api.headnode_start('hn-0') api.headnode_delete('hn-0')
def test_isolated_networks(self): """Do a bunch of network operations on the switch, and verify things along the way. The above is super vague; unfortunately the setup operations are very slow, so it makes a huge difference to do everything in one pass. See the comments in-line to understand exactly what is being tested. """ def create_networks(): """Create networks and connect things to them. Test various things along the way. """ nodes = self.collect_nodes() # Create two networks network_create_simple('net-0', 'anvil-nextgen') network_create_simple('net-1', 'anvil-nextgen') ports = self.get_all_ports(nodes) # Assert that n0 and n1 are not on any network port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set() assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set() # Connect n0 and n1 to net-0 and net-1 respectively api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') api.node_connect_network(nodes[1].label, nodes[1].nics[0].label, 'net-1') deferred.apply_networking() # Assert that n0 and n1 are on isolated networks port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port]) # Add n2 and n3 to the same networks as n0 and n1 respectively api.node_connect_network(nodes[2].label, nodes[2].nics[0].label, 'net-0') api.node_connect_network(nodes[3].label, nodes[3].nics[0].label, 'net-1') deferred.apply_networking() # Assert that n2 and n3 have been added to n0 and n1's networks # respectively port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port, nodes[2].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port, nodes[3].nics[0].port]) def delete_networks(): """Tear down things set up by create_networks again, we do various checks along the way. """ # Query the DB for nodes on this project project = api.get_or_404(model.Project, 'anvil-nextgen') nodes = project.nodes ports = self.get_all_ports(nodes) # Remove all nodes from their networks. We do this in two different # ways for different ports to test the different mechanisms. For # the first two nodes we explicity remove the attachments. For the # latter two we call port_revert. for node in nodes[:2]: attachment = model.NetworkAttachment.query \ .filter_by(nic=node.nics[0]).one() api.node_detach_network(node.label, node.nics[0].label, attachment.network.label) for node in nodes[2:]: port = node.nics[0].port api.port_revert(port.owner.label, port.label) deferred.apply_networking() # Assert that none of the nodes are on any network port_networks = self.get_port_networks(ports) for node in nodes: assert self.get_network(node.nics[0].port, port_networks) == \ set() # Delete the networks api.network_delete('net-0') api.network_delete('net-1') # Create a project api.project_create('anvil-nextgen') create_networks() delete_networks()
def test_headnode_deletion_while_running(self): api.project_create('anvil-nextgen') api.headnode_create('hn-0', 'anvil-nextgen', 'base-headnode-2') api.headnode_start('hn-0') api.headnode_delete('hn-0')
def test_isolated_networks(self): def create_networks(): nodes = self.collect_nodes() # Create two networks network_create_simple('net-0', 'anvil-nextgen') network_create_simple('net-1', 'anvil-nextgen') ports = self.get_all_ports(nodes) # Assert that n0 and n1 are not on any network port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set() assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set() # Connect n0 and n1 to net-0 and net-1 respectively api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') api.node_connect_network(nodes[1].label, nodes[1].nics[0].label, 'net-1') deferred.apply_networking() # Assert that n0 and n1 are on isolated networks port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port]) # Add n2 and n3 to the same networks as n0 and n1 respectively api.node_connect_network(nodes[2].label, nodes[2].nics[0].label, 'net-0') api.node_connect_network(nodes[3].label, nodes[3].nics[0].label, 'net-1') deferred.apply_networking() # Assert that n2 and n3 have been added to n0 and n1's networks # respectively port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port, nodes[2].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port, nodes[3].nics[0].port]) def delete_networks(): # Query the DB for nodes on this project project = api._must_find(model.Project, 'anvil-nextgen') nodes = project.nodes ports = self.get_all_ports(nodes) # Remove all nodes from their networks. We do this in two different # ways for different ports to test the different mechanisms. For # the first two nodes we explicity remove the attachments. For the # latter two we call port_revert. for node in nodes[:2]: attachment = model.NetworkAttachment.query \ .filter_by(nic=node.nics[0]).one() api.node_detach_network(node.label, node.nics[0].label, attachment.network.label) for node in nodes[2:]: port = node.nics[0].port api.port_revert(port.owner.label, port.label) deferred.apply_networking() # Assert that none of the nodes are on any network port_networks = self.get_port_networks(ports) for node in nodes: assert self.get_network(node.nics[0].port, port_networks) == \ set() # Delete the networks api.network_delete('net-0') api.network_delete('net-1') # Create a project api.project_create('anvil-nextgen') create_networks() delete_networks()
def test_multi_networks(self): """A suite of tests to ensure that multiple networks can relate to a single port. Previously, there was an issue with get_port_networks() where it didn't return the full range of vlans, rather it returned only the ones at the ends of the ranges. These tests ensure that the full range of vlans can be fetched. Issue: https://github.com/CCI-MOC/hil/issues/921 See the inline comments for the specifics. """ def get_legal_channels(network): """Get the legal channels for a network.""" response_body = api.show_network(network) response_body = json.loads(response_body) return response_body['channels'] def create_multi_nets(): """Create multiple networks and connect them all to one port. Test that each network can be successfully added and discovered on the port. """ nodes = self.collect_nodes() # create 5 networks for i in range(5): network_create_simple('net-%d' % i, 'anvil-nextgen') ports = self.get_all_ports(nodes) # assert that node 0 is not on any network port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set() # get channel IDs for tagged versions of networks net_tag = {} for i in range(4): net_tag[i] = get_legal_channels('net-%d' % i)[1] # connect node 0 to net-0 in native mode api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') deferred.apply_networking() # connect node 0 to net-1 in tagged mode api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-1', channel=net_tag[1]) deferred.apply_networking() # connect node 0 to net-2 in tagged mode api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-2', channel=net_tag[2]) deferred.apply_networking() # connect node 0 to net-3 in tagged mode api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-3', channel=net_tag[3]) deferred.apply_networking() # assert that all networks show up on the port port_networks = self.get_port_networks(ports) networks = \ set([net for net, _channel in port_networks.get(nodes[0].nics[0].port)]) # create a list of networks with native net-0 included networks_added = set([get_legal_channels('net-0')[0], net_tag[1], net_tag[2], net_tag[3]]) assert networks == networks_added def teardown(): """Teardown the setup from create_multi_nets. """ # Query the DB for nodes on this project project = api.get_or_404(model.Project, 'anvil-nextgen') nodes = project.nodes ports = self.get_all_ports(nodes) # Remove all nodes from their networks using port_revert. for node in nodes: port = node.nics[0].port api.port_revert(port.owner.label, port.label) deferred.apply_networking() # Assert that none of the nodes are on any network port_networks = self.get_port_networks(ports) for node in nodes: assert self.get_network(node.nics[0].port, port_networks) == \ set() # Delete the networks api.network_delete('net-0') api.network_delete('net-1') api.network_delete('net-2') api.network_delete('net-3') # Create a project api.project_create('anvil-nextgen') create_multi_nets() teardown()
def test_multi_networks(self): """A suite of tests to ensure that multiple networks can relate to a single port. Previously, there was an issue with get_port_networks() where it didn't return the full range of vlans, rather it returned only the ones at the ends of the ranges. These tests ensure that the full range of vlans can be fetched. Issue: https://github.com/CCI-MOC/hil/issues/921 See the inline comments for the specifics. """ def get_legal_channels(network): """Get the legal channels for a network.""" response_body = api.show_network(network) response_body = json.loads(response_body) return response_body['channels'] def create_multi_nets(): """Create multiple networks and connect them all to one port. Test that each network can be successfully added and discovered on the port. """ nodes = self.collect_nodes() # create 5 networks for i in range(5): network_create_simple('net-%d' % i, 'anvil-nextgen') ports = self.get_all_ports(nodes) # assert that node 0 is not on any network port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set() # get channel IDs for tagged versions of networks net_tag = {} for i in range(4): net_tag[i] = get_legal_channels('net-%d' % i)[1] # connect node 0 to net-0 in native mode api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') deferred.apply_networking() # connect node 0 to net-1 in tagged mode api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-1', channel=net_tag[1]) deferred.apply_networking() # connect node 0 to net-2 in tagged mode api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-2', channel=net_tag[2]) deferred.apply_networking() # connect node 0 to net-3 in tagged mode api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-3', channel=net_tag[3]) deferred.apply_networking() # assert that all networks show up on the port port_networks = self.get_port_networks(ports) networks = \ set([net for net, _channel in port_networks.get(nodes[0].nics[0].port)]) # create a list of networks with native net-0 included networks_added = set([ get_legal_channels('net-0')[0], net_tag[1], net_tag[2], net_tag[3] ]) assert networks == networks_added def teardown(): """Teardown the setup from create_multi_nets. """ # Query the DB for nodes on this project project = api.get_or_404(model.Project, 'anvil-nextgen') nodes = project.nodes ports = self.get_all_ports(nodes) # Remove all nodes from their networks using port_revert. for node in nodes: port = node.nics[0].port api.port_revert(port.owner.label, port.label) deferred.apply_networking() # Assert that none of the nodes are on any network port_networks = self.get_port_networks(ports) for node in nodes: assert self.get_network(node.nics[0].port, port_networks) == \ set() # Delete the networks api.network_delete('net-0') api.network_delete('net-1') api.network_delete('net-2') api.network_delete('net-3') # Create a project api.project_create('anvil-nextgen') create_multi_nets() teardown()
def maintenance_proj_init(): """Create maintenance project.""" api.project_create('maintenance')
def test_duplicate_user_add_project(self): self.dbauth.user_create('charlie', 'secret') api.project_create('acme-corp') self.dbauth.user_add_project('charlie', 'acme-corp') with pytest.raises(errors.DuplicateError): self.dbauth.user_add_project('charlie', 'acme-corp')
def test_bad_user_remove_project(self): """Tests that removing a user from a project they're not in fails.""" self.dbauth.user_create('charlie', 'secret') api.project_create('acme-corp') with pytest.raises(errors.NotFoundError): self.dbauth.user_remove_project('charlie', 'acme-corp')
def test_duplicate_user_add_project(self): self.dbauth.user_create('charlie', 'secret') api.project_create('acme-corp') self.dbauth.user_add_project('charlie', 'acme-corp') with pytest.raises(api.DuplicateError): self.dbauth.user_add_project('charlie', 'acme-corp')
def test_isolated_networks(self): """Do a bunch of network operations on the switch, and verify things along the way. The above is super vague; unfortunately the setup operations are very slow, so it makes a huge difference to do everything in one pass. See the comments in-line to understand exactly what is being tested. """ def get_legal_channels(network): """Get the legal channels for a network.""" response_body = api.show_network(network) response_body = json.loads(response_body) return response_body['channels'] def create_networks(): """Create networks and connect things to them. Test various things along the way. """ nodes = self.collect_nodes() # Create four networks network_create_simple('net-0', 'anvil-nextgen') network_create_simple('net-1', 'anvil-nextgen') network_create_simple('net-2', 'anvil-nextgen') network_create_simple('net-3', 'anvil-nextgen') ports = self.get_all_ports(nodes) # get the switch name from any of the nics switch = nodes[0].nics[0].port.owner # Assert that n0 and n1 are not on any network port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set() assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set() # Get the channel ids for the tagged versions of the networks: net_tag = {} net_tag[0] = get_legal_channels('net-0')[1] net_tag[1] = get_legal_channels('net-1')[1] net_tag[2] = get_legal_channels('net-2')[1] # Connect node 0 to net-0 (native mode) api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') # before connecting node 1 to net-1 in tagged mode, we must check # if the switch supports nativeless trunk mode; if not, then we # add some native network and perform additional checks before # proceeding. if 'nativeless-trunk-mode' not in switch.get_capabilities(): # connecting the first network as tagged should raise an error with pytest.raises(errors.BlockedError): api.node_connect_network(nodes[1].label, nodes[1].nics[0].label, 'net-2', channel=net_tag[2]) api.node_connect_network(nodes[1].label, nodes[1].nics[0].label, 'net-2') deferred.apply_networking() # Connect node 1 to net-1 (tagged mode) api.node_connect_network(nodes[1].label, nodes[1].nics[0].label, 'net-1', channel=net_tag[1]) deferred.apply_networking() # Assert that n0 and n1 are on isolated networks port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port]) # Add n2 and n3 to the same networks as n0 and n1 respectively, but # with different channels (native vs. tagged) if 'nativeless-trunk-mode' not in switch.get_capabilities(): api.node_connect_network(nodes[2].label, nodes[2].nics[0].label, 'net-3') deferred.apply_networking() api.node_connect_network(nodes[2].label, nodes[2].nics[0].label, 'net-0', channel=net_tag[0]) api.node_connect_network(nodes[3].label, nodes[3].nics[0].label, 'net-1') deferred.apply_networking() # Assert that n2 and n3 have been added to n0 and n1's networks # respectively port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port, nodes[2].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port, nodes[3].nics[0].port]) # Verify that we can put nodes on more than one network, with # different channels: # at this point, node-2 is connected to net-0 (tagged) # and depending on the switch, to net-3 (native). Let's connect it # to net-1 (tagged) (which node-1 is connected to) api.node_connect_network(nodes[2].label, nodes[2].nics[0].label, 'net-1', channel=net_tag[1]) deferred.apply_networking() port_networks = self.get_port_networks(ports) # assert that node-2 was added to node-1's network correctly. assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port, nodes[2].nics[0].port, nodes[3].nics[0].port]) def delete_networks(): """Tear down things set up by create_networks again, we do various checks along the way. """ # Query the DB for nodes on this project project = api.get_or_404(model.Project, 'anvil-nextgen') nodes = project.nodes ports = self.get_all_ports(nodes) # Remove all nodes from their networks. We do this in two ways, to # test the different mechanisms. # For the first two nodes, we first build up a list of # the arguments to the API calls, which has no direct references to # database objects, and then make the API calls and invoke # deferred.apply_networking after. This is important -- # The API calls and apply_networking normally run in their own # transaction. We get away with not doing this in the tests because # we serialize everything ourselves, so there's no risk of # interference. If we were to hang on to references to database # objects across such calls however, things could get harry. all_attachments = [] net = namedtuple('net', 'node nic network channel') for node in nodes[:2]: attachments = model.NetworkAttachment.query \ .filter_by(nic=node.nics[0]).all() for attachment in attachments: all_attachments.append( net(node=node.label, nic=node.nics[0].label, network=attachment.network.label, channel=attachment.channel)) switch = nodes[0].nics[0].port.owner # in some switches, the native network can only be disconnected # after we remove all tagged networks first. The following checks # for that and rearranges the networks (all_attachments) such that # tagged networks are removed first. if 'nativeless-trunk-mode' not in switch.get_capabilities(): # sort by channel; vlan/<integer> comes before vlan/native # because the ASCII for numbers comes before ASCII for letters. all_attachments = sorted(all_attachments, key=lambda net: net.channel) for attachment in all_attachments: api.node_detach_network(attachment.node, attachment.nic, attachment.network) deferred.apply_networking() # For the second two nodes, we just call port_revert on the nic's # port. for node in nodes[2:]: port = node.nics[0].port api.port_revert(port.owner.label, port.label) deferred.apply_networking() # Assert that none of the nodes are on any network port_networks = self.get_port_networks(ports) for node in nodes: assert self.get_network(node.nics[0].port, port_networks) == \ set() # Delete the networks api.network_delete('net-0') api.network_delete('net-1') api.network_delete('net-2') api.network_delete('net-3') # Create a project api.project_create('anvil-nextgen') create_networks() delete_networks()
def test_isolated_networks(self): """Do a bunch of network operations on the switch, and verify things along the way. The above is super vague; unfortunately the setup operations are very slow, so it makes a huge difference to do everything in one pass. See the comments in-line to understand exactly what is being tested. """ def create_networks(): """Create networks and connect things to them. Test various things along the way. """ nodes = self.collect_nodes() # Create two networks network_create_simple('net-0', 'anvil-nextgen') network_create_simple('net-1', 'anvil-nextgen') ports = self.get_all_ports(nodes) # Assert that n0 and n1 are not on any network port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set() assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set() # Connect n0 and n1 to net-0 and net-1 respectively api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') api.node_connect_network(nodes[1].label, nodes[1].nics[0].label, 'net-1') deferred.apply_networking() # Assert that n0 and n1 are on isolated networks port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port]) # Add n2 and n3 to the same networks as n0 and n1 respectively api.node_connect_network(nodes[2].label, nodes[2].nics[0].label, 'net-0') api.node_connect_network(nodes[3].label, nodes[3].nics[0].label, 'net-1') deferred.apply_networking() # Assert that n2 and n3 have been added to n0 and n1's networks # respectively port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port, nodes[2].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port, nodes[3].nics[0].port]) def delete_networks(): """Tear down things set up by create_networks again, we do various checks along the way. """ # Query the DB for nodes on this project project = api._must_find(model.Project, 'anvil-nextgen') nodes = project.nodes ports = self.get_all_ports(nodes) # Remove all nodes from their networks. We do this in two different # ways for different ports to test the different mechanisms. For # the first two nodes we explicity remove the attachments. For the # latter two we call port_revert. for node in nodes[:2]: attachment = model.NetworkAttachment.query \ .filter_by(nic=node.nics[0]).one() api.node_detach_network(node.label, node.nics[0].label, attachment.network.label) for node in nodes[2:]: port = node.nics[0].port api.port_revert(port.owner.label, port.label) deferred.apply_networking() # Assert that none of the nodes are on any network port_networks = self.get_port_networks(ports) for node in nodes: assert self.get_network(node.nics[0].port, port_networks) == \ set() # Delete the networks api.network_delete('net-0') api.network_delete('net-1') # Create a project api.project_create('anvil-nextgen') create_networks() delete_networks()
def test_isolated_networks(self): def get_legal_channels(network): response_body = api.show_network(network) response_body = json.loads(response_body) return response_body['channels'] def create_networks(): nodes = self.collect_nodes() # Create two networks network_create_simple('net-0', 'anvil-nextgen') network_create_simple('net-1', 'anvil-nextgen') ports = self.get_all_ports(nodes) # Assert that n0 and n1 are not on any network port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set() assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set() # Get the channel ids for the tagged versions of the networks: net_tag = {} net_tag[0] = get_legal_channels('net-0')[1] net_tag[1] = get_legal_channels('net-1')[1] # Connect node 0 to net-0 (native mode) api.node_connect_network(nodes[0].label, nodes[0].nics[0].label, 'net-0') # Connect node 1 to net-1 (tagged mode) api.node_connect_network(nodes[1].label, nodes[1].nics[0].label, 'net-1', channel=net_tag[1]) deferred.apply_networking() # Assert that n0 and n1 are on isolated networks port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port]) # Add n2 and n3 to the same networks as n0 and n1 respectively, but # with different channels (native vs. tagged) api.node_connect_network(nodes[2].label, nodes[2].nics[0].label, 'net-0', channel=net_tag[0]) api.node_connect_network(nodes[3].label, nodes[3].nics[0].label, 'net-1') deferred.apply_networking() # Assert that n2 and n3 have been added to n0 and n1's networks # respectively port_networks = self.get_port_networks(ports) assert self.get_network(nodes[0].nics[0].port, port_networks) == \ set([nodes[0].nics[0].port, nodes[2].nics[0].port]) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port, nodes[3].nics[0].port]) # Verify that we can put nodes on more than one network, with # different channels: api.node_connect_network(nodes[2].label, nodes[2].nics[0].label, 'net-1') deferred.apply_networking() port_networks = self.get_port_networks(ports) assert self.get_network(nodes[1].nics[0].port, port_networks) == \ set([nodes[1].nics[0].port, nodes[2].nics[0].port, nodes[3].nics[0].port]) def delete_networks(): # Query the DB for nodes on this project project = api._must_find(model.Project, 'anvil-nextgen') nodes = project.nodes ports = self.get_all_ports(nodes) # Remove all nodes from their networks. We do this in two ways, to # test the different mechanisms. # For the first two nodes, we first build up a list of # the arguments to the API calls, which has no direct references to # database objects, and then make the API calls and invoke # deferred.apply_networking after. This is important -- # The API calls and apply_networking normally run in their own # transaction. We get away with not doing this in the tests because # we serialize everything ourselves, so there's no risk of # interference. If we were to hang on to references to database # objects across such calls however, things could get harry. all_attachments = [] for node in nodes[:2]: attachments = model.NetworkAttachment.query \ .filter_by(nic=node.nics[0]).all() for attachment in attachments: all_attachments.append((node.label, node.nics[0].label, attachment.network.label)) for attachment in all_attachments: api.node_detach_network(*attachment) deferred.apply_networking() # For the second two nodes, we just call port_revert on the nic's # port. for node in nodes[2:]: port = node.nics[0].port api.port_revert(port.owner.label, port.label) deferred.apply_networking() # Assert that none of the nodes are on any network port_networks = self.get_port_networks(ports) for node in nodes: assert self.get_network(node.nics[0].port, port_networks) == \ set() # Delete the networks api.network_delete('net-0') api.network_delete('net-1') # Create a project api.project_create('anvil-nextgen') create_networks() delete_networks()