stackamole-xblock 9.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fake/common/djangoapps/student/__init__.py +0 -0
- fake/common/djangoapps/student/models.py +15 -0
- fake/course/__init__.py +0 -0
- requirements/base.txt +25 -0
- requirements/flake8.txt +1 -0
- requirements/setup.txt +3 -0
- requirements/test.txt +19 -0
- samples/hot/sample-template.yaml +427 -0
- src/pip-delete-this-directory.txt +5 -0
- stackamole/__init__.py +8 -0
- stackamole/admin.py +130 -0
- stackamole/common.py +423 -0
- stackamole/jobs.py +211 -0
- stackamole/locale/config.yaml +14 -0
- stackamole/locale/de_DE/LC_MESSAGES/django.po +109 -0
- stackamole/locale/de_DE/LC_MESSAGES/djangojs.po +68 -0
- stackamole/locale/el/LC_MESSAGES/django.po +113 -0
- stackamole/locale/el/LC_MESSAGES/djangojs.po +77 -0
- stackamole/locale/en/LC_MESSAGES/django.po +107 -0
- stackamole/locale/en/LC_MESSAGES/djangojs.po +68 -0
- stackamole/locale/es_419/LC_MESSAGES/django.po +109 -0
- stackamole/locale/es_419/LC_MESSAGES/djangojs.po +68 -0
- stackamole/locale/et_EE/LC_MESSAGES/django.po +110 -0
- stackamole/locale/et_EE/LC_MESSAGES/djangojs.po +68 -0
- stackamole/locale/hi/LC_MESSAGES/django.po +111 -0
- stackamole/locale/hi/LC_MESSAGES/djangojs.po +72 -0
- stackamole/management/__init__.py +0 -0
- stackamole/management/commands/__init__.py +0 -0
- stackamole/management/commands/reaper.py +21 -0
- stackamole/management/commands/suspender.py +20 -0
- stackamole/migrations/0001_initial.py +43 -0
- stackamole/migrations/0002_stacklog.py +47 -0
- stackamole/migrations/0003_blanks.py +84 -0
- stackamole/migrations/0004_auto_20190715_1053.py +24 -0
- stackamole/migrations/0005_auto_20190811_1555.py +56 -0
- stackamole/migrations/0006_auto_20200107_1332.py +44 -0
- stackamole/migrations/0007_add_delete_by_and_delete_age.py +34 -0
- stackamole/migrations/0008_add_database_defaults_for_stack_key_and_password.py +23 -0
- stackamole/migrations/0009_add_null_true_for_key_and_password.py +23 -0
- stackamole/migrations/0010_add_user_foreign_key.py +52 -0
- stackamole/migrations/0011_allow_null_for_learner.py +29 -0
- stackamole/migrations/0012_add_suspend_by.py +21 -0
- stackamole/migrations/0013_migrate_app_label.py +71 -0
- stackamole/migrations/__init__.py +0 -0
- stackamole/models.py +106 -0
- stackamole/openstack.py +132 -0
- stackamole/provider.py +437 -0
- stackamole/public/css/main.css +203 -0
- stackamole/public/js/client.js +269 -0
- stackamole/public/js/guacamole-common-js/0.9.13-incubating-all.min.js +1 -0
- stackamole/public/js/guacamole-common-js/1.4.0-all.min.js +155 -0
- stackamole/public/js/guacamole-common-js/1.5.2-all.min.js +161 -0
- stackamole/public/js/guacamole-common-js/1.5.4-all.min.js +161 -0
- stackamole/public/js/guacamole-common-js/1.5.5-all.min.js +161 -0
- stackamole/public/js/main.js +492 -0
- stackamole/public/js/plugins.js +95 -0
- stackamole/stackamole.py +1226 -0
- stackamole/static/html/lab.html +88 -0
- stackamole/static/html/main.html +93 -0
- stackamole/tasks.py +947 -0
- stackamole_guacamole_client/__init__.py +0 -0
- stackamole_guacamole_client/asgi.py +26 -0
- stackamole_guacamole_client/consumers.py +95 -0
- stackamole_guacamole_client/settings.py +87 -0
- stackamole_xblock-9.4.0.dist-info/METADATA +647 -0
- stackamole_xblock-9.4.0.dist-info/RECORD +89 -0
- stackamole_xblock-9.4.0.dist-info/WHEEL +5 -0
- stackamole_xblock-9.4.0.dist-info/entry_points.txt +3 -0
- stackamole_xblock-9.4.0.dist-info/licenses/LICENSE +661 -0
- stackamole_xblock-9.4.0.dist-info/top_level.txt +7 -0
- tests/__init__.py +0 -0
- tests/resources/course/stackamole/fake_lab_1.xml +9 -0
- tests/resources/course/stackamole/fake_lab_2.xml +9 -0
- tests/resources/course/stackamole/fake_lab_hook_events_1.xml +4 -0
- tests/resources/course/stackamole/fake_lab_hook_events_2.xml +3 -0
- tests/resources/course/stackamole/fake_lab_no_port_name.xml +3 -0
- tests/resources/course/stackamole/fake_lab_no_port_number.xml +3 -0
- tests/resources/course/stackamole/fake_lab_no_provider_capacity.xml +5 -0
- tests/resources/course/stackamole/fake_lab_no_provider_name.xml +3 -0
- tests/unit/__init__.py +0 -0
- tests/unit/test_admin.py +170 -0
- tests/unit/test_commands.py +31 -0
- tests/unit/test_common.py +307 -0
- tests/unit/test_jobs.py +649 -0
- tests/unit/test_models.py +72 -0
- tests/unit/test_openstack.py +149 -0
- tests/unit/test_provider.py +847 -0
- tests/unit/test_stackamole.py +1664 -0
- tests/unit/test_tasks.py +2324 -0
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from django.contrib.auth.models import User
|
|
2
|
+
from django.db import models
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AnonymousUserId(models.Model):
|
|
6
|
+
"""
|
|
7
|
+
Pared down from:
|
|
8
|
+
|
|
9
|
+
edx-platform/common/djangoapps/student/models.py:AnonymousUserId
|
|
10
|
+
|
|
11
|
+
This is so we don't have to install all of edx-platform just to test it.
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
|
|
15
|
+
anonymous_user_id = models.CharField(unique=True, max_length=32)
|
fake/course/__init__.py
ADDED
|
File without changes
|
requirements/base.txt
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
apscheduler<4
|
|
2
|
+
cliff<=4.7
|
|
3
|
+
dogpile.cache<=1.3
|
|
4
|
+
keystoneauth1<=5.8
|
|
5
|
+
openstacksdk<=4.1
|
|
6
|
+
osc-lib<=3.1
|
|
7
|
+
os-client-config<=2.1
|
|
8
|
+
oslo.config<=9.7.1
|
|
9
|
+
oslo.serialization<=5.5
|
|
10
|
+
oslo.utils<=7.4
|
|
11
|
+
oslo.i18n<6.6
|
|
12
|
+
paramiko>=4.0,<4.1
|
|
13
|
+
pymongo<=4.4.0 # keep in sync with edx-platform
|
|
14
|
+
python-heatclient<=4.0
|
|
15
|
+
python-keystoneclient<=5.5
|
|
16
|
+
python-novaclient<=18.7
|
|
17
|
+
tenacity<=9.0
|
|
18
|
+
|
|
19
|
+
# for stackamole_guacamole_client
|
|
20
|
+
django<=5.2.13
|
|
21
|
+
channels>=4.3,<4.4
|
|
22
|
+
daphne<=4.0.0
|
|
23
|
+
mysqlclient<=2.2.8 # keep in sync with edx-platform
|
|
24
|
+
jsonfield>=3.2.0,<4 # keep in sync with edx-platform
|
|
25
|
+
pyguacamole>=0.11
|
requirements/flake8.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flake8
|
requirements/setup.txt
ADDED
requirements/test.txt
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-r base.txt
|
|
2
|
+
|
|
3
|
+
# These package versions must be kept in sync with edx-platform as much as possible.
|
|
4
|
+
xblock-sdk<0.9.0;python_version<"3.9"
|
|
5
|
+
xblock-sdk;python_version>="3.9"
|
|
6
|
+
six==1.16.0
|
|
7
|
+
lazy<=1.5
|
|
8
|
+
openedx-django-pyfs>=3.8.0
|
|
9
|
+
sqlparse>=0.4.1,<0.5
|
|
10
|
+
web-fragments<=2.0.0
|
|
11
|
+
edx-opaque-keys<=3.0.0
|
|
12
|
+
|
|
13
|
+
# dependencies of supported nested XBlocks
|
|
14
|
+
path.py>=12.4
|
|
15
|
+
|
|
16
|
+
# Tooling
|
|
17
|
+
ddt
|
|
18
|
+
nose
|
|
19
|
+
coverage
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
heat_template_version: 2014-10-16
|
|
2
|
+
|
|
3
|
+
description: |
|
|
4
|
+
Sample lab environment.
|
|
5
|
+
- Consists of one deployment host (deploy) and three regular hosts
|
|
6
|
+
(daisy, eric, frank).
|
|
7
|
+
- Provides two separate networks.
|
|
8
|
+
- Installs an apt proxy on the deploy node for quicker updates
|
|
9
|
+
- Waits for cloud-init provisioning to finish before reports CREATE_COMPLETE
|
|
10
|
+
- Creates cron job to sync time with hardware clock, to work around stack
|
|
11
|
+
suspension desync
|
|
12
|
+
- Starts screen on connection
|
|
13
|
+
|
|
14
|
+
parameters:
|
|
15
|
+
flavor:
|
|
16
|
+
type: string
|
|
17
|
+
description: Flavor to use for servers
|
|
18
|
+
default: m1.medium.e
|
|
19
|
+
public_net_id:
|
|
20
|
+
type: string
|
|
21
|
+
description: Public network ID
|
|
22
|
+
default: 62954df1-05bb-42e5-9960-ca921cccaeeb
|
|
23
|
+
image:
|
|
24
|
+
type: string
|
|
25
|
+
description: Image ID or name
|
|
26
|
+
default: ubuntu-14.04.3-server-cloudimg
|
|
27
|
+
ubuntu_mirror:
|
|
28
|
+
type: string
|
|
29
|
+
description: Ubuntu package archive mirror
|
|
30
|
+
default: mirrors.se.eu.kernel.org
|
|
31
|
+
timeout:
|
|
32
|
+
type: number
|
|
33
|
+
description: Timeout for stack creation to finish
|
|
34
|
+
default: 900
|
|
35
|
+
|
|
36
|
+
resources:
|
|
37
|
+
all_done:
|
|
38
|
+
type: OS::Heat::WaitCondition
|
|
39
|
+
properties:
|
|
40
|
+
handle: {get_resource: all_done_handle}
|
|
41
|
+
count: 4
|
|
42
|
+
timeout: {get_param: timeout}
|
|
43
|
+
|
|
44
|
+
all_done_handle:
|
|
45
|
+
type: OS::Heat::WaitConditionHandle
|
|
46
|
+
|
|
47
|
+
proxy_ready:
|
|
48
|
+
type: OS::Heat::WaitCondition
|
|
49
|
+
properties:
|
|
50
|
+
handle: {get_resource: proxy_ready_handle}
|
|
51
|
+
count: 1
|
|
52
|
+
timeout: {get_param: timeout}
|
|
53
|
+
|
|
54
|
+
proxy_ready_handle:
|
|
55
|
+
type: OS::Heat::WaitConditionHandle
|
|
56
|
+
|
|
57
|
+
deploy_config:
|
|
58
|
+
type: OS::Heat::CloudConfig
|
|
59
|
+
properties:
|
|
60
|
+
cloud_config:
|
|
61
|
+
### bootcmd: runs arbitrary commands early in the boot sequence
|
|
62
|
+
bootcmd:
|
|
63
|
+
- apt-get update
|
|
64
|
+
- apt-get -y install --no-install-recommends apt-cacher-ng
|
|
65
|
+
- { get_attr: ['proxy_ready_handle', 'curl_cli'] }
|
|
66
|
+
## users: defines users, sets passwords and SSH authorized keys, and
|
|
67
|
+
### enables sudo privileges.
|
|
68
|
+
users:
|
|
69
|
+
- default
|
|
70
|
+
- name: training
|
|
71
|
+
gecos: Training User
|
|
72
|
+
groups: users,adm
|
|
73
|
+
ssh-authorized-keys:
|
|
74
|
+
- { get_attr: [ training_key, public_key ] }
|
|
75
|
+
lock-passwd: false
|
|
76
|
+
shell: /bin/bash
|
|
77
|
+
sudo: ALL=(ALL) NOPASSWD:ALL
|
|
78
|
+
### mounts: enables and disables mount points
|
|
79
|
+
#mounts:
|
|
80
|
+
# - [ 'vdb', null ]
|
|
81
|
+
### write_files: writes arbitrary files
|
|
82
|
+
write_files:
|
|
83
|
+
- path: /etc/cron.d/hwclock
|
|
84
|
+
permissions: '0644'
|
|
85
|
+
content: |
|
|
86
|
+
# /etc/cron.d/hwclock: periodically sync the system time to the hardware clock
|
|
87
|
+
SHELL=/bin/sh
|
|
88
|
+
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
|
89
|
+
*/5 * * * * root hwclock -s
|
|
90
|
+
- path: /etc/hosts
|
|
91
|
+
permissions: '0644'
|
|
92
|
+
content: |
|
|
93
|
+
127.0.0.1 localhost
|
|
94
|
+
192.168.122.100 deploy.example.com deploy
|
|
95
|
+
192.168.122.114 daisy.example.com daisy
|
|
96
|
+
192.168.122.115 eric.example.com eric
|
|
97
|
+
192.168.122.116 frank.example.com frank
|
|
98
|
+
- path: /etc/network/interfaces.d/eth1.cfg
|
|
99
|
+
permissions: '0644'
|
|
100
|
+
content: |
|
|
101
|
+
auto eth1
|
|
102
|
+
iface eth1 inet dhcp
|
|
103
|
+
- path: /etc/ntp.conf
|
|
104
|
+
permissions: '0644'
|
|
105
|
+
content: |
|
|
106
|
+
driftfile /var/lib/ntp/ntp.drift
|
|
107
|
+
statistics loopstats peerstats clockstats
|
|
108
|
+
filegen loopstats file loopstats type day enable
|
|
109
|
+
filegen peerstats file peerstats type day enable
|
|
110
|
+
filegen clockstats file clockstats type day enable
|
|
111
|
+
server 0.ubuntu.pool.ntp.org iburst
|
|
112
|
+
server 1.ubuntu.pool.ntp.org iburst
|
|
113
|
+
server 2.ubuntu.pool.ntp.org iburst
|
|
114
|
+
server 3.ubuntu.pool.ntp.org iburst
|
|
115
|
+
server ntp.ubuntu.com
|
|
116
|
+
restrict -4 default kod notrap nomodify nopeer noquery
|
|
117
|
+
restrict -6 default kod notrap nomodify nopeer noquery
|
|
118
|
+
restrict 127.0.0.1
|
|
119
|
+
restrict ::1
|
|
120
|
+
- path: /home/training/.ssh/id_rsa
|
|
121
|
+
permissions: '0600'
|
|
122
|
+
content: { get_attr: [ training_key, private_key ] }
|
|
123
|
+
- path: /home/training/.profile
|
|
124
|
+
permissions: '0644'
|
|
125
|
+
content: |
|
|
126
|
+
exec /usr/bin/screen -xRR
|
|
127
|
+
apt_proxy: "http://localhost:3142"
|
|
128
|
+
apt_mirror:
|
|
129
|
+
str_replace:
|
|
130
|
+
template: http://%mirror/ubuntu
|
|
131
|
+
params:
|
|
132
|
+
"%mirror": { get_param: ubuntu_mirror }
|
|
133
|
+
### package_update: refresh package on first boot
|
|
134
|
+
package_update: false
|
|
135
|
+
### package_upgrade: runs package update on first boot
|
|
136
|
+
package_upgrade: true
|
|
137
|
+
### packages: installs packages
|
|
138
|
+
packages:
|
|
139
|
+
- attr
|
|
140
|
+
- ntp
|
|
141
|
+
- screen
|
|
142
|
+
runcmd:
|
|
143
|
+
- chown -R training:training /home/training
|
|
144
|
+
- chsh -s /bin/bash training
|
|
145
|
+
- { get_attr: ['all_done_handle', 'curl_cli'] }
|
|
146
|
+
|
|
147
|
+
host_config:
|
|
148
|
+
type: OS::Heat::CloudConfig
|
|
149
|
+
properties:
|
|
150
|
+
cloud_config:
|
|
151
|
+
## users: defines users, sets passwords and SSH authorized keys, and
|
|
152
|
+
### enables sudo privileges.
|
|
153
|
+
users:
|
|
154
|
+
- default
|
|
155
|
+
- name: training
|
|
156
|
+
gecos: Training User
|
|
157
|
+
groups: users,adm
|
|
158
|
+
ssh-authorized-keys:
|
|
159
|
+
- { get_attr: [ training_key, public_key ] }
|
|
160
|
+
lock-passwd: false
|
|
161
|
+
shell: /bin/bash
|
|
162
|
+
sudo: ALL=(ALL) NOPASSWD:ALL
|
|
163
|
+
### bootcmd: runs arbitrary commands early in the boot sequence
|
|
164
|
+
# bootcmd:
|
|
165
|
+
### mounts: enables and disables mount points
|
|
166
|
+
#mounts:
|
|
167
|
+
# - [ 'vdb', null ]
|
|
168
|
+
### write_files: writes arbitrary files
|
|
169
|
+
write_files:
|
|
170
|
+
- path: /etc/cron.d/ntpdate
|
|
171
|
+
permissions: '0644'
|
|
172
|
+
content: |
|
|
173
|
+
# /etc/cron.d/ntpdate: periodically sync the system time to the NTP server
|
|
174
|
+
SHELL=/bin/sh
|
|
175
|
+
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
|
176
|
+
*/5 * * * * root ntpdate deploy
|
|
177
|
+
- path: /etc/hosts
|
|
178
|
+
permissions: '0644'
|
|
179
|
+
content: |
|
|
180
|
+
127.0.0.1 localhost
|
|
181
|
+
192.168.122.100 deploy.example.com deploy
|
|
182
|
+
192.168.122.114 daisy.example.com daisy
|
|
183
|
+
192.168.122.115 eric.example.com eric
|
|
184
|
+
192.168.122.116 frank.example.com frank
|
|
185
|
+
- path: /etc/network/interfaces.d/eth1.cfg
|
|
186
|
+
permissions: '0644'
|
|
187
|
+
content: |
|
|
188
|
+
auto eth1
|
|
189
|
+
iface eth1 inet dhcp
|
|
190
|
+
- path: /home/training/.ssh/id_rsa
|
|
191
|
+
permissions: '0600'
|
|
192
|
+
content: { get_attr: [ training_key, private_key ] }
|
|
193
|
+
apt_proxy: "http://deploy:3142"
|
|
194
|
+
apt_mirror:
|
|
195
|
+
str_replace:
|
|
196
|
+
template: http://%mirror/ubuntu
|
|
197
|
+
params:
|
|
198
|
+
"%mirror": { get_param: ubuntu_mirror }
|
|
199
|
+
### package_update: refresh package on first boot
|
|
200
|
+
package_update: true
|
|
201
|
+
### package_upgrade: runs package update on first boot
|
|
202
|
+
package_upgrade: true
|
|
203
|
+
### packages: installs packages
|
|
204
|
+
packages:
|
|
205
|
+
- screen
|
|
206
|
+
- attr
|
|
207
|
+
runcmd:
|
|
208
|
+
- chown -R training:training /home/training
|
|
209
|
+
- chsh -s /bin/bash training
|
|
210
|
+
- { get_attr: ['all_done_handle', 'curl_cli'] }
|
|
211
|
+
|
|
212
|
+
server_security_group:
|
|
213
|
+
type: OS::Neutron::SecurityGroup
|
|
214
|
+
properties:
|
|
215
|
+
description: Neutron security group rules
|
|
216
|
+
name: server_security_group
|
|
217
|
+
rules:
|
|
218
|
+
- remote_mode: 'remote_group_id'
|
|
219
|
+
remote_group_id:
|
|
220
|
+
direction: ingress
|
|
221
|
+
- remote_ip_prefix: 0.0.0.0/0
|
|
222
|
+
protocol: tcp
|
|
223
|
+
direction: ingress
|
|
224
|
+
port_range_min: 22
|
|
225
|
+
port_range_max: 22
|
|
226
|
+
- remote_ip_prefix: 0.0.0.0/0
|
|
227
|
+
protocol: icmp
|
|
228
|
+
|
|
229
|
+
training_key:
|
|
230
|
+
type: OS::Nova::KeyPair
|
|
231
|
+
properties:
|
|
232
|
+
name: { get_param: 'OS::stack_name' }
|
|
233
|
+
save_private_key: true
|
|
234
|
+
|
|
235
|
+
management_net:
|
|
236
|
+
type: OS::Neutron::Net
|
|
237
|
+
properties:
|
|
238
|
+
name: management-net
|
|
239
|
+
|
|
240
|
+
management_sub_net:
|
|
241
|
+
type: OS::Neutron::Subnet
|
|
242
|
+
properties:
|
|
243
|
+
name: management-sub-net
|
|
244
|
+
network_id: { get_resource: management_net }
|
|
245
|
+
cidr: 192.168.122.0/24
|
|
246
|
+
gateway_ip: 192.168.122.1
|
|
247
|
+
dns_nameservers:
|
|
248
|
+
- 8.8.8.8
|
|
249
|
+
- 8.8.4.4
|
|
250
|
+
enable_dhcp: true
|
|
251
|
+
allocation_pools:
|
|
252
|
+
- start: "192.168.122.2"
|
|
253
|
+
end: "192.168.122.50"
|
|
254
|
+
|
|
255
|
+
router:
|
|
256
|
+
type: OS::Neutron::Router
|
|
257
|
+
|
|
258
|
+
router_gateway:
|
|
259
|
+
type: OS::Neutron::RouterGateway
|
|
260
|
+
properties:
|
|
261
|
+
router_id: { get_resource: router }
|
|
262
|
+
network_id: { get_param: public_net_id }
|
|
263
|
+
|
|
264
|
+
router_interface:
|
|
265
|
+
type: OS::Neutron::RouterInterface
|
|
266
|
+
properties:
|
|
267
|
+
router_id: { get_resource: router }
|
|
268
|
+
subnet_id: { get_resource: management_sub_net }
|
|
269
|
+
|
|
270
|
+
data_net:
|
|
271
|
+
type: OS::Neutron::Net
|
|
272
|
+
properties:
|
|
273
|
+
name: data-net
|
|
274
|
+
|
|
275
|
+
data_sub_net:
|
|
276
|
+
type: OS::Neutron::Subnet
|
|
277
|
+
properties:
|
|
278
|
+
name: data-sub-net
|
|
279
|
+
network_id: { get_resource: data_net }
|
|
280
|
+
cidr: 192.168.133.0/24
|
|
281
|
+
gateway_ip: ""
|
|
282
|
+
dns_nameservers:
|
|
283
|
+
- 8.8.8.8
|
|
284
|
+
- 8.8.4.4
|
|
285
|
+
enable_dhcp: true
|
|
286
|
+
allocation_pools:
|
|
287
|
+
- start: "192.168.133.2"
|
|
288
|
+
end: "192.168.133.50"
|
|
289
|
+
|
|
290
|
+
deploy:
|
|
291
|
+
type: OS::Nova::Server
|
|
292
|
+
properties:
|
|
293
|
+
name: deploy
|
|
294
|
+
image: { get_param: image }
|
|
295
|
+
flavor: { get_param: flavor }
|
|
296
|
+
user_data: { get_resource: deploy_config }
|
|
297
|
+
user_data_format: RAW
|
|
298
|
+
networks:
|
|
299
|
+
- port: { get_resource: deploy_management_port }
|
|
300
|
+
- port: { get_resource: deploy_data_port }
|
|
301
|
+
|
|
302
|
+
deploy_management_port:
|
|
303
|
+
type: OS::Neutron::Port
|
|
304
|
+
properties:
|
|
305
|
+
network_id: { get_resource: management_net }
|
|
306
|
+
fixed_ips:
|
|
307
|
+
- ip_address: 192.168.122.100
|
|
308
|
+
security_groups:
|
|
309
|
+
- { get_resource: server_security_group }
|
|
310
|
+
|
|
311
|
+
deploy_data_port:
|
|
312
|
+
type: OS::Neutron::Port
|
|
313
|
+
properties:
|
|
314
|
+
network_id: { get_resource: data_net }
|
|
315
|
+
fixed_ips:
|
|
316
|
+
- ip_address: 192.168.133.100
|
|
317
|
+
|
|
318
|
+
deploy_floating_ip:
|
|
319
|
+
type: OS::Neutron::FloatingIP
|
|
320
|
+
properties:
|
|
321
|
+
floating_network_id: { get_param: public_net_id }
|
|
322
|
+
port_id: { get_resource: deploy_management_port }
|
|
323
|
+
fixed_ip_address: 192.168.122.100
|
|
324
|
+
|
|
325
|
+
daisy:
|
|
326
|
+
type: OS::Nova::Server
|
|
327
|
+
properties:
|
|
328
|
+
name: daisy
|
|
329
|
+
image: { get_param: image }
|
|
330
|
+
flavor: { get_param: flavor }
|
|
331
|
+
user_data: { get_resource: host_config }
|
|
332
|
+
user_data_format: RAW
|
|
333
|
+
metadata:
|
|
334
|
+
wait_condition_data: { get_attr: [ proxy_ready, data ] }
|
|
335
|
+
networks:
|
|
336
|
+
- port: { get_resource: daisy_management_port }
|
|
337
|
+
- port: { get_resource: daisy_data_port }
|
|
338
|
+
|
|
339
|
+
daisy_management_port:
|
|
340
|
+
type: OS::Neutron::Port
|
|
341
|
+
properties:
|
|
342
|
+
network_id: { get_resource: management_net }
|
|
343
|
+
fixed_ips:
|
|
344
|
+
- ip_address: 192.168.122.114
|
|
345
|
+
security_groups:
|
|
346
|
+
- { get_resource: server_security_group }
|
|
347
|
+
|
|
348
|
+
daisy_data_port:
|
|
349
|
+
type: OS::Neutron::Port
|
|
350
|
+
properties:
|
|
351
|
+
network_id: { get_resource: data_net }
|
|
352
|
+
fixed_ips:
|
|
353
|
+
- ip_address: 192.168.133.114
|
|
354
|
+
|
|
355
|
+
eric:
|
|
356
|
+
type: OS::Nova::Server
|
|
357
|
+
properties:
|
|
358
|
+
name: eric
|
|
359
|
+
image: { get_param: image }
|
|
360
|
+
flavor: { get_param: flavor }
|
|
361
|
+
user_data: { get_resource: host_config }
|
|
362
|
+
user_data_format: RAW
|
|
363
|
+
metadata:
|
|
364
|
+
wait_condition_data: { get_attr: [ proxy_ready, data ] }
|
|
365
|
+
networks:
|
|
366
|
+
- port: { get_resource: eric_management_port }
|
|
367
|
+
- port: { get_resource: eric_data_port }
|
|
368
|
+
|
|
369
|
+
eric_management_port:
|
|
370
|
+
type: OS::Neutron::Port
|
|
371
|
+
properties:
|
|
372
|
+
network_id: { get_resource: management_net }
|
|
373
|
+
fixed_ips:
|
|
374
|
+
- ip_address: 192.168.122.115
|
|
375
|
+
security_groups:
|
|
376
|
+
- { get_resource: server_security_group }
|
|
377
|
+
|
|
378
|
+
eric_data_port:
|
|
379
|
+
type: OS::Neutron::Port
|
|
380
|
+
properties:
|
|
381
|
+
network_id: { get_resource: data_net }
|
|
382
|
+
fixed_ips:
|
|
383
|
+
- ip_address: 192.168.133.115
|
|
384
|
+
|
|
385
|
+
frank:
|
|
386
|
+
type: OS::Nova::Server
|
|
387
|
+
properties:
|
|
388
|
+
name: frank
|
|
389
|
+
image: { get_param: image }
|
|
390
|
+
flavor: { get_param: flavor }
|
|
391
|
+
user_data: { get_resource: host_config }
|
|
392
|
+
user_data_format: RAW
|
|
393
|
+
metadata:
|
|
394
|
+
wait_condition_data: { get_attr: [ proxy_ready, data ] }
|
|
395
|
+
networks:
|
|
396
|
+
- port: { get_resource: frank_management_port }
|
|
397
|
+
- port: { get_resource: frank_data_port }
|
|
398
|
+
|
|
399
|
+
frank_management_port:
|
|
400
|
+
type: OS::Neutron::Port
|
|
401
|
+
properties:
|
|
402
|
+
network_id: { get_resource: management_net }
|
|
403
|
+
fixed_ips:
|
|
404
|
+
- ip_address: 192.168.122.116
|
|
405
|
+
security_groups:
|
|
406
|
+
- { get_resource: server_security_group }
|
|
407
|
+
|
|
408
|
+
frank_data_port:
|
|
409
|
+
type: OS::Neutron::Port
|
|
410
|
+
properties:
|
|
411
|
+
network_id: { get_resource: data_net }
|
|
412
|
+
fixed_ips:
|
|
413
|
+
- ip_address: 192.168.133.116
|
|
414
|
+
|
|
415
|
+
outputs:
|
|
416
|
+
public_ip:
|
|
417
|
+
description: Floating IP address of deploy in public network
|
|
418
|
+
value: { get_attr: [ deploy_floating_ip, floating_ip_address ] }
|
|
419
|
+
private_key:
|
|
420
|
+
description: Training private key
|
|
421
|
+
value: { get_attr: [ training_key, private_key ] }
|
|
422
|
+
reboot_on_resume:
|
|
423
|
+
description: Servers to reboot upon resume
|
|
424
|
+
value:
|
|
425
|
+
- { get_resource: daisy }
|
|
426
|
+
- { get_resource: eric }
|
|
427
|
+
- { get_resource: frank }
|
stackamole/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from importlib import metadata
|
|
2
|
+
# __version__ attribute as suggested by (deferred) PEP 396:
|
|
3
|
+
# https://www.python.org/dev/peps/pep-0396/
|
|
4
|
+
#
|
|
5
|
+
# Single-source package definition as suggested (among several
|
|
6
|
+
# options) by:
|
|
7
|
+
# https://packaging.python.org/guides/single-sourcing-package-version/
|
|
8
|
+
__version__ = metadata.version('stackamole-xblock')
|
stackamole/admin.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Admin registration for stackamole Xblock models
|
|
3
|
+
"""
|
|
4
|
+
from django import forms
|
|
5
|
+
from django.contrib import admin
|
|
6
|
+
|
|
7
|
+
from .common import (
|
|
8
|
+
DELETE_COMPLETE,
|
|
9
|
+
SUSPEND_COMPLETE,
|
|
10
|
+
VALID_STATES,
|
|
11
|
+
get_xblock_settings
|
|
12
|
+
)
|
|
13
|
+
from .models import Stack, StackLog
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@admin.action(description="Mark selected stacks as SUSPEND_COMPLETE")
|
|
17
|
+
def mark_suspended(modeladmin, request, queryset):
|
|
18
|
+
"""
|
|
19
|
+
Mark selected stacks as successfully suspended.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
queryset.update(status=SUSPEND_COMPLETE)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@admin.action(description="Mark selected stacks as DELETE_COMPLETE")
|
|
26
|
+
def mark_deleted(modeladmin, request, queryset):
|
|
27
|
+
"""
|
|
28
|
+
Mark selected stacks as deleted, and reset the provider.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
queryset.update(status=DELETE_COMPLETE, provider="")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@admin.action(description="Clear stacklog for selected stacks")
|
|
35
|
+
def clear_stacklog(self, request, queryset):
|
|
36
|
+
logs = StackLog.objects.filter(stack_id__in=queryset.values('id'))
|
|
37
|
+
logs.delete()
|
|
38
|
+
self.message_user(
|
|
39
|
+
request,
|
|
40
|
+
f'Stacklog cleared for {queryset.count()} stacks!')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def student_email(stack):
|
|
44
|
+
"""
|
|
45
|
+
Display the learner email for admin page.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
if stack.learner:
|
|
49
|
+
return stack.learner.email
|
|
50
|
+
else:
|
|
51
|
+
return ""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
student_email.short_description = "Email"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class StackAdminForm(forms.ModelForm):
|
|
58
|
+
"""
|
|
59
|
+
Custom form for constructing `state` and `provider` choices dynamically
|
|
60
|
+
without the need to modify the model.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
class Meta:
|
|
64
|
+
model = Stack
|
|
65
|
+
exclude = ['id']
|
|
66
|
+
|
|
67
|
+
provider = forms.ChoiceField(required=False)
|
|
68
|
+
status = forms.ChoiceField()
|
|
69
|
+
delete_by = forms.DateTimeField(required=False)
|
|
70
|
+
|
|
71
|
+
def __init__(self, *args, **kwargs):
|
|
72
|
+
super(StackAdminForm, self).__init__(*args, **kwargs)
|
|
73
|
+
|
|
74
|
+
# Gather provider choices. If the record has `providers` set
|
|
75
|
+
# (signifying the course author limited which providers this course can
|
|
76
|
+
# use), only let the user pick from those. Otherwise, allow any
|
|
77
|
+
# providers configured in the system.
|
|
78
|
+
if (self.instance and
|
|
79
|
+
len(self.instance.providers)):
|
|
80
|
+
provider_choices = [""] + [p["name"] for p in
|
|
81
|
+
self.instance.providers]
|
|
82
|
+
else:
|
|
83
|
+
settings = get_xblock_settings()
|
|
84
|
+
providers = settings.get("providers", {})
|
|
85
|
+
provider_choices = [""] + list(providers)
|
|
86
|
+
|
|
87
|
+
self.fields["provider"].choices = [(i, i) for i in provider_choices]
|
|
88
|
+
self.fields["status"].choices = [("", "")] + \
|
|
89
|
+
[(i, i) for i in VALID_STATES]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class StackAdmin(admin.ModelAdmin):
|
|
93
|
+
"""
|
|
94
|
+
Custom stack admin class. Uses a custom form, enables searching and
|
|
95
|
+
filtering, and totally excludes fields used by the XBlock solely to
|
|
96
|
+
communicate with tasks or jobs. Of the remaining fields, only `status` and
|
|
97
|
+
`provider` should be editable.
|
|
98
|
+
|
|
99
|
+
Adding stacks manually is forbidden, but deleting records is allowed. A
|
|
100
|
+
custom action allows marking stacks as `DELETE_COMPLETE` in bulk.
|
|
101
|
+
|
|
102
|
+
"""
|
|
103
|
+
form = StackAdminForm
|
|
104
|
+
list_display = ("name", "course_id", student_email, "status", "provider",
|
|
105
|
+
"launch_timestamp",)
|
|
106
|
+
list_filter = ("course_id", "status", "provider",)
|
|
107
|
+
list_editable = ("status", "provider")
|
|
108
|
+
list_select_related = True
|
|
109
|
+
actions = [mark_suspended, mark_deleted, clear_stacklog]
|
|
110
|
+
search_fields = (
|
|
111
|
+
"name", "course_id", "learner__email", "status", "provider")
|
|
112
|
+
readonly_fields = ("name", student_email, "course_id", "run", "protocol",
|
|
113
|
+
"port", "ip", "launch_timestamp", "suspend_timestamp",
|
|
114
|
+
"suspend_by", "created_on", "error_msg", "delete_age",)
|
|
115
|
+
exclude = ("student_id", "providers", "hook_script", "hook_events",
|
|
116
|
+
"launch_task_id", "user", "key", "password", "learner")
|
|
117
|
+
ordering = ("-launch_timestamp",)
|
|
118
|
+
|
|
119
|
+
def get_changelist_form(self, request, **kwargs):
|
|
120
|
+
return StackAdminForm
|
|
121
|
+
|
|
122
|
+
def has_add_permission(self, request, obj=None):
|
|
123
|
+
"""
|
|
124
|
+
Adding stacks manually is not supported.
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
admin.site.register(Stack, StackAdmin)
|