Example #1
0
 def update(self, request, name):
     """Update a commissioning script."""
     script = get_object_or_404(Script, name=name)
     content = Bin(get_content_parameter(request))
     data = request.data.copy()
     data['script'] = content
     data['script_type'] = SCRIPT_TYPE.COMMISSIONING
     form = ScriptForm(instance=script, data=data)
     if form.is_valid():
         form.save(request)
         return rc.ALL_OK
     else:
         return MAASAPIValidationError(form.errors)
Example #2
0
def load_builtin_scripts():
    for script in BUILTIN_SCRIPTS:
        if script.inject_file:
            with open(script.inject_path, 'r') as f:
                script.substitutes['inject_file'] = f.read()
        script_content = tempita.Template.from_filename(
            script.script_path, encoding='utf-8')
        script_content = script_content.substitute({
            'name': script.name,
            **script.substitutes,
            })
        try:
            script_in_db = Script.objects.get(name=script.name)
        except Script.DoesNotExist:
            form = ScriptForm(data={
                'script': script_content,
                'comment': "Created by maas-%s" % get_maas_version(),
            })
            # Form validation should never fail as these are the scripts which
            # ship with MAAS. If they ever do this will be cause by unit tests.
            if not form.is_valid():
                raise Exception("%s: %s" % (script.name, form.errors))
            script_in_db = form.save(commit=False)
            script_in_db.default = True
            script_in_db.save()
        else:
            if script_in_db.script.data != script_content:
                # Don't add back old versions of a script. This prevents two
                # connected regions with different versions of a script from
                # fighting with eachother.
                no_update = False
                for vtf in script_in_db.script.previous_versions():
                    if vtf.data == script_content:
                        # Don't update anything if we detect we have an old
                        # version of the builtin scripts
                        no_update = True
                        break
                if no_update:
                    continue
                form = ScriptForm(instance=script_in_db, data={
                    'script': script_content,
                    'comment': "Updated by maas-%s" % get_maas_version(),
                    }, edit_default=True)
                # Form validation should never fail as these are the scripts
                # which ship with MAAS. If they ever do this will be cause by
                # unit tests.
                if not form.is_valid():
                    raise Exception("%s: %s" % (script.name, form.errors))
                script_in_db = form.save(commit=False)
                script_in_db.default = True
                script_in_db.save()
Example #3
0
 def test_packages_validate(self):
     apt_pkgs = [factory.make_name('apt_pkg') for _ in range(3)]
     snap_pkg = {
         'name': factory.make_name('snap_pkg'),
         'channel': random.choice(['stable', 'edge', 'beta', 'candidate']),
         'mode': random.choice(['classic', 'dev', 'jail']),
     }
     url = factory.make_url()
     form = ScriptForm(
         data={
             'script':
             factory.make_script_content({
                 'name': factory.make_name('name'),
                 'packages': {
                     'apt': apt_pkgs,
                     'snap': snap_pkg,
                     'url': url
                 },
             })
         })
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertDictEqual(
         {
             'apt': apt_pkgs,
             'snap': [snap_pkg],
             'url': [url]
         }, script.packages)
Example #4
0
    def test__update_script_doesnt_effect_other_fields(self):
        script = factory.make_Script()
        script_content = factory.make_script_content()
        name = script.name
        title = script.title
        description = script.description
        tags = script.tags
        script_type = script.script_type
        hardware_type = script.hardware_type
        parallel = script.parallel
        results = script.results
        parameters = script.parameters
        packages = script.packages
        timeout = script.timeout
        destructive = script.destructive

        form = ScriptForm(data={'script': script_content}, instance=script)
        self.assertTrue(form.is_valid(), form.errors)
        script = form.save()

        self.assertEquals(name, script.name)
        self.assertEquals(title, script.title)
        self.assertEquals(description, script.description)
        self.assertEquals(tags, script.tags)
        self.assertEquals(script_type, script.script_type)
        self.assertEquals(hardware_type, script.hardware_type)
        self.assertDictEqual(packages, script.packages)
        self.assertEquals(parallel, script.parallel)
        self.assertDictEqual(results, script.results)
        self.assertDictEqual(parameters, script.parameters)
        self.assertEquals(timeout, script.timeout)
        self.assertEquals(destructive, script.destructive)
        self.assertFalse(script.default)
        self.assertEquals(script_content, script.script.data)
Example #5
0
 def test__converts_single_package_to_list(self):
     apt_pkg = factory.make_name("apt_pkg")
     snap_pkg = factory.make_name("snap_pkg")
     url = factory.make_url()
     form = ScriptForm(
         data={
             "script":
             factory.make_script_content({
                 "name": factory.make_name("name"),
                 "packages": {
                     "apt": apt_pkg,
                     "snap": snap_pkg,
                     "url": url,
                 },
             })
         })
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertDictEqual(
         {
             "apt": [apt_pkg],
             "snap": [snap_pkg],
             "url": [url]
         },
         script.packages,
     )
Example #6
0
    def test__create_with_default_values(self):
        name = factory.make_name("name")
        script_content = factory.make_script_content()

        form = ScriptForm(data={"name": name, "script": script_content})
        self.assertTrue(form.is_valid(), form.errors)
        script = form.save()

        self.assertEquals(name, script.name)
        self.assertEquals("", script.title)
        self.assertEquals("", script.description)
        self.assertEquals(1, len(script.tags))
        self.assertEquals(SCRIPT_TYPE.TESTING, script.script_type)
        self.assertEquals(HARDWARE_TYPE.NODE, script.hardware_type)
        self.assertEquals(SCRIPT_PARALLEL.DISABLED, script.parallel)
        self.assertDictEqual({}, script.packages)
        self.assertDictEqual({}, script.results)
        self.assertDictEqual({}, script.parameters)
        self.assertEquals(timedelta(0), script.timeout)
        self.assertFalse(script.destructive)
        self.assertEquals(script_content, script.script.data)
        self.assertFalse(script.default)
        self.assertItemsEqual([], script.for_hardware)
        self.assertFalse(script.may_reboot)
        self.assertFalse(script.recommission)
Example #7
0
 def test_packages_validate(self):
     apt_pkgs = [factory.make_name("apt_pkg") for _ in range(3)]
     snap_pkg = {
         "name": factory.make_name("snap_pkg"),
         "channel": random.choice(["stable", "edge", "beta", "candidate"]),
         "mode": random.choice(["classic", "dev", "jail"]),
     }
     url = factory.make_url()
     form = ScriptForm(
         data={
             "script":
             factory.make_script_content({
                 "name": factory.make_name("name"),
                 "packages": {
                     "apt": apt_pkgs,
                     "snap": snap_pkg,
                     "url": url,
                 },
             })
         })
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertDictEqual(
         {
             "apt": apt_pkgs,
             "snap": [snap_pkg],
             "url": [url]
         },
         script.packages,
     )
Example #8
0
 def test__loads_yaml_embedded_attributes(self):
     embedded_yaml = {
         'name': factory.make_name('name'),
         'title': factory.make_name('title'),
         'description': factory.make_name('description'),
         'tags': [factory.make_name('tag') for _ in range(3)],
         'script_type': factory.pick_choice(SCRIPT_TYPE_CHOICES),
         'hardware_type': factory.pick_choice(HARDWARE_TYPE_CHOICES),
         'parallel': factory.pick_choice(SCRIPT_PARALLEL_CHOICES),
         'results': ['write_speed'],
         'parameters': [{'type': 'storage'}, {'type': 'runtime'}],
         'packages': {'apt': [factory.make_name('package')]},
         'timeout': random.randint(0, 1000),
         'destructive': factory.pick_bool(),
     }
     script_content = factory.make_script_content(embedded_yaml)
     form = ScriptForm(data={'script': script_content})
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertEquals(embedded_yaml['name'], script.name)
     self.assertEquals(embedded_yaml['title'], script.title)
     self.assertEquals(embedded_yaml['description'], script.description)
     self.assertThat(script.tags, ContainsAll(embedded_yaml['tags']))
     self.assertEquals(embedded_yaml['script_type'], script.script_type)
     self.assertEquals(embedded_yaml['hardware_type'], script.hardware_type)
     self.assertEquals(embedded_yaml['parallel'], script.parallel)
     self.assertItemsEqual(embedded_yaml['results'], script.results)
     self.assertItemsEqual(embedded_yaml['parameters'], script.parameters)
     self.assertDictEqual(embedded_yaml['packages'], script.packages)
     self.assertEquals(
         timedelta(0, embedded_yaml['timeout']), script.timeout)
     self.assertEquals(embedded_yaml['destructive'], script.destructive)
     self.assertFalse(script.default)
     self.assertEquals(script_content, script.script.data)
Example #9
0
 def test__update_setting_default_has_no_effect(self):
     script = factory.make_Script(default=True)
     form = ScriptForm(data={
         'default': False,
     }, instance=script)
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertTrue(script.default)
Example #10
0
 def test__only_loads_when_script_updated(self):
     script = factory.make_Script(script=factory.make_script_content(
         {"name": factory.make_name("name")}))
     name = factory.make_name("name")
     form = ScriptForm(instance=script, data={"name": name})
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertEquals(name, script.name)
Example #11
0
 def test__allows_list_of_results(self):
     results = [factory.make_name('result') for _ in range(3)]
     form = ScriptForm(data={'script': factory.make_script_content({
         'name': factory.make_name('name'),
         'results': results,
         })})
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertItemsEqual(results, script.results)
Example #12
0
 def test__ignores_other_version_yaml(self):
     script = factory.make_Script()
     name = script.name
     form = ScriptForm(instance=script, data={
         'script': factory.make_script_content(
             {'name': factory.make_name('name')}, version='9.0')})
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertEquals(name, script.name)
Example #13
0
 def test__create_setting_default_has_no_effect(self):
     form = ScriptForm(data={
         'name': factory.make_name('name'),
         'script': factory.make_script_content(),
         'default': True,
     })
     self.assertTrue(form.is_valid())
     script = form.save()
     self.assertFalse(script.default)
Example #14
0
 def tests_yaml_tags_can_be_list_of_strings(self):
     tags = [factory.make_name('tag') for _ in range(3)]
     form = ScriptForm(data={'script': factory.make_script_content({
         'name': factory.make_name('name'),
         'tags': tags,
         })})
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertThat(script.tags, ContainsAll(tags))
Example #15
0
 def test_create_setting_default_has_no_effect(self):
     form = ScriptForm(
         data={
             "name": factory.make_name("name"),
             "script": factory.make_script_content(),
             "default": True,
         })
     self.assertTrue(form.is_valid())
     script = form.save()
     self.assertFalse(script.default)
Example #16
0
 def test__type_aliased_to_script_type(self):
     script_type = factory.pick_choice(SCRIPT_TYPE_CHOICES)
     form = ScriptForm(data={
         'name': factory.make_name('name'),
         'script': factory.make_script_content(),
         'type': script_type,
     })
     self.assertTrue(form.is_valid())
     script = form.save()
     self.assertEquals(script_type, script.script_type)
Example #17
0
    def update(self, request, name):
        """Update a commissioning script.

        :param name: The name of the script.
        :type name: unicode

        :param title: The title of the script.
        :type title: unicode

        :param description: A description of what the script does.
        :type description: unicode

        :param tags: A comma seperated list of tags for this script.
        :type tags: unicode

        :param type: The type defines when the script should be used. Can be
            testing or commissioning, defaults to testing.
        :type script_type: unicode

        :param timeout: How long the script is allowed to run before failing.
            0 gives unlimited time, defaults to 0.
        :type timeout: unicode

        :param destructive: Whether or not the script overwrites data on any
            drive on the running system. Destructive scripts can not be run on
            deployed systems. Defaults to false.
        :type destructive: boolean

        :param script: The content of the script to be uploaded in binary form.
            note: this is not a normal parameter, but a file upload. Its
            filename is ignored; MAAS will know it by the name you pass to the
            request. Optionally you can ignore the name and script parameter in
            favor of uploading a single file as part of the request.

        :param comment: A comment about what this change does.
        :type comment: unicode
        """
        if name.isdigit():
            script = get_object_or_404(Script, id=int(name))
        else:
            script = get_object_or_404(Script, name=name)

        data = request.data.copy()
        if 'script' in request.FILES:
            data['script'] = request.FILES.get('script').read()
        elif len(request.FILES) == 1:
            for name, script_content in request.FILES.items():
                data['name'] = name
                data['script'] = script_content.read()

        form = ScriptForm(instance=script, data=data)
        if form.is_valid():
            return form.save()
        else:
            raise MAASAPIValidationError(form.errors)
Example #18
0
 def test_type_aliased_to_script_type(self):
     script_type = factory.pick_choice(SCRIPT_TYPE_CHOICES)
     form = ScriptForm(
         data={
             "name": factory.make_name("name"),
             "script": factory.make_script_content(),
             "type": script_type,
         })
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertEquals(script_type, script.script_type)
Example #19
0
    def test__can_use_parallel(self):
        script_parallel = factory.pick_choice(SCRIPT_PARALLEL_CHOICES)
        form = ScriptForm(data={
            'name': factory.make_name('name'),
            'script': factory.make_script_content(),
            'parallel': script_parallel,
        })
        self.assertTrue(form.is_valid(), form.errors)
        script = form.save()

        self.assertEquals(script_parallel, script.parallel)
Example #20
0
    def test__yaml_doesnt_update_timeout(self):
        script = factory.make_Script()
        orig_timeout = script.timeout

        form = ScriptForm(
            data={'script': factory.make_script_content({
                'timeout': random.randint(0, 1000),
            })}, instance=script)
        self.assertTrue(form.is_valid(), form.errors)
        script = form.save()
        self.assertEquals(orig_timeout, script.timeout)
Example #21
0
    def test__yaml_doesnt_update_tags(self):
        script = factory.make_Script()
        orig_tags = script.tags

        form = ScriptForm(
            data={'script': factory.make_script_content({
                'tags': [factory.make_name('tag') for _ in range(3)],
            })}, instance=script)
        self.assertTrue(form.is_valid(), form.errors)
        script = form.save()
        self.assertItemsEqual(orig_tags, script.tags)
Example #22
0
def load_builtin_scripts():
    for script in BUILTIN_SCRIPTS:
        if script.inject_file:
            with open(script.inject_path, "r") as f:
                script.substitutes["inject_file"] = f.read()
        script_content = tempita.Template.from_filename(script.script_path,
                                                        encoding="utf-8")
        script_content = script_content.substitute({
            "name": script.name,
            **script.substitutes
        })
        form = None
        try:
            script_in_db = Script.objects.get(name=script.name)
        except Script.DoesNotExist:
            form = ScriptForm(
                data={
                    "script": script_content,
                    "comment": f"Created by maas-{get_maas_version()}",
                })
        else:
            if script_in_db.script.data != script_content:
                # Don't add back old versions of a script. This prevents two
                # connected regions with different versions of a script from
                # fighting with eachother.
                for vtf in script_in_db.script.previous_versions():
                    if vtf.data == script_content:
                        # Don't update anything if we detect we have an old
                        # version of the builtin scripts
                        break
                else:
                    form = ScriptForm(
                        instance=script_in_db,
                        data={
                            "script": script_content,
                            "comment": f"Updated by maas-{get_maas_version()}",
                        },
                        edit_default=True,
                    )

        if form is not None:
            # Form validation should never fail as these are the scripts
            # which ship with MAAS. If they ever do this will be cause by
            # unit tests.
            assert (
                form.is_valid()
            ), f"Builtin script {script.name} caused these errors: {form.errors}"
            script_in_db = form.save(commit=False)
        if NODE_INFO_SCRIPTS.get(script.name, {}).get("run_on_controller"):
            script_in_db.add_tag("deploy-info")
        else:
            script_in_db.remove_tag("deploy-info")
        script_in_db.default = True
        script_in_db.save()
Example #23
0
    def test__can_use_hardware_type_name(self):
        hardware_type = factory.pick_choice(HARDWARE_TYPE_CHOICES)
        form = ScriptForm(data={
            'name': factory.make_name('name'),
            'script': factory.make_script_content(),
            'hardware_type': hardware_type,
        })
        self.assertTrue(form.is_valid(), form.errors)
        script = form.save()

        self.assertEquals(hardware_type, script.hardware_type)
Example #24
0
    def test__update(self):
        script = factory.make_Script()
        name = factory.make_name('name')
        title = factory.make_name('title')
        description = factory.make_name('description')
        tags = [factory.make_name('tag') for _ in range(3)]
        script_type = factory.pick_choice(SCRIPT_TYPE_CHOICES)
        hardware_type = factory.pick_choice(HARDWARE_TYPE_CHOICES)
        parallel = factory.pick_choice(SCRIPT_PARALLEL_CHOICES)
        packages = {'apt': [factory.make_name('package')]}
        timeout = random.randint(0, 1000)
        destructive = factory.pick_bool()
        script_content = factory.make_script_content()
        comment = factory.make_name('comment')
        orig_script_content = script.script.data

        form = ScriptForm(data={
            'name': name,
            'title': title,
            'description': description,
            'tags': ','.join(tags),
            'type': script_type,
            'hardware_type': hardware_type,
            'parallel': parallel,
            'packages': json.dumps(packages),
            'timeout': str(timeout),
            'destructive': destructive,
            'script': script_content,
            'comment': comment,
        },
                          instance=script)
        self.assertTrue(form.is_valid(), form.errors)
        script = form.save()

        self.assertEquals(name, script.name)
        self.assertEquals(title, script.title)
        self.assertEquals(description, script.description)
        self.assertThat(script.tags, ContainsAll(tags))
        self.assertEquals(script_type, script.script_type)
        self.assertEquals(hardware_type, script.hardware_type)
        self.assertEquals(parallel, script.parallel)
        self.assertDictEqual({}, script.results)
        self.assertDictEqual({}, script.parameters)
        self.assertDictEqual(packages, script.packages)
        self.assertEquals(timedelta(0, timeout), script.timeout)
        self.assertEquals(destructive, script.destructive)
        self.assertEquals(script_content, script.script.data)
        self.assertEquals(comment, script.script.comment)
        self.assertEquals(orig_script_content,
                          script.script.previous_version.data)
        self.assertEquals(None, script.script.previous_version.comment)
        self.assertFalse(script.default)
Example #25
0
    def create(self, request):
        """Create a new commissioning script.

        Each commissioning script is identified by a unique name.

        By convention the name should consist of a two-digit number, a dash,
        and a brief descriptive identifier consisting only of ASCII
        characters.  You don't need to follow this convention, but not doing
        so opens you up to risks w.r.t. encoding and ordering.  The name must
        not contain any whitespace, quotes, or apostrophes.

        A commissioning machine will run each of the scripts in lexicographical
        order.  There are no promises about how non-ASCII characters are
        sorted, or even how upper-case letters are sorted relative to
        lower-case letters.  So where ordering matters, use unique numbers.

        Scripts built into MAAS will have names starting with "00-maas" or
        "99-maas" to ensure that they run first or last, respectively.

        Usually a commissioning script will be just that, a script.  Ideally a
        script should be ASCII text to avoid any confusion over encoding.  But
        in some cases a commissioning script might consist of a binary tool
        provided by a hardware vendor.  Either way, the script gets passed to
        the commissioning machine in the exact form in which it was uploaded.

        :param name: Unique identifying name for the script.  Names should
            follow the pattern of "25-burn-in-hard-disk" (all ASCII, and with
            numbers greater than zero, and generally no "weird" characters).
        :param content: A script file, to be uploaded in binary form.  Note:
            this is not a normal parameter, but a file upload.  Its filename
            is ignored; MAAS will know it by the name you pass to the request.
        """
        content = Bin(get_content_parameter(request))
        data = request.data.copy()
        data["script"] = content
        data["script_type"] = SCRIPT_TYPE.COMMISSIONING
        form = ScriptForm(data=data)
        if form.is_valid():
            script = form.save(request)
            return {
                "name":
                script.name,
                "content":
                b64encode(script.script.data.encode()),
                "deprecated":
                ("The commissioning-scripts endpoint is deprecated. "
                 "Please use the node-scripts endpoint."),
                "resource_uri":
                reverse("commissioning_script_handler", args=[script.name]),
            }
        else:
            raise MAASAPIValidationError(form.errors)
Example #26
0
 def test__allows_list_of_results(self):
     results = [factory.make_name("result") for _ in range(3)]
     form = ScriptForm(
         data={
             "script":
             factory.make_script_content({
                 "name": factory.make_name("name"),
                 "results": results
             })
         })
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertItemsEqual(results, script.results)
Example #27
0
 def tests_yaml_tags_can_be_list_of_strings(self):
     tags = [factory.make_name("tag") for _ in range(3)]
     form = ScriptForm(
         data={
             "script":
             factory.make_script_content({
                 "name": factory.make_name("name"),
                 "tags": tags
             })
         })
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertThat(script.tags, ContainsAll(tags))
Example #28
0
 def test__converts_single_package_to_list(self):
     apt_pkg = factory.make_name('apt_pkg')
     snap_pkg = factory.make_name('snap_pkg')
     url = factory.make_url()
     form = ScriptForm(data={'script': factory.make_script_content({
         'name': factory.make_name('name'),
         'packages': {'apt': apt_pkg, 'snap': snap_pkg, 'url': url},
         })})
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertDictEqual(
         {'apt': [apt_pkg], 'snap': [snap_pkg], 'url': [url]},
         script.packages)
Example #29
0
 def test__allows_dictionary_of_results(self):
     results = {
         factory.make_name('result_key'): {
             'title': factory.make_name('result_title')
             for _ in range(3)}
         for _ in range(3)}
     form = ScriptForm(data={'script': factory.make_script_content({
         'name': factory.make_name('name'),
         'results': results,
         })})
     self.assertTrue(form.is_valid(), form.errors)
     script = form.save()
     self.assertDictEqual(results, script.results)
Example #30
0
    def test__update_allows_editing_tag_and_timeout_on_default_script(self):
        script = factory.make_Script(default=True, destructive=False)
        tags = [factory.make_name('tag') for _ in range(3)]
        timeout = random.randint(0, 1000)

        form = ScriptForm(data={
            'tags': ','.join(tags),
            'timeout': str(timeout),
            }, instance=script)
        self.assertTrue(form.is_valid())
        script = form.save()

        self.assertThat(script.tags, ContainsAll(tags))
        self.assertEquals(timedelta(0, timeout), script.timeout)