def get_maas_user_agent(): from maasserver.models import Config user_agent = get_maas_version_user_agent() uuid = Config.objects.get_config('uuid') if uuid: user_agent = "%s/%s" % (user_agent, uuid) return user_agent
def test__returns_unknown_if_version_is_empty_and_not_bzr_branch(self): mock_version = self.patch(version, "get_version_from_apt") mock_version.return_value = "" mock_branch_version = self.patch(version, "get_maas_branch_version") mock_branch_version.return_value = None self.assertEqual("maas/%s/unknown" % old_version, version.get_maas_version_user_agent())
def test_returns_package_version(self): mock_apt = self.patch(version, "get_version_from_apt") mock_apt.return_value = "1.8.0~alpha4+bzr356-0ubuntu1" self.assertEqual( "maas/1.8.0~alpha4/bzr356-0ubuntu1", version.get_maas_version_user_agent(), )
def test__returns_from_source_and_revno_from_branch(self): mock_version = self.patch(version, "get_version_from_apt") mock_version.return_value = "" revno = random.randint(1, 5000) mock_branch_version = self.patch(version, "get_maas_branch_version") mock_branch_version.return_value = revno self.assertEqual("maas/%s from source/bzr%d" % (old_version, revno), version.get_maas_version_user_agent())
def test_get_maas_user_agent_with_uuid(self): region = factory.make_RegionController() self.useFixture(MAASIDFixture(region.system_id)) RegionController.objects.get_or_create_uuid() user_agent = get_maas_user_agent() composed_user_agent = "%s/%s" % (get_maas_version_user_agent(), Config.objects.get_config('uuid')) self.assertEquals(user_agent, composed_user_agent)
def test__returns_from_source_and_hashfrom_repo(self): mock_version = self.patch(version, "get_version_from_apt") mock_version.return_value = "" mock_repo_hash = self.patch(version, "get_maas_repo_hash") mock_repo_hash.return_value = 'deadbeef' self.assertEqual( "maas/%s from source/git+%s" % (old_version, 'deadbeef'), version.get_maas_version_user_agent())
def test__passes_user_agent_with_maas_version(self): mock_download = self.patch(bootsources, 'download_all_image_descriptions') factory.make_BootSource(keyring_data=b'1234') cache_boot_sources() self.assertThat( mock_download, MockCalledOnceWith(ANY, user_agent=get_maas_version_user_agent()))
def fetch(self, params): """Fetch the releases and the arches from the provided source.""" # Must be administrator. assert self.user.is_superuser, "Permission denied." # Build a source, but its not saved into the database. boot_source = self.get_bootsource(params, from_db=False) try: # Validate the boot source fields without committing it. boot_source.clean_fields() except ValidationError as error: raise HandlerValidationError(error) source = boot_source.to_dict_without_selections() # FIXME: This modifies the environment of the entire process, which is # Not Cool. We should integrate with simplestreams in a more # Pythonic manner. set_simplestreams_env() with tempdir("keyrings") as keyrings_path: [source] = write_all_keyrings(keyrings_path, [source]) try: descriptions = download_all_image_descriptions( [source], user_agent=get_maas_version_user_agent()) except Exception as error: raise HandlerError(str(error)) items = list(descriptions.items()) err_msg = "Mirror provides no Ubuntu images." if len(items) == 0: raise HandlerError(err_msg) releases = {} arches = {} for image_spec, product_info in items: # Only care about Ubuntu images. if image_spec.os != 'ubuntu': continue releases[image_spec.release] = { 'name': image_spec.release, 'title': product_info.get( 'release_title', format_ubuntu_distro_series(image_spec.release)), 'checked': False, 'deleted': False, } arches[image_spec.arch] = { 'name': image_spec.arch, 'title': image_spec.arch, 'checked': False, 'deleted': False, } if len(releases) == 0 or len(arches) == 0: raise HandlerError(err_msg) return json.dumps({ 'releases': list(releases.values()), 'arches': list(arches.values()), })
def test_returns_unknown_if_version_is_empty_and_not_git_repo(self): mock_version = self.patch(version, "get_version_from_apt") mock_version.return_value = "" mock_repo_hash = self.patch(version, "get_maas_repo_hash") mock_repo_hash.return_value = None self.assertEqual( "maas/%s/unknown" % old_version, version.get_maas_version_user_agent(), )
def cache_boot_sources(): """Cache all image information in boot sources. Called from *outside* of a transaction this will: 1. Retrieve information about all boot sources from the database. The transaction is committed before proceeding. 2. The boot sources are consulted (i.e. there's network IO now) and image descriptions downloaded. 3. Update the boot source cache with the fetched information. If the boot source has been modified or deleted during #2 then the results are discarded. This approach does not require an exclusive lock. """ # Nomenclature herein: `bootsource` is an ORM record for BootSource; # `source` is one of those converted to a dict. The former ought not to be # used outside of a transactional context. @transactional def get_sources(): return list( bootsource.to_dict_without_selections() for bootsource in BootSource.objects.all() # TODO: Only where there are no corresponding BootSourceCache # records or the BootSource's updated timestamp is later than any # of the BootSourceCache records' timestamps. ) @transactional def update_cache(source, descriptions): try: bootsource = BootSource.objects.get(url=source["url"]) except BootSource.DoesNotExist: # The record was deleted while we were fetching the description. maaslog.debug( "Image descriptions at %s are no longer needed; discarding.", source["url"]) else: if bootsource.compare_dict_without_selections(source): # Only delete from the cache once we have the descriptions. BootSourceCache.objects.filter(boot_source=bootsource).delete() if not descriptions.is_empty(): for spec, item in descriptions.mapping.items(): title = get_product_title(item) if title is None: extra = {} else: extra = {'title': title} BootSourceCache.objects.create( boot_source=bootsource, os=spec.os, arch=spec.arch, subarch=spec.subarch, kflavor=spec.kflavor, release=spec.release, label=spec.label, release_codename=item.get('release_codename'), release_title=item.get('release_title'), support_eol=item.get('support_eol'), bootloader_type=item.get('bootloader-type'), extra=extra, ) maaslog.debug( "Image descriptions for %s have been updated.", source["url"]) else: maaslog.debug( "Image descriptions for %s are outdated; discarding.", source["url"]) @transactional def check_commissioning_series_selected(): commissioning_osystem = Config.objects.get_config( name='commissioning_osystem') commissioning_series = Config.objects.get_config( name='commissioning_distro_series') qs = BootSourceSelection.objects.filter( os=commissioning_osystem, release=commissioning_series) if not qs.exists(): if not Notification.objects.filter( ident='commissioning_series_unselected').exists(): Notification.objects.create_error_for_users( '%s %s is configured as the commissioning release but it ' 'is not selected for download!' % ( commissioning_osystem, commissioning_series), ident='commissioning_series_unselected') qs = BootSourceCache.objects.filter( os=commissioning_osystem, release=commissioning_series) if not qs.exists(): if not Notification.objects.filter( ident='commissioning_series_unavailable').exists(): Notification.objects.create_error_for_users( '%s %s is configured as the commissioning release but it ' 'is unavailable in the configured streams!' % ( commissioning_osystem, commissioning_series), ident='commissioning_series_unavailable') # FIXME: This modifies the environment of the entire process, which is Not # Cool. We should integrate with simplestreams in a more Pythonic manner. yield deferToDatabase(set_simplestreams_env) errors = [] sources = yield deferToDatabase(get_sources) for source in sources: with tempdir("keyrings") as keyrings_path: [source] = write_all_keyrings(keyrings_path, [source]) try: descriptions = download_all_image_descriptions( [source], user_agent=get_maas_version_user_agent()) except (IOError, ConnectionError) as error: errors.append( "Failed to import images from boot source " "%s: %s" % (source["url"], error)) else: yield deferToDatabase(update_cache, source, descriptions) yield deferToDatabase(check_commissioning_series_selected) maaslog.info("Updated boot sources cache.") component = COMPONENT.REGION_IMAGE_IMPORT if len(errors) > 0: yield deferToDatabase( register_persistent_error, component, "<br>".join(map(html.escape, errors))) else: yield deferToDatabase( discard_persistent_error, component)