Skip to content

Commit 91dbaea

Browse files
authored
Merge pull request #4 from sandwichcloud/regionzones
Add regions and zones
2 parents 17cb683 + ca49bc1 commit 91dbaea

21 files changed

+569
-69
lines changed

.env-sample

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ RABBITMQ_PASSWORD=hunter2
2525
# Gitlab: deli_counter.auth.drivers.gitlab.driver:GitlabAuthDriver
2626
# OpenID: deli_counter.auth.drivers.openid.driver:OpenIDAuthDriver
2727
# LDAP: deli_counter.auth_drivers.ldap.driver:LDAPAuthDriver
28-
# DB: deli_counter.auth_drivers.db.driver:DBAuthDriver
29-
AUTH_DRIVER=deli_counter.auth.drivers.github.driver:GithubAuthDriver
28+
# DB: deli_counter.auth_drivers.db.driver:DBAuthDriver (always enabled)
29+
AUTH_DRIVERS=deli_counter.auth.drivers.github.driver:GithubAuthDriver
3030

3131
####################
3232
# GITHUB AUTH #

deli_counter/http/mounts/root/mount.py

-27
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import arrow
22
import cherrypy
33
from simple_settings import settings
4-
from sqlalchemy.orm import Query
54

65
from deli_counter.auth.manager import AuthManager
76
from ingredients_db.models.authn import AuthNToken, AuthNUser
@@ -64,32 +63,6 @@ def enforce_policy(self, policy_name, resource_object=None):
6463
self.auth_manager.enforce_policy(policy_name, session, cherrypy.request.token, cherrypy.request.user,
6564
cherrypy.request.project, resource_object)
6665

67-
def paginate(self, db_cls, response_cls, limit, marker, starting_query=None):
68-
if starting_query is None:
69-
starting_query = Query(db_cls)
70-
resp_objects = []
71-
with cherrypy.request.db_session() as session:
72-
starting_query.session = session
73-
db_objects = starting_query.order_by(db_cls.created_at.desc())
74-
75-
if marker is not None:
76-
marker = session.query(db_cls).filter(db_cls.id == marker).first()
77-
if marker is None:
78-
raise cherrypy.HTTPError(status=400, message="Unknown marker ID")
79-
db_objects = db_objects.filter(db_cls.created_at < marker.created_at)
80-
81-
db_objects = db_objects.limit(limit + 1)
82-
83-
for db_object in db_objects:
84-
resp_objects.append(response_cls.from_database(db_object))
85-
86-
more_pages = False
87-
if len(resp_objects) > limit:
88-
more_pages = True
89-
del resp_objects[-1] # Remove the last item to reset back to original limit
90-
91-
return resp_objects, more_pages
92-
9366
def resource_object(self, id_param, cls):
9467
resource_id = cherrypy.request.params[id_param]
9568
with cherrypy.request.db_session() as session:

deli_counter/http/mounts/root/routes/v1/auth/z/policies.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def get(self, policy_id: uuid.UUID):
6363
@cherrypy.tools.model_out_pagination(cls=ResponsePolicy)
6464
@cherrypy.tools.enforce_policy(policy_name="policies:list")
6565
def list(self, limit: int, marker: uuid.UUID):
66-
return self.mount.paginate(AuthZPolicy, ResponsePolicy, limit, marker)
66+
return self.paginate(AuthZPolicy, ResponsePolicy, limit, marker)
6767

6868
@Route('{policy_id}', methods=[RequestMethods.PUT])
6969
@cherrypy.tools.model_params(cls=ParamsPolicy)

deli_counter/http/mounts/root/routes/v1/auth/z/roles.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def get(self, role_id: uuid.UUID):
5050
@cherrypy.tools.model_out_pagination(cls=ResponseRole)
5151
@cherrypy.tools.enforce_policy(policy_name="roles:list")
5252
def list(self, limit: int, marker: uuid.UUID):
53-
return self.mount.paginate(AuthZRole, ResponseRole, limit, marker)
53+
return self.paginate(AuthZRole, ResponseRole, limit, marker)
5454

5555
@Route('{role_id}', methods=[RequestMethods.DELETE])
5656
@cherrypy.tools.model_params(cls=ParamsRole)

deli_counter/http/mounts/root/routes/v1/images.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from deli_counter.http.mounts.root.routes.v1.validation_models.images import ParamsImage, RequestCreateImage, \
88
ResponseImage, ParamsListImage
99
from ingredients_db.models.images import Image, ImageVisibility, ImageState
10+
from ingredients_db.models.region import Region, RegionState
1011
from ingredients_http.request_methods import RequestMethods
1112
from ingredients_http.route import Route
1213
from ingredients_http.router import Router
@@ -44,11 +45,21 @@ def create(self):
4445
if image is not None:
4546
raise cherrypy.HTTPError(409, 'An image with the requested file already exists.')
4647

48+
region = session.query(Region).filter(Region.id == request.region_id).first()
49+
if region is None:
50+
raise cherrypy.HTTPError(404, "A region with the requested id does not exist.")
51+
52+
if region.state != RegionState.CREATED:
53+
raise cherrypy.HTTPError(412,
54+
"The requested region is not in the following state: %s" %
55+
RegionState.CREATED.value)
56+
4757
image = Image()
4858
image.name = request.name
4959
image.file_name = request.file_name
5060
image.visibility = request.visibility
5161
image.project_id = project.id
62+
image.region_id = region.id
5263

5364
session.add(image)
5465
session.flush()
@@ -74,13 +85,19 @@ def get(self, image_id):
7485
@cherrypy.tools.model_params(cls=ParamsListImage)
7586
@cherrypy.tools.model_out_pagination(cls=ResponseImage)
7687
@cherrypy.tools.enforce_policy(policy_name="images:list")
77-
def list(self, limit: int, marker: uuid.UUID):
88+
def list(self, region_id, limit: int, marker: uuid.UUID):
7889
project = cherrypy.request.project
7990
starting_query = Query(Image).filter(
8091
or_(Image.project_id == project.id,
8192
Image.visibility == ImageVisibility.PUBLIC,
8293
Image.members.any(id=project.id)))
83-
return self.mount.paginate(Image, ResponseImage, limit, marker, starting_query=starting_query)
94+
if region_id is not None:
95+
with cherrypy.request.db_session() as session:
96+
region = session.query(Region).filter(Region.id == region_id).first()
97+
if region is None:
98+
raise cherrypy.HTTPError(404, "A region with the requested id does not exist.")
99+
starting_query = starting_query.filter(Image.region_id == region.id)
100+
return self.paginate(Image, ResponseImage, limit, marker, starting_query=starting_query)
84101

85102
@Route(route='{image_id}', methods=[RequestMethods.DELETE])
86103
@cherrypy.tools.project_scope()

deli_counter/http/mounts/root/routes/v1/instance.py

+57-8
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
from ingredients_db.models.network import Network, NetworkState
1313
from ingredients_db.models.network_port import NetworkPort
1414
from ingredients_db.models.public_key import PublicKey
15+
from ingredients_db.models.region import Region, RegionState
1516
from ingredients_db.models.task import Task
17+
from ingredients_db.models.zones import Zone, ZoneState
1618
from ingredients_http.request_methods import RequestMethods
1719
from ingredients_http.route import Route
1820
from ingredients_http.router import Router
@@ -34,19 +36,28 @@ def __init__(self):
3436
def create(self):
3537
request: RequestCreateInstance = cherrypy.request.model
3638

37-
# TODO: allow multiple instances to be created at once add -# to the name
38-
3939
with cherrypy.request.db_session() as session:
4040
project = cherrypy.request.project
4141

4242
instance = session.query(Instance).filter(Instance.project_id == project.id).filter(
4343
Instance.name == request.name).first()
44-
4544
if instance is not None:
4645
raise cherrypy.HTTPError(409, 'An instance already exists with the requested name.')
4746

48-
image = session.query(Image).filter(Image.id == request.image_id).first()
47+
region = session.query(Region).filter(Region.id == request.region_id).first()
48+
if region is None:
49+
raise cherrypy.HTTPError(404, "A region with the requested id does not exist.")
50+
51+
if region.state != RegionState.CREATED:
52+
raise cherrypy.HTTPError(412,
53+
"The requested region is not in the following state: %s" %
54+
RegionState.CREATED.value)
4955

56+
if region.schedulable is False:
57+
raise cherrypy.HTTPError(412, "The requested region is not currently schedulable.")
58+
59+
image = session.query(Image).filter(Image.id == request.image_id).filter(
60+
Image.region_id == region.id).first()
5061
if image is None:
5162
raise cherrypy.HTTPError(404, "An image with the requested id does not exist.")
5263

@@ -64,15 +75,30 @@ def create(self):
6475
# Image is public so don't error
6576
pass
6677

67-
network = session.query(Network).filter(Network.id == request.network_id).first()
68-
78+
network = session.query(Network).filter(Network.id == request.network_id).filter(
79+
Network.region_id == region.id).first()
6980
if network is None:
7081
raise cherrypy.HTTPError(404, "A network with the requested id does not exist.")
7182

7283
if network.state != NetworkState.CREATED:
7384
raise cherrypy.HTTPError(412, "The requested network is not in the '%s' state" % (
7485
NetworkState.CREATED.value))
7586

87+
zone = None
88+
if request.zone_id is not None:
89+
zone = session.query(Zone).filter(Zone.id == request.zone_id).filter(
90+
Zone.region_id == region.id).first()
91+
if zone is None:
92+
raise cherrypy.HTTPError(404, "A zone with the requested id does not exist.")
93+
94+
if zone.state != ZoneState.CREATED:
95+
raise cherrypy.HTTPError(412,
96+
"The requested zone is not in the following state: %s" %
97+
ZoneState.CREATED.value)
98+
99+
if zone.schedulable is False:
100+
raise cherrypy.HTTPError(412, "The requested zone is not currently schedulable.")
101+
76102
network_port = NetworkPort()
77103
network_port.network_id = network.id
78104
session.add(network_port)
@@ -84,6 +110,11 @@ def create(self):
84110
instance.project_id = project.id
85111
instance.network_port_id = network_port.id
86112
instance.tags = request.tags
113+
114+
instance.region_id = region.id
115+
if zone is not None:
116+
instance.zone_id = zone.id
117+
87118
session.add(instance)
88119
session.flush()
89120

@@ -120,11 +151,25 @@ def get(self, instance_id: uuid.UUID):
120151
@cherrypy.tools.model_params(cls=ParamsListInstance)
121152
@cherrypy.tools.model_out_pagination(cls=ResponseInstance)
122153
@cherrypy.tools.enforce_policy(policy_name="instances:list")
123-
def list(self, image_id: uuid.UUID, limit: int, marker: uuid.UUID):
154+
def list(self, image_id, region_id, zone_id, limit: int, marker: uuid.UUID):
124155
# TODO: allow filtering by tags
125156
project = cherrypy.request.project
126157
starting_query = Query(Instance).filter(Instance.project_id == project.id)
127-
return self.mount.paginate(Instance, ResponseInstance, limit, marker, starting_query=starting_query)
158+
if image_id is not None:
159+
starting_query = starting_query.filter(Instance.image_id == image_id)
160+
if region_id is not None:
161+
with cherrypy.request.db_session() as session:
162+
region = session.query(Region).filter(Region.id == region_id).first()
163+
if region is None:
164+
raise cherrypy.HTTPError(404, "A region with the requested id does not exist.")
165+
starting_query = starting_query.filter(Instance.region_id == region.id)
166+
if zone_id is not None:
167+
with cherrypy.request.db_session() as session:
168+
zone = session.query(Zone).filter(Zone.id == zone_id).first()
169+
if zone is None:
170+
raise cherrypy.HTTPError(404, "A zone with the requested id does not exist.")
171+
starting_query = starting_query.filter(Instance.zone_id == zone_id)
172+
return self.paginate(Instance, ResponseInstance, limit, marker, starting_query=starting_query)
128173

129174
@Route(route='{instance_id}', methods=[RequestMethods.DELETE])
130175
@cherrypy.tools.project_scope()
@@ -240,13 +285,17 @@ def action_image(self, instance_id: uuid.UUID):
240285
raise cherrypy.HTTPError(409, "Can only image an instance in the following state: %s" %
241286
InstanceState.STOPPED.value)
242287

288+
region = session.query(Region).join(Zone, Region.id == Zone.region_id).filter(
289+
Zone.id == instance.zone_id).one()
290+
243291
instance.state = InstanceState.IMAGING
244292

245293
image = Image()
246294
image.name = request.name
247295
image.file_name = str(instance.id)
248296
image.visibility = request.visibility
249297
image.project_id = instance.project_id
298+
image.region_id = region.id
250299

251300
session.add(image)
252301
session.flush()

deli_counter/http/mounts/root/routes/v1/networks.py

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import uuid
22

33
import cherrypy
4+
from sqlalchemy.orm import Query
45

56
from deli_counter.http.mounts.root.routes.v1.validation_models.networks import RequestCreateNetwork, ResponseNetwork, \
67
ParamsNetwork, ParamsListNetwork
78
from ingredients_db.models.network import Network, NetworkState
89
from ingredients_db.models.network_port import NetworkPort
10+
from ingredients_db.models.region import Region, RegionState
911
from ingredients_http.request_methods import RequestMethods
1012
from ingredients_http.route import Route
1113
from ingredients_http.router import Router
@@ -36,6 +38,15 @@ def create(self):
3638
if network is not None:
3739
raise cherrypy.HTTPError(409, "A network with the requested port group already exists.")
3840

41+
region = session.query(Region).filter(Region.id == request.region_id).first()
42+
if region is None:
43+
raise cherrypy.HTTPError(404, "A region with the requested id does not exist.")
44+
45+
if region.state != RegionState.CREATED:
46+
raise cherrypy.HTTPError(412,
47+
"The requested region is not in the following state: %s" %
48+
RegionState.CREATED.value)
49+
3950
# TODO: make sure cidr doesn't overlap with another network
4051

4152
network = Network()
@@ -46,6 +57,7 @@ def create(self):
4657
network.dns_servers = request.dns_servers
4758
network.pool_start = request.pool_start
4859
network.pool_end = request.pool_end
60+
network.region_id = region.id
4961

5062
session.add(network)
5163
session.flush()
@@ -69,8 +81,15 @@ def get(self, network_id):
6981
@cherrypy.tools.model_params(cls=ParamsListNetwork)
7082
@cherrypy.tools.model_out_pagination(cls=ResponseNetwork)
7183
@cherrypy.tools.enforce_policy(policy_name="networks:list")
72-
def list(self, limit: int, marker: uuid.UUID):
73-
return self.mount.paginate(Network, ResponseNetwork, limit, marker)
84+
def list(self, region_id, limit: int, marker: uuid.UUID):
85+
starting_query = Query(Network)
86+
if region_id is not None:
87+
with cherrypy.request.db_session() as session:
88+
region = session.query(Region).filter(Region.id == region_id).first()
89+
if region is None:
90+
raise cherrypy.HTTPError(404, "A region with the requested id does not exist.")
91+
starting_query = starting_query.filter(Network.region_id == region.id)
92+
return self.paginate(Network, ResponseNetwork, limit, marker, starting_query=starting_query)
7493

7594
@Route(route='{network_id}', methods=[RequestMethods.DELETE])
7695
@cherrypy.tools.model_params(cls=ParamsNetwork)

deli_counter/http/mounts/root/routes/v1/projects.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def get(self, project_id):
5555
def list(self, name: str, limit: int, marker: uuid.UUID):
5656
# TODO: only list projects that we are a member of
5757
# optional param to list all
58-
return self.mount.paginate(Project, ResponseProject, limit, marker)
58+
return self.paginate(Project, ResponseProject, limit, marker)
5959

6060
@Route(route='{project_id}', methods=[RequestMethods.DELETE])
6161
@cherrypy.tools.model_params(cls=ParamsProject)

0 commit comments

Comments
 (0)