Merge "Updated config.env.sample" liblaas
authorSawyer Bergeron <sbergeron@iol.unh.edu>
Fri, 25 Mar 2022 15:23:15 +0000 (15:23 +0000)
committerGerrit Code Review <gerrit@opnfv.org>
Fri, 25 Mar 2022 15:23:15 +0000 (15:23 +0000)
src/api/migrations/0022_merge_20211102_2136.py [new file with mode: 0644]
src/api/models.py
src/api/views.py
src/booking/quick_deployer.py
src/dashboard/admin_utils.py
src/resource_inventory/migrations/0023_cloudinitfile_generated.py [new file with mode: 0644]
src/resource_inventory/models.py
src/static/js/dashboard.js
src/templates/base/notifier/end_booking.html
src/templates/base/notifier/expiring_booking.html
src/templates/base/notifier/new_booking.html

diff --git a/src/api/migrations/0022_merge_20211102_2136.py b/src/api/migrations/0022_merge_20211102_2136.py
new file mode 100644 (file)
index 0000000..bb27ae4
--- /dev/null
@@ -0,0 +1,14 @@
+# Generated by Django 2.2 on 2021-11-02 21:36
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('api', '0019_auto_20210907_1448'),
+        ('api', '0021_auto_20210405_1943'),
+    ]
+
+    operations = [
+    ]
index 5928ea9..93168f5 100644 (file)
@@ -20,6 +20,7 @@ from django.utils import timezone
 import json
 import uuid
 import yaml
+import re
 
 from booking.models import Booking
 from resource_inventory.models import (
@@ -362,7 +363,8 @@ class GeneratedCloudConfig(models.Model):
 
     def _normalize_username(self, username: str) -> str:
         # TODO: make usernames posix compliant
-        return username
+        s = re.sub(r'\W+', '', username)
+        return s
 
     def _get_ssh_string(self, username: str) -> str:
         user = User.objects.get(username=username)
@@ -502,7 +504,7 @@ class GeneratedCloudConfig(models.Model):
         return main_dict
 
     def serialize(self) -> str:
-        return yaml.dump(self._to_dict())
+        return yaml.dump(self._to_dict(), width=float("inf"))
 
 
 class APILog(models.Model):
index a10b3ec..1516374 100644 (file)
@@ -295,7 +295,7 @@ def resource_ci_userdata(request, lab_name="", job_id="", resource_id="", file_i
         "datasource_list": ["None"],
     }
 
-    return HttpResponse(yaml.dump(cloud_dict), status=200)
+    return HttpResponse(yaml.dump(cloud_dict, width=float("inf")), status=200)
 
 
 @csrf_exempt
@@ -310,9 +310,9 @@ def resource_ci_userdata_directory(request, lab_name="", job_id="", resource_id=
     files = resource.config.cloud_init_files
     files = [{"id": file.id, "priority": file.priority} for file in files.order_by("priority").all()]
 
-    d = {
-        'merge_failures': []
-    }
+    d = {}
+
+    merge_failures = []
 
     merger = Merger(
         [
@@ -325,7 +325,7 @@ def resource_ci_userdata_directory(request, lab_name="", job_id="", resource_id=
 
     for f in resource.config.cloud_init_files.order_by("priority").all():
         try:
-            other_dict = yaml.load(f.text)
+            other_dict = yaml.safe_load(f.text)
             if not (type(d) is dict):
                 raise Exception("CI file was valid yaml but was not a dict")
 
@@ -335,9 +335,12 @@ def resource_ci_userdata_directory(request, lab_name="", job_id="", resource_id=
             print("Failed to merge file in, as it had invalid content:", f.id)
             print("File text was:")
             print(f.text)
-            d['merge_failures'].append({f.id: str(e)})
+            merge_failures.append({f.id: str(e)})
+
+    if len(merge_failures) > 0:
+        d['merge_failures'] = merge_failures
 
-    file = CloudInitFile.create(text=yaml.dump(d), priority=0)
+    file = CloudInitFile.create(text=yaml.dump(d, width=float("inf")), priority=0)
 
     return HttpResponse(json.dumps([{"id": file.id, "priority": file.priority}]), status=200)
 
index 31865be..4b85d76 100644 (file)
@@ -82,6 +82,8 @@ def update_template(old_template, image, hostname, user, global_cloud_config=Non
         description=old_template.description,
         public=False,
         temporary=True,
+        private_vlan_pool=old_template.private_vlan_pool,
+        public_vlan_pool=old_template.public_vlan_pool,
         copy_of=old_template
     )
 
index b105e96..045caeb 100644 (file)
@@ -22,7 +22,8 @@ from resource_inventory.models import (
     DiskProfile,
     CpuProfile,
     RamProfile,
-    Interface
+    Interface,
+    CloudInitFile,
 )
 
 import json
@@ -50,7 +51,7 @@ from booking.models import Booking
 from notifier.manager import NotificationHandler
 from api.models import JobFactory
 
-from api.models import JobStatus
+from api.models import JobStatus, Job, GeneratedCloudConfig
 
 
 def print_div():
@@ -528,6 +529,30 @@ def extend_booking(booking_id, days=0, hours=0, minutes=0, weeks=0):
     booking.save()
 
 
+def regenerate_cloud_configs(booking_id):
+    b = Booking.objects.get(id=booking_id)
+    for res in b.resource.get_resources():
+        res.config.cloud_init_files.set(res.config.cloud_init_files.filter(generated=False))  # careful!
+        res.config.save()
+        cif = GeneratedCloudConfig.objects.create(resource_id=res.labid, booking=b, rconfig=res.config)
+        cif.save()
+        cif = CloudInitFile.create(priority=0, text=cif.serialize())
+        cif.save()
+        res.config.cloud_init_files.add(cif)
+        res.config.save()
+
+
+def set_job_new(job_id):
+    j = Job.objects.get(id=job_id)
+    b = j.booking
+    regenerate_cloud_configs(b.id)
+    for task in j.get_tasklist():
+        task.status = JobStatus.NEW
+        task.save()
+    j.status = JobStatus.NEW
+    j.save()
+
+
 def docs(function=None, fulltext=False):
     """
     Print documentation for a given function in admin_utils.
diff --git a/src/resource_inventory/migrations/0023_cloudinitfile_generated.py b/src/resource_inventory/migrations/0023_cloudinitfile_generated.py
new file mode 100644 (file)
index 0000000..b309753
--- /dev/null
@@ -0,0 +1,18 @@
+# Generated by Django 2.2 on 2021-12-17 18:54
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('resource_inventory', '0022_auto_20210925_2028'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='cloudinitfile',
+            name='generated',
+            field=models.BooleanField(default=False),
+        ),
+    ]
index aefd5ce..5d87430 100644 (file)
@@ -157,6 +157,7 @@ class CloudInitFile(models.Model):
 
     # higher priority is applied later, so "on top" of existing files
     priority = models.IntegerField()
+    generated = models.BooleanField(default=False)
 
     @classmethod
     def merge_strategy(cls):
index 85a337b..e3978e3 100644 (file)
@@ -418,9 +418,12 @@ class MultipleSelectFilterWidget {
             cnt += required_resources[resource];
         }
 
-        if (cnt > 1 && hostname && image) {
+        if (cnt > 1 && hostname) {
             hostname.readOnly = true;
-            image.disabled = true;
+            // we only disable hostname modification because there is no sane case where you want all hosts to have the same hostname
+            // image is still allowed to be set across all hosts, but is filtered to the set of images that are commonly applicable still
+            // if no images exist that would apply to all hosts in a pod, then the user is restricted to not setting an image
+            // and the default image for each host is used
         }
 
         this.updateAvailibility();
index ebad027..22fbd58 100644 (file)
 <html>
+    <link rel="preconnect" href="https://fonts.googleapis.com">
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+    <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet">
+
+    <style>
+        h2{
+            font-family: 'Source Sans Pro';
+        }
+        p{
+            font-family: Montserrat;
+        }
+        li{
+            font-family: Montserrat;
+        }
+        a{
+            color: #f8f9fa;
+            text-decoration: none;
+        }
+        button{
+            background-color:#a3c1db;
+            color: #343a40;
+            border: 0px;
+            border-radius: 4mm;
+            height: 25px;
+            width: 310px;
+            text-align: center;
+            margin: 15px;
+            text-decoration: none;
+            font-family: Montserrat;
+            font-size: 16;
+        }
+        button:focus{
+            border: 0px;
+        }
+        .textFormatting{
+            text-align: center;
+            color:#343a40;
+            margin: auto;
+
+        }
+        .content{
+            background-color: #f8f9fa;
+            position: center;
+        }
+        table{
+            margin-left: auto;
+            margin-right: auto;
+            border: 1px solid #343a40;
+            border-collapse: collapse;
+        }
+        tr{
+            border-bottom: 1px solid #343a40;
+        }
+        td{
+            color:#343a40;
+            padding: 3px;
+            font-family: Montserrat
+        }
+        .row1{
+            background-color: #7598b6;
+        }
+        .row2{
+            background-color: #d7e2f0;
+        }
+        .row3{
+            background-color: #d2e5f3;
+        }
+    </style>
+
     <body>
-        <div id="message_content_wrapper">
+        <div id="message_content_wrapper" class="textFormatting content">
             {% if owner %}
-            <h3>Your booking has expired</h3>
+            <h2>Your Booking Has Expired.</h2>
             <p>Your booking has ended and the machines have been cleaned up.</p>
             <p>Thank you for working on {{booking.lab.project}}, and feel free to book more machines if you need them.</p>
             {% else %}
-            <h3>A booking that you collaborated on has expired</h3>
-            <p>The booking owned by {{booking.owner.username}} that you worked on has ended</p>
+            <h2>A Booking That You Collaborated on Has Expired.</h2>
+            <p>The booking owned by {{booking.owner.username}} that you worked on has ended.</p>
             <p>Thank you for contributing to {{booking.lab.project}}.</p>
             {% endif %}
-            <p>Booking information:</p>
-            <ul>
-                <li>owner: {{booking.owner.username}}</li>
-                <li>id: {{booking.id}}</li>
-                <li>lab: {{booking.resource.template.lab.lab_user.username}}</li>
-                <li>resource: {{booking.resource.template.name}}</li>
-                <li>start: {{booking.start}}</li>
-                <li>end: {{booking.end}}</li>
-                <li>purpose: {{booking.purpose}}</li>
-                <li>collaborators:
-                    <ul>
-                        {% for user in booking.collaborators.all %}
-                        <li>{{user.username}}</li>
-                        {% empty %}
-                        <li>No collaborators</li>
-                        {% endfor %}
-                    </ul>
-                </li>
-            </ul>
-
-            <p>You can find more detailed information <a href=/booking/detail/{{booking.id}}/>Here</a></p>
+            <br>
+            <table>
+                <tr class="row1">
+                    <td style="text-align: center; font-size: larger;" colspan="2">Booking Information:</td>
+                </tr>
+                <tr class="row2">
+                    <td>Owner:</td>
+                    <td>{{booking.owner.username}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>id:</td>
+                    <td>{{booking.id}}</td>
+                </tr>
+                <tr class="row2">
+                    <td>lab:</td>
+                    <td>{{booking.resource.template.lab.lab_user.username}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>resource:</td>
+                    <td>{{booking.resource.template.name}}</td>
+                </tr>
+                <tr class="row2">
+                    <td>start:</td>
+                    <td>{{booking.start}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>end:</td>
+                    <td>{{booking.end}}</td>
+                </tr>
+                <tr class="row2">
+                    <td>purpose:</td>
+                    <td>{{booking.purpose}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>collaborator{% booking.collaborators.all.count|pluralize:"s" %}:</td>
+                    <td></td>
+                </tr>
+                {% for user in booking.collaborators.all %}
+                <tr class="{% cycle 'row2' 'row3' %}">
+                    <td></td>
+                    <td>{{user.username}}</td>
+                </tr>
+                {% empty %}
+                <tr class="row2">
+                    <td></td>
+                    <td>No collaborators</td>
+                </tr>
+                {% endfor %}
+            </table>
+            <button type="button" onclick="window.location.href='/booking/detail/{{booking.id}}'">You can find more detailed information here</button>
         </div>
     </body>
 </html>
index 89042fe..7247081 100644 (file)
 <html>
+    <link rel="preconnect" href="https://fonts.googleapis.com">
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+    <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet">
+
+    <style>
+        h2{
+            font-family: 'Source Sans Pro';
+        }
+        p{
+            font-family: Montserrat;
+        }
+        li{
+            font-family: Montserrat;
+        }
+        a{
+            color: #f8f9fa;
+            text-decoration: none;
+        }
+        button{
+            background-color:#a3c1db;
+            color: #343a40;
+            border: 0px;
+            border-radius: 4mm;
+            height: 25px;
+            width: 310px;
+            text-align: center;
+            margin: 15px;
+            text-decoration: none;
+            font-family: Montserrat;
+            font-size: 16;
+        }
+        button:focus{
+            border: 0px;
+        }
+        .textFormatting{
+            text-align: center;
+            color:#343a40;
+            margin: auto;
+
+        }
+        .content{
+            background-color: #f8f9fa;
+            position: center;
+        }
+        table{
+            margin-left: auto;
+            margin-right: auto;
+            border: 1px solid #343a40;
+            border-collapse: collapse;
+        }
+        tr{
+            border-bottom: 1px solid #343a40;
+        }
+        td{
+            color:#343a40;
+            padding: 3px;
+            font-family: Montserrat
+        }
+        .row1{
+            background-color: #7598b6;
+        }
+        .row2{
+            background-color: #d7e2f0;
+        }
+        .row3{
+            background-color: #d2e5f3;
+        }
+    </style>
+
+
     <body>
-        <div id="message_content_wrapper">
+        <div id="message_content_wrapper" class="textFormatting content">
             {% if owner %}
-            <h3>Your booking is about to expire</h3>
+            <h2>Your Booking Is About to Expire.</h2>
             <p>Your booking will expire within 48 hours ({{booking.end}}).</p>
             {% else %}
-            <h3>A booking that you collaborate on is about to expire</h3>
-            <p>The booking owned by {{booking.owner.username}} that you work on is about to expire</p>
+            <h2>A Booking That You Collaborate on Is About to Expire.</h2>
+            <p>The booking owned by {{booking.owner.username}} that you work on is about to expire.</p>
             {% endif %}
             <p>Please take the time to backup all data or extend the booking if needed.</p>
-            <p>Booking information:</p>
-            <ul>
-                <li>owner: {{booking.owner.username}}</li>
-                <li>id: {{booking.id}}</li>
-                <li>lab: {{booking.resource.template.lab.lab_user.username}}</li>
-                <li>resource: {{booking.resource.template.name}}</li>
-                <li>start: {{booking.start}}</li>
-                <li>end: {{booking.end}}</li>
-                <li>purpose: {{booking.purpose}}</li>
-                <li>collaborators:
-                    <ul>
-                        {% for user in booking.collaborators.all %}
-                        <li>{{user.username}}</li>
-                        {% empty %}
-                        <li>No collaborators</li>
-                        {% endfor %}
-                    </ul>
-                </li>
-            </ul>
-
-            <p>You can find more detailed information <a href=/booking/detail/{{booking.id}}/>Here</a></p>
+            <br>
+            <table>
+                <tr class="row1">
+                    <td style="text-align: center; font-size: larger;" colspan="2">Booking Information:</td>
+                </tr>
+                <tr class="row2">
+                    <td>Owner:</td>
+                    <td>{{booking.owner.username}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>id:</td>
+                    <td>{{booking.id}}</td>
+                </tr>
+                <tr class="row2">
+                    <td>lab:</td>
+                    <td>{{booking.resource.template.lab.lab_user.username}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>resource:</td>
+                    <td>{{booking.resource.template.name}}</td>
+                </tr>
+                <tr class="row2">
+                    <td>start:</td>
+                    <td>{{booking.start}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>end:</td>
+                    <td>{{booking.end}}</td>
+                </tr>
+                <tr class="row2">
+                    <td>purpose:</td>
+                    <td>{{booking.purpose}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>collaborator{% booking.collaborators.all.count|pluralize:"s" %}:</td>
+                    <td></td>
+                </tr>
+                {% for user in booking.collaborators.all %}
+                <tr class="{% cycle 'row2' 'row3' %}">
+                    <td></td>
+                    <td>{{user.username}}</td>
+                </tr>
+                {% empty %}
+                <tr class="row2">
+                    <td></td>
+                    <td>No collaborators</td>
+                </tr>
+                {% endfor %}
+            </table>
+            <button type="button" onclick="window.location.href='/booking/detail/{{booking.id}}'">You can find more detailed information here</button>
         </div>
     </body>
 </html>
index 7580694..d886f40 100644 (file)
 <html>
+    <link rel="preconnect" href="https://fonts.googleapis.com">
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+    <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet">
+
+    <style>
+        h2{
+            font-family: 'Source Sans Pro';
+        }
+        p{
+            font-family: Montserrat;
+        }
+        li{
+            font-family: Montserrat;
+        }
+        a{
+            color: #f8f9fa;
+            text-decoration: none;
+        }
+        button{
+            background-color:#a3c1db;
+            color: #343a40;
+            border: 0px;
+            border-radius: 4mm;
+            height: 25px;
+            width: 310px;
+            text-align: center;
+            margin: 15px;
+            text-decoration: none;
+            font-family: Montserrat;
+            font-size: 16;
+        }
+        button:focus{
+            border: 0px;
+        }
+        .textFormatting{
+            text-align: center;
+            color:#343a40;
+            margin: auto;
+
+        }
+        .content{
+            background-color: #f8f9fa;
+            position: center;
+        }
+        table{
+            margin-left: auto;
+            margin-right: auto;
+            border: 1px solid #343a40;
+            border-collapse: collapse;
+        }
+        tr{
+            border-bottom: 1px solid #343a40;
+        }
+        td{
+            color:#343a40;
+            padding: 3px;
+            font-family: Montserrat
+        }
+        .row1{
+            background-color: #7598b6;
+            font-weight: bolder;
+        }
+        .row2{
+            background-color: #d7e2f0;
+        }
+        .row3{
+            background-color: #d2e5f3;
+        }
+    </style>
     <body>
-        <div id="message_content_wrapper">
+        <div id="message_content_wrapper" class="textFormatting content">
             {% if owner %}
-            <h3>You have created a new booking</h3>
+            <h2>You Have Created a New Booking.</h2>
             <p>We have recieved your booking request and will start working on it right away.</p>
             {% else %}
-            <h3>You have been added as a collaborator to a booking</h3>
+            <h2>You Have Been Added as a Collaborator to a Booking.</h2>
             <p>{{booking.owner.username}} has given you access to their booking.</p>
             {% endif %}
-            <p>Booking information:</p>
-            <ul>
-                <li>owner: {{booking.owner.username}}</li>
-                <li>id: {{booking.id}}</li>
-                <li>lab: {{booking.resource.template.lab.lab_user.username}}</li>
-                <li>resource: {{booking.resource.template.name}}</li>
-                <li>start: {{booking.start}}</li>
-                <li>end: {{booking.end}}</li>
-                <li>purpose: {{booking.purpose}}</li>
-                <li>collaborators:
-                    <ul>
-                        {% for user in booking.collaborators.all %}
-                        <li>{{user.username}}</li>
-                        {% empty %}
-                        <li>No collaborators</li>
-                        {% endfor %}
-                    </ul>
-                </li>
-            </ul>
+            <br>
 
-            <p>You can find more detailed information <a href=/booking/detail/{{booking.id}}/>Here</a></p>
+            <table>
+                <tr class="row1">
+                    <td style="text-align: center; font-size: larger;" colspan="2">Booking Information:</td>
+                </tr>
+                <tr class="row2">
+                    <td>Owner:</td>
+                    <td>{{booking.owner.username}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>id:</td>
+                    <td>{{booking.id}}</td>
+                </tr>
+                <tr class="row2">
+                    <td>lab:</td>
+                    <td>{{booking.resource.template.lab.lab_user.username}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>resource:</td>
+                    <td>{{booking.resource.template.name}}</td>
+                </tr>
+                <tr class="row2">
+                    <td>start:</td>
+                    <td>{{booking.start}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>end:</td>
+                    <td>{{booking.end}}</td>
+                </tr>
+                <tr class="row2">
+                    <td>purpose:</td>
+                    <td>{{booking.purpose}}</td>
+                </tr>
+                <tr class="row3">
+                    <td>collaborators:</td>
+                    <td></td>
+                </tr>
+                {% for user in booking.collaborators.all %}
+                <tr class="{% cycle 'row2' 'row3' %}">
+                    <td></td>
+                    <td>{{user.username}}</td>
+                </tr>
+                {% empty %}
+                <tr class="row2">
+                    <td></td>
+                    <td>No collaborators</td>
+                </tr>
+                {% endfor %}
+            </table>
+            <button type="button" onclick="window.location.href='/booking/detail/{{booking.id}}'">You can find more detailed information here</button>
         </div>
     </body>
 </html>