Skip to content
This repository has been archived by the owner on Apr 27, 2022. It is now read-only.

Network_ACL #545

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/haas.cfg.dev-no-hardware
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dry_run=True
[extensions]
haas.ext.switches.mock =

# haas.ext.network_allocators.null =
haas.ext.network_allocators.null =
#
# Depending on what you're trying to do, you may want to use the vlan_pool
# network allocator instead of the null allocator. To do this, comment out the
Expand Down
53 changes: 46 additions & 7 deletions haas/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,45 @@ def project_remove_user(project, user):
local.db.commit()


@rest_call('POST', '/project/<project>/add_network')
def project_add_network(project, network):
"""Add a network to a project.

If the project or network does not exist, a NotFoundError will be raised.
"""
network = _must_find(model.Network, network)
project = _must_find(model.Project, project)
if project in network.access:
raise DuplicateError('Network %s is already in project %s'%
(network.label, project.label))

network.access.append(project)
local.db.commit()



@rest_call('POST', '/project/<project>/remove_network')
def project_remove_network(project, network):
"""Remove a network from a project.

If the project or network does not exist, a NotFoundError will be raised.
If the project is the creator of the netwrok a BlockedError will be raised.
"""
network = _must_find(model.Network, network)
project = _must_find(model.Project, project)
if project not in network.access:
raise NotFoundError("Network %s is not in project %s"%
(network.label, project.label))

if project is network.creator:
raise BlockedError("Project %s is creator of network %s and cannot be removed"%
(project.label, network.label))

network.access.remove(project)
local.db.commit()



# Node Code #
#############

Expand Down Expand Up @@ -294,7 +333,7 @@ def _have_attachment(nic, query):
if nic.current_action:
raise BlockedError("A networking operation is already active on the nic.")

if (network.access is not None) and (network.access is not project):
if (network.access) and (project not in network.access):
raise ProjectMismatchError("Project does not have access to given network.")

if _have_attachment(nic, model.NetworkAttachment.network == network):
Expand Down Expand Up @@ -490,7 +529,7 @@ def headnode_connect_network(headnode, hnic, network):

project = headnode.project

if (network.access is not None) and (network.access is not project):
if (network.access) and (project not in network.access):
raise ProjectMismatchError("Project does not have access to given network.")

hnic.network = network
Expand Down Expand Up @@ -547,14 +586,14 @@ def network_create(network, creator, access, net_id):
if net_id != "":
raise BadArgumentError("Project-created networks must use network ID allocation")
creator = _must_find(model.Project, creator)
access = _must_find(model.Project, access)
access = [_must_find(model.Project, access)]
else:
# Administrator-owned network
creator = None
if access == "":
access = None
access = []
else:
access = _must_find(model.Project, access)
access = [_must_find(model.Project, access)]

# Allocate net_id, if requested
if net_id == "":
Expand Down Expand Up @@ -614,8 +653,8 @@ def show_network(network):
else:
result['creator'] = network.creator.label

if network.access is not None:
result['access'] = network.access.label
if network.access:
result['access'] = [p.label for p in network.access]

return json.dumps(result)

Expand Down
14 changes: 14 additions & 0 deletions haas/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,20 @@ def project_remove_user(project, user):
url = object_url('project', project, 'remove_user')
do_post(url, data={'user': user})


@cmd
def project_add_network(project, network):
"""Add <network> to <project>"""
url = object_url('project', project, 'add_network')
do_post(url, data={'network': network})


@cmd
def project_remove_network(project, network):
"""Remove <network> from <project>"""
url = object_url('project', project, 'remove_network')
do_post(url, data={'network': network})

@cmd
def project_create(project):
"""Create a <project>"""
Expand Down
19 changes: 12 additions & 7 deletions haas/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
Column('user_id', ForeignKey('user.id')),
Column('project_id', ForeignKey('project.id')))

# A joining table for networks and projects, which have a many to many relationship:
network_projects = Table('network_projects', Base.metadata,
Column('project_id', ForeignKey('project.id')),
Column('network_id', ForeignKey('network.id')))


def init_db(create=False, uri=None):
"""Start up the DB connection.
Expand Down Expand Up @@ -257,20 +262,20 @@ class Network(Model):
creator = relationship("Project",
backref=backref('networks_created'),
foreign_keys=[creator_id])
# The project that has access to the network, or None if the network is
# public. This field determines who can connect a node or headnode to a
# network.
access_id = Column(ForeignKey('project.id'))
access = relationship("Project",
backref=backref('networks_access'),
foreign_keys=[access_id])
# True if network_id was allocated by the driver; False if it was
# assigned by an administrator.
allocated = Column(Boolean)

# An identifier meaningful to the networking driver:
network_id = Column(String, nullable=False)

# The project that has access to the network, or None if the network is
# public. This field determines who can connect a node or headnode to a
# network.
access = relationship("Project",
backref=backref('networks_access'),
secondary='network_projects')

def __init__(self, creator, access, allocated, network_id, label):
"""Create a network.

Expand Down
38 changes: 34 additions & 4 deletions tests/unit/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,36 @@ def test_bad_project_remove_user(self, db):
api.project_remove_user('acme-corp', 'alice')


class TestProjectAddDeleteNetwork:
"""Tests for adding and deleting a network from a project"""

def test_project_add_network(self, db):
api.project_create('acme-corp')
api.project_create('anvil-nextgen')
network_create_simple('hammernet', 'acme-corp')
api.project_add_network('anvil-nextgen', 'hammernet')
network = api._must_find(model.Network, 'hammernet')
project = api._must_find(model.Project, 'anvil-nextgen')
assert project in network.access
assert network in project.networks_access

def test_project_remove_network(self, db):
api.project_create('acme-corp')
api.project_create('anvil-nextgen')
network_create_simple('hammernet', 'acme-corp')
api.project_add_network('anvil-nextgen', 'hammernet')
api.project_remove_network('anvil-nextgen', 'hammernet')
network = api._must_find(model.Network, 'hammernet')
project = api._must_find(model.Project, 'anvil-nextgen')
assert project not in network.access
assert network not in project.networks_access

def test_project_remove_network_creator(self, db):
api.project_create('acme-corp')
network_create_simple('hammernet', 'acme-corp')
with pytest.raises(api.BlockedError):
api.project_remove_network('acme-corp', 'hammernet')

class TestNetworking:

def test_networking_involved(self, db):
Expand Down Expand Up @@ -1416,7 +1446,7 @@ def test_show_network_simple(self, db):
assert result == {
'name': 'spiderwebs',
'creator': 'anvil-nextgen',
'access': 'anvil-nextgen',
'access': ['anvil-nextgen'],
"channels": ["null"]
}

Expand Down Expand Up @@ -1444,7 +1474,7 @@ def test_show_network_provider(self, db):
assert result == {
'name': 'spiderwebs',
'creator': 'admin',
'access': 'anvil-nextgen',
'access': ['anvil-nextgen'],
'channels': ['null'],
}

Expand All @@ -1467,7 +1497,7 @@ def test_project_network(self, db):
project = api._must_find(model.Project, 'anvil-nextgen')
network = api._must_find(model.Network, 'hammernet')
assert network.creator is project
assert network.access is project
assert project in network.access
assert network.allocated is True

def test_project_network_imported_fails(self, db):
Expand Down Expand Up @@ -1495,7 +1525,7 @@ def test_admin_network(self, db):
api.network_create(network, 'admin', project_api, net_id)
network = api._must_find(model.Network, network)
assert network.creator is None
assert network.access is project_db
assert (project_db is None and not network.access) or project_db in network.access
assert network.allocated is allocated
network = api._must_find(model.Network, 'hammernet' + project_api + '35')
assert network.network_id == '35'
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,15 @@ class TestNetwork(ModelTest):

def sample_obj(self):
pj = Project('anvil-nextgen')
return Network(pj, pj, True, '102', 'hammernet')
return Network(pj, [pj], True, '102', 'hammernet')

class TestNetworkingAction(ModelTest):

def sample_obj(self):
nic = Nic(Node('node-99', 'ipmihost', 'root', 'tapeworm'),
'ipmi', '00:11:22:33:44:55')
project = Project('anvil-nextgen')
network = Network(project, project, True, '102', 'hammernet')
network = Network(project, [project], True, '102', 'hammernet')
return NetworkingAction(nic=nic,
new_network=network,
channel='null')