Beispiel #1
0
    def import_to_repo(self):
        logging.info("Trying to use %s extractor...", self.__class__.__name__)

        ### Mmap the flatpak file and create a GLib.Variant from it
        mapped_file = GLib.MappedFile.new(self._src_path, False)
        delta = GLib.Variant.new_from_bytes(
            GLib.VariantType(OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT),
            mapped_file.get_bytes(), False)

        ### Parse flatpak metadata
        # Use get_child_value instead of array index to avoid
        # slowdown (constructing the whole array?)
        checksum_variant = delta.get_child_value(3)
        OSTree.validate_structureof_csum_v(checksum_variant)

        metadata_variant = delta.get_child_value(0)
        logging.debug("Metadata keys: %s", metadata_variant.keys())

        commit = OSTree.checksum_from_bytes_v(checksum_variant)

        self._metadata = {}
        logging.debug("===== Start Metadata =====")
        for key in FlatpakImporter.METADATA_KEYS:
            try:
                self._metadata[key] = metadata_variant[key]
            except KeyError:
                self._metadata[key] = ''

            logging.debug(" %s: %s", key, self._metadata[key])
        logging.debug("===== End Metadata =====")

        self._apply_commit_to_repo(commit, self._metadata['ref'])
Beispiel #2
0
 def validate_ostree_file(csum):
     _, inputfile, file_info, xattrs = repo.load_file(csum)
     # images are imported from layer tarballs, without any xattr.  Don't use xattr to compute
     # the OSTree object checksum.
     xattrs = GLib.Variant("a(ayay)", [])
     _, checksum_v = OSTree.checksum_file_from_input(file_info, xattrs, inputfile, OSTree.ObjectType.FILE)
     return OSTree.checksum_from_bytes(checksum_v)
Beispiel #3
0
    def _import_layers_into_ostree(repo, imagebranch, manifest, layers):
        repo.prepare_transaction()
        for layer, tar in layers.items():
            mtree = OSTree.MutableTree()
            repo.write_archive_to_mtree(Gio.File.new_for_path(tar), mtree, None, True)
            root = repo.write_mtree(mtree)[1]
            metav = GLib.Variant("a{sv}", {'docker.layer': GLib.Variant('s', layer)})
            csum = repo.write_commit(None, "", None, metav, root)[1]
            repo.transaction_set_ref(None, "%s%s" % (OSTREE_OCIIMAGE_PREFIX, layer), csum)

        # create a $OSTREE_OCIIMAGE_PREFIX$image-$tag branch
        if not isinstance(manifest, str):
            manifest = json.dumps(manifest)

        metadata = GLib.Variant("a{sv}", {'docker.manifest': GLib.Variant('s', manifest)})
        mtree = OSTree.MutableTree()
        file_info = Gio.FileInfo()
        file_info.set_attribute_uint32("unix::uid", 0)
        file_info.set_attribute_uint32("unix::gid", 0)
        file_info.set_attribute_uint32("unix::mode", 0o755 | stat.S_IFDIR)

        dirmeta = OSTree.create_directory_metadata(file_info, None)
        csum_dirmeta = repo.write_metadata(OSTree.ObjectType.DIR_META, None, dirmeta)[1]
        mtree.set_metadata_checksum(OSTree.checksum_from_bytes(csum_dirmeta))

        root = repo.write_mtree(mtree)[1]
        csum = repo.write_commit(None, "", None, metadata, root)[1]
        repo.transaction_set_ref(None, imagebranch, csum)

        repo.commit_transaction(None)
Beispiel #4
0
 def validate_ostree_file(csum):
     _, inputfile, file_info, xattrs = repo.load_file(csum)
     # images are imported from layer tarballs, without any xattr.  Don't use xattr to compute
     # the OSTree object checksum.
     xattrs = GLib.Variant("a(ayay)", [])
     _, checksum_v = OSTree.checksum_file_from_input(file_info, xattrs, inputfile, OSTree.ObjectType.FILE)
     return OSTree.checksum_from_bytes(checksum_v)
Beispiel #5
0
        def get_system_image(rev):
            commit_rev = repo.resolve_rev(rev, False)[1]
            commit = repo.load_commit(commit_rev)[1]

            tag = ":".join(rev.replace("ociimage/", "").rsplit('-', 1))
            timestamp = OSTree.commit_get_timestamp(commit)
            return {'Id' : commit_rev, 'RepoTags' : [tag], 'Names' : [], 'Created': timestamp }
Beispiel #6
0
    def create_disks(self):
        [res, rev] = self.repo.resolve_rev(self.ref, False)
        [res, commit] = self.repo.load_variant(OSTree.ObjectType.COMMIT, rev)

        commitdate = GLib.DateTime.new_from_unix_utc(
            OSTree.commit_get_timestamp(commit)).format("%c")
        print commitdate
        # XXX - Define this somewhere?
        imageoutputdir = os.path.join(self.outputdir, 'images')

        imagedir = os.path.join(imageoutputdir, rev[:8])
        if not os.path.exists(imagedir):
            os.makedirs(imagedir)

        imagestmpdir = os.path.join(self.workdir, 'images')
        os.mkdir(imagestmpdir)

        generated = []

        imgtargetinstaller = os.path.join(imagestmpdir, 'install',
                                          '%s-installer.iso' % self.os_name)
        self.create_installer_image(self.workdir, imgtargetinstaller)
        generated.append(imgtargetinstaller)

        for f in generated:
            destpath = os.path.join(imagedir, os.path.basename(f))
            print "Created: " + destpath
            shutil.move(f, destpath)
Beispiel #7
0
    def prune_ostree_images(self):
        repo = self._get_ostree_repo()
        if not repo:
            return
        refs = {}
        app_refs = []

        for i in repo.list_refs()[1]:
            if i.startswith(OSTREE_OCIIMAGE_PREFIX):
                if len(i) == len(OSTREE_OCIIMAGE_PREFIX) + 64:
                    refs[i] = False
                else:
                    app_refs.append(i)

        def visit(rev):
            manifest = self._image_manifest(repo,
                                            repo.resolve_rev(rev, True)[1])
            if not manifest:
                return
            for layer in SystemContainers.get_layers_from_manifest(
                    json.loads(manifest)):
                refs[OSTREE_OCIIMAGE_PREFIX +
                     layer.replace("sha256:", "")] = True

        for app in app_refs:
            visit(app)

        for k, v in refs.items():
            if not v:
                ref = OSTree.parse_refspec(k)
                util.write_out("Deleting %s" % k)
                repo.set_ref_immediate(ref[1], ref[2], None)
Beispiel #8
0
    def run(self):
        """Pull a remote and delete it.

        All pulls in our code follow the pattern pull + delete.

        :raise: PayloadInstallationError if the pull fails
        """
        # pull requires this for some reason
        mainctx = create_new_context()
        mainctx.push_thread_default()

        cancellable = None

        # Variable substitute the ref: https://pagure.io/atomic-wg/issue/299
        ref = RpmOstree.varsubst_basearch(self._data.ref)

        self.report_progress(
            _("Starting pull of {branch_name} from {source}").format(
                branch_name=ref, source=self._data.remote))

        progress = OSTree.AsyncProgress.new()
        progress.connect('changed', self._pull_progress_cb)

        pull_opts = {'refs': Variant('as', [ref])}
        # If we're doing a kickstart, we can at least use the content as a reference:
        # See <https://github.com/rhinstaller/anaconda/issues/1117>
        # The first path here is used by <https://pagure.io/fedora-lorax-templates>
        # and the second by <https://github.com/projectatomic/rpm-ostree-toolbox/>
        # FIXME extend tests to cover this part of code
        if OSTree.check_version(2017, 8):
            for path in ['/ostree/repo', '/install/ostree/repo']:
                if os.path.isdir(path + '/objects'):
                    pull_opts['localcache-repos'] = Variant('as', [path])
                    break

        sysroot_file = Gio.File.new_for_path(conf.target.physical_root)
        sysroot = OSTree.Sysroot.new(sysroot_file)
        sysroot.load(cancellable)
        repo = sysroot.get_repo(None)[1]
        # We don't support resuming from interrupted installs
        repo.set_disable_fsync(True)

        try:
            repo.pull_with_options(self._data.remote,
                                   Variant('a{sv}', pull_opts), progress,
                                   cancellable)
        except GError as e:
            raise PayloadInstallationError(
                "Failed to pull from repository: %s" % e) from e

        log.info("ostree pull: %s", progress.get_status() or "")
        self.report_progress(_("Preparing deployment of {}").format(ref))

        # Now that we have the data pulled, delete the remote for now. This will allow a remote
        # configuration defined in the tree (if any) to override what's in the kickstart.
        # Otherwise, we'll re-add it in post.  Ideally, ostree would support a pull without adding
        # a remote, but that would get quite complex.
        repo.remote_delete(self._data.remote, None)

        mainctx.pop_thread_default()
    def create_disks(self):
        [res,rev] = self.repo.resolve_rev(self.ref, False)
        [res,commit] = self.repo.load_variant(OSTree.ObjectType.COMMIT, rev)

        commitdate = GLib.DateTime.new_from_unix_utc(OSTree.commit_get_timestamp(commit)).format("%c")
        print commitdate
        # XXX - Define this somewhere?
        imageoutputdir=os.path.join(self.outputdir, 'images')

        imagedir = os.path.join(imageoutputdir, rev[:8])
        if not os.path.exists(imagedir):
            os.makedirs(imagedir)

        imagestmpdir = os.path.join(self.workdir, 'images')
        if not os.path.exists(imagestmpdir):
            os.mkdir(imagestmpdir)

        generated = []

        imgtargetcloud=os.path.join(imagestmpdir, self._name, '%s.qcow2' % self.os_name)
        self.create_cloud_image(self.workdir, imgtargetcloud, self._kickstart)
        generated.append(imgtargetcloud)

        for f in generated:
            destpath = os.path.join(imagedir, os.path.basename(f))
            print "Created: " + destpath
            shutil.move(f, destpath)
Beispiel #10
0
    def _inspect_system_branch(self, repo, imagebranch):
        commit_rev = repo.resolve_rev(imagebranch, False)[1]
        commit = repo.load_commit(commit_rev)[1]

        branch_id = imagebranch.replace(OSTREE_OCIIMAGE_PREFIX, "")
        tag = ":".join(branch_id.rsplit('-', 1))
        timestamp = OSTree.commit_get_timestamp(commit)
        labels = {}

        manifest = self._image_manifest(repo, commit_rev)
        if len(branch_id) == 64:
            image_id = branch_id
            tag = "<none>"
        else:
            image_id = commit_rev

        if manifest:
            manifest = json.loads(manifest)
            if 'Labels' in manifest:
                labels = manifest['Labels']

            if 'Digest' in manifest:
                image_id = manifest['Digest'].replace("sha256:", "")

        if self.user:
            image_type = "user"
        else:
            image_type = "system"

        return {'Id' : image_id, 'ImageId' : image_id, 'RepoTags' : [tag], 'Names' : [], 'Created': timestamp,
                'ImageType' : image_type, 'Labels' : labels, 'OSTree-rev' : commit_rev}
Beispiel #11
0
 def _checkout_layer(self, repo, rootfs_fd, rootfs, rev):
     # ostree 2016.8 has a glib introspection safe API for checkout, use it
     # when available.
     if hasattr(repo, "checkout_at"):
         options = OSTree.RepoCheckoutAtOptions() # pylint: disable=no-member
         options.overwrite_mode = OSTree.RepoCheckoutOverwriteMode.UNION_FILES
         options.process_whiteouts = True
         options.disable_fsync = True
         if self.user:
             options.mode = OSTree.RepoCheckoutMode.USER
         repo.checkout_at(options, rootfs_fd, rootfs, rev)
     else:
         if self.user:
             user = ["--user-mode"]
         else:
             user = []
         util.check_call(["ostree", "--repo=%s" % self.get_ostree_repo_location(),
                          "checkout",
                          "--union"] +
                         user +
                          ["--whiteouts",
                           "--fsync=no",
                           rev,
                           rootfs],
                         stdin=DEVNULL,
                         stdout=DEVNULL,
                         stderr=DEVNULL)
Beispiel #12
0
    def prune_ostree_images(self):
        repo = self._get_ostree_repo()
        if not repo:
            return
        refs = {}
        app_refs = []

        for i in repo.list_refs()[1]:
            if i.startswith(OSTREE_OCIIMAGE_PREFIX):
                if len(i) == len(OSTREE_OCIIMAGE_PREFIX) + 64:
                    refs[i] = False
                else:
                    app_refs.append(i)

        def visit(rev):
            commit = repo.resolve_rev(rev, False)[1]
            manifest = SystemContainers._get_commit_metadata(repo, commit, "docker.manifest")
            if not manifest:
                return
            for layer in SystemContainers._get_layers_from_manifest(json.loads(manifest)):
                refs[OSTREE_OCIIMAGE_PREFIX + layer.replace("sha256:", "")] = True

        for app in app_refs:
            visit(app)

        for k, v in refs.items():
            if not v:
                ref = OSTree.parse_refspec(k)
                self.write_out("Deleting %s" % k)
                repo.set_ref_immediate(ref[1], ref[2], None)
        return
Beispiel #13
0
 def _checkout_layer(self, repo, rootfs_fd, rootfs, rev):
     OSTREE_SAFE_GLIB_REPO_CHECKOUT_OPTIONS = False
     # There is an issue in the way the RepoCheckoutOptions is mapped by glib, as the C
     # struct is using bit fields that are not supported by the introspection.
     # Accessing .disable_fsync and .process_whiteouts thus results in a segfault in
     # libostree.  Re-enable this once it gets fixed.
     if OSTREE_SAFE_GLIB_REPO_CHECKOUT_OPTIONS:
         options = OSTree.RepoCheckoutOptions()  # pylint: disable=no-member
         options.overwrite_mode = OSTree.RepoCheckoutOverwriteMode.UNION_FILES
         options.process_whiteouts = True
         options.disable_fsync = True
         if self.user:
             options.mode = OSTree.RepoCheckoutMode.USER
         repo.checkout_tree_at(options, rootfs_fd, rootfs, rev)
     else:
         if self.user:
             user = ["--user-mode"]
         else:
             user = []
         util.check_call([
             "ostree",
             "--repo=%s" % self._get_ostree_repo_location(), "checkout",
             "--union"
         ] + user + ["--whiteouts", "--fsync=no", rev, rootfs],
                         stdin=DEVNULL,
                         stdout=DEVNULL,
                         stderr=DEVNULL)
Beispiel #14
0
    def create(self, outputdir, post=None):
        [res,rev] = self.repo.resolve_rev(self.ref, False)
        [res,commit] = self.repo.load_variant(OSTree.ObjectType.COMMIT, rev)

        commitdate = GLib.DateTime.new_from_unix_utc(OSTree.commit_get_timestamp(commit)).format("%c")
        print commitdate

        lorax_opts = []
        if self.local_overrides:
            lorax_opts.extend([ '-s', self.local_overrides ])
        if self.lorax_additional_repos:
            for repourl in self.lorax_additional_repos.split(','):
                lorax_opts.extend(['-s', repourl.strip()])
        http_proxy = os.environ.get('http_proxy')
        if http_proxy:
            lorax_opts.extend([ '--proxy', http_proxy ])

        template_src = self.pkgdatadir + '/lorax-embed-repo.tmpl'
        template_dest = self.workdir + '/lorax-embed-repo.tmpl'
        shutil.copy(template_src, template_dest)

        if post is not None:
            # Yeah, this is pretty awful.
            post_str = '%r' % ('%post --erroronfail\n' + open(post).read() + '\n%end\n', )
            with open(template_dest, 'a') as f:
                f.write('\nappend usr/share/anaconda/interactive-defaults.ks %s\n' % (post_str, ))

        lorax_workdir = os.path.join(self.workdir, 'lorax')
        os.makedirs(lorax_workdir)
        run_sync(['lorax', '--nomacboot',
                  '--add-template=%s' % template_dest,
                  '--add-template-var=ostree_osname=%s' % self.os_name,
                  '--add-template-var=ostree_repo=%s' % self.ostree_repo,
                  '--add-template-var=ostree_ref=%s' % self.ref,
                  '-p', self.os_pretty_name, '-v', self.release,
                  '-r', self.release, '-s', self.yum_baseurl,
                  '-e', 'systemd-container',
                  ] + lorax_opts + ['output'],
                 cwd=lorax_workdir)
        # We injected data into boot.iso, so it's now installer.iso
        lorax_output = lorax_workdir + '/output'
        lorax_images = lorax_output + '/images'
        os.rename(lorax_images + '/boot.iso', lorax_images + '/installer.iso')

        treeinfo = lorax_output + '/.treeinfo'
        treeinfo_tmp = treeinfo + '.tmp'
        with open(treeinfo) as treein:
            with open(treeinfo_tmp, 'w') as treeout:
                for line in treein:
                    if line.startswith('boot.iso'):
                        treeout.write(line.replace('boot.iso', 'installer.iso'))
                    else:
                        treeout.write(line)
        os.rename(treeinfo_tmp, treeinfo)

        for p in os.listdir(lorax_output):
            print "Generated: " + p
            shutil.move(os.path.join(lorax_output, p),
                        os.path.join(outputdir, p))
Beispiel #15
0
    def _import_layers_into_ostree(repo, imagebranch, manifest, layers):
        repo.prepare_transaction()
        for layer, tar in layers.items():
            mtree = OSTree.MutableTree()

            def filter_func(*args):
                info = args[2]
                if info.get_file_type() == Gio.FileType.DIRECTORY:
                    info.set_attribute_uint32(
                        "unix::mode",
                        info.get_attribute_uint32("unix::mode") | stat.S_IWUSR)
                return OSTree.RepoCommitFilterResult.ALLOW

            modifier = OSTree.RepoCommitModifier.new(0, filter_func, None)
            repo.write_archive_to_mtree(Gio.File.new_for_path(tar), mtree,
                                        modifier, True)
            root = repo.write_mtree(mtree)[1]
            metav = GLib.Variant("a{sv}",
                                 {'docker.layer': GLib.Variant('s', layer)})
            csum = repo.write_commit(None, "", None, metav, root)[1]
            repo.transaction_set_ref(None,
                                     "%s%s" % (OSTREE_OCIIMAGE_PREFIX, layer),
                                     csum)

        # create a $OSTREE_OCIIMAGE_PREFIX$image-$tag branch
        if not isinstance(manifest, str):
            manifest = json.dumps(manifest)

        metadata = GLib.Variant(
            "a{sv}", {'docker.manifest': GLib.Variant('s', manifest)})
        mtree = OSTree.MutableTree()
        file_info = Gio.FileInfo()
        file_info.set_attribute_uint32("unix::uid", 0)
        file_info.set_attribute_uint32("unix::gid", 0)
        file_info.set_attribute_uint32("unix::mode", 0o755 | stat.S_IFDIR)

        dirmeta = OSTree.create_directory_metadata(file_info, None)
        csum_dirmeta = repo.write_metadata(OSTree.ObjectType.DIR_META, None,
                                           dirmeta)[1]
        mtree.set_metadata_checksum(OSTree.checksum_from_bytes(csum_dirmeta))

        root = repo.write_mtree(mtree)[1]
        csum = repo.write_commit(None, "", None, metadata, root)[1]
        repo.transaction_set_ref(None, imagebranch, csum)

        repo.commit_transaction(None)
    def setUp(self):
        self.tmp = tempfile.TemporaryDirectory()

        self.repo = OSTree.Repo.new(Gio.File.new_for_path(self.tmp.name))
        self.repo.set_disable_fsync(True)
        self.repo.create(OSTree.RepoMode.BARE_USER_ONLY)

        self.mtree = OSTree.MutableTree()
Beispiel #17
0
 def delete_image(self, image):
     repo = self._get_ostree_repo()
     if not repo:
         return
     imagebranch, commit_rev = self._resolve_image(repo, image)
     if not commit_rev:
         return
     ref = OSTree.parse_refspec(imagebranch)
     repo.set_ref_immediate(ref[1], ref[2], None)
Beispiel #18
0
 def delete_image(self, image):
     repo = self._get_ostree_repo()
     if not repo:
         return
     imagebranch = SystemContainers._get_ostree_image_branch(image)
     commit_rev = repo.resolve_rev(imagebranch, True)
     if not commit_rev[1]:
         return
     ref = OSTree.parse_refspec(imagebranch)
     repo.set_ref_immediate(ref[1], ref[2], None)
Beispiel #19
0
 def delete_image(self, image):
     repo = self._get_ostree_repo()
     if not repo:
         return
     imagebranch = SystemContainers._get_ostree_image_branch(image)
     commit_rev = repo.resolve_rev(imagebranch, True)
     if not commit_rev[1]:
         return
     ref = OSTree.parse_refspec(imagebranch)
     repo.set_ref_immediate(ref[1], ref[2], None)
Beispiel #20
0
 def repo(self):
     if not os.path.exists(self.ostree_repo):
         #  Remove the cache, if the repo. is gone ... or rpm-ostree is very
         # confused.
         shutil.rmtree(self.rpmostree_cache_dir)
         os.makedirs(self.ostree_repo)
         subprocess.check_call(
             ['ostree', 'init', "--repo=" + self.ostree_repo])
     if self._repo is None:
         self._repo = OSTree.Repo(
             path=Gio.File.new_for_path(self.ostree_repo))
         self._repo.open(None)
     return self._repo
Beispiel #21
0
    def checkout_container(self, container_name, rev_number):
        """
        This method checks out a container into its corresponding folder, to a given
        commit revision.
        Before that, it stops the container using systemd, if found.

        Parameters:
        container_name (str): the name of the container
        rev_number (str): the commit revision
        """
        service = self.systemd.ListUnitsByNames([container_name + '.service'])
        if service[0][2] != 'not-found':
            self.logger.info("Stop the container {}".format(container_name))
            self.stop_unit(container_name)

        res = True
        rootfs_fd = None
        try:
            options = OSTree.RepoCheckoutAtOptions()
            options.overwrite_mode = OSTree.RepoCheckoutOverwriteMode.UNION_IDENTICAL
            options.process_whiteouts = True
            options.bareuseronly_dirs = True
            options.no_copy_fallback = True
            options.mode = OSTree.RepoCheckoutMode.USER

            self.logger.info("Getting rev from repo:{}".format(container_name + ':' + container_name))

            if rev_number is None:
                rev = self.repo_containers.resolve_rev(container_name + ':' + container_name, False)[1]
            else:
                rev = rev_number
            self.logger.info("Rev value:{}".format(rev))
            if os.path.isdir(PATH_APPS + '/' + container_name):
                shutil.rmtree(PATH_APPS + '/' + container_name)
            os.mkdir(PATH_APPS + '/' + container_name)
            self.logger.info("Create directory {}/{}".format(PATH_APPS, container_name))
            rootfs_fd = os.open(PATH_APPS + '/' + container_name, os.O_DIRECTORY)
            res = self.repo_containers.checkout_at(options, rootfs_fd, PATH_APPS + '/' + container_name, rev)
            open(PATH_APPS + '/' + container_name + '/' + VALIDATE_CHECKOUT, 'a').close()

        except GLib.Error as e:
            self.logger.error("Checking out {} failed ({})".format(container_name, str(e)))
            raise
        if rootfs_fd is not None:
            os.close(rootfs_fd)
        if not res:
            raise Exception("Checking out {} failed (returned False)")
Beispiel #22
0
def open_repository(repo_path):
    # Open repository
    repo_file = Gio.File.new_for_path(repo_path)
    repo = OSTree.Repo(path=repo_file)

    if os.path.exists(os.path.join(repo_path, 'config')):
        logging.info('Opening repo at %s', repo_path)
        repo.open()
    else:
        logging.info('Creating archive-z2 repo at %s', repo_path)
        try:
            os.makedirs(repo_path)
        except OSError as err:
            if err.errno != errno.EEXIST:
                raise
        repo.create(OSTree.RepoMode.ARCHIVE_Z2)

    return repo
Beispiel #23
0
    def repo(self):
        if not os.path.exists(self.ostree_repo):
            #  Remove the cache, if the repo. is gone ... or rpm-ostree is very
            # confused.
            if (self.rpmostree_cache_dir is not None and
                os.path.exists(self.rpmostree_cache_dir)):
                shutil.rmtree(self.rpmostree_cache_dir)
            os.makedirs(self.ostree_repo)
            subprocess.check_call(['ostree', 'init',
                                   "--repo="+self.ostree_repo, '--mode=archive-z2'])
        if self._repo is None:
            self._repo = OSTree.Repo(path=Gio.File.new_for_path(self.ostree_repo))

            try:
                self._repo.open(None)
            except:
                fail_msg("The repo location {0} has not been initialized.  Use 'ostree --repo={0} init --mode=archive-z2' to initialize and re-run rpm-ostree-toolbox".format(self.ostree_repo))

        return self._repo
Beispiel #24
0
    def validate_layer(self, layer):
        ret = []
        layer = layer.replace("sha256:", "")
        repo = self._get_ostree_repo()
        if not repo:
            return ret

        def validate_ostree_file(csum):
            _, inputfile, file_info, xattrs = repo.load_file(csum)
            # images are imported from layer tarballs, without any xattr.  Don't use xattr to compute
            # the OSTree object checksum.
            xattrs = GLib.Variant("a(ayay)", [])
            _, checksum_v = OSTree.checksum_file_from_input(file_info, xattrs, inputfile, OSTree.ObjectType.FILE)
            return OSTree.checksum_from_bytes(checksum_v)

        def traverse(it):
            def get_out_content_checksum(obj): return obj.out_content_checksum if hasattr(obj, 'out_content_checksum') else obj[1]
            def get_out_checksum(obj): return obj.out_checksum if hasattr(obj, 'out_checksum') else obj[1]
            while True:
                res = it.next()  # pylint: disable=next-method-called
                if res == OSTree.RepoCommitIterResult.DIR:
                    dir_checksum = get_out_content_checksum(it.get_dir())
                    dir_it = OSTree.RepoCommitTraverseIter()
                    dirtree = repo.load_variant(OSTree.ObjectType.DIR_TREE, dir_checksum)
                    dir_it.init_dirtree(repo, dirtree[1], OSTree.RepoCommitTraverseFlags.REPO_COMMIT_TRAVERSE_FLAG_NONE)
                    traverse(dir_it)
                elif res == OSTree.RepoCommitIterResult.FILE:
                    new_checksum = validate_ostree_file(get_out_checksum(it.get_file()))
                    if new_checksum != get_out_checksum(it.get_file()):
                        ret.append({"name" : it.get_file().out_name,
                                    "old-checksum" : it.get_file().out_checksum,
                                    "new-checksum" : new_checksum})
                elif res == OSTree.RepoCommitIterResult.ERROR:
                    raise ValueError("Internal error while validating the layer")
                elif res == OSTree.RepoCommitIterResult.END:
                    break

        current_rev = repo.resolve_rev("%s%s" % (OSTREE_OCIIMAGE_PREFIX, layer), False)[1]

        it = OSTree.RepoCommitTraverseIter()
        it.init_commit(repo, repo.load_commit(current_rev)[1], OSTree.RepoCommitTraverseFlags.REPO_COMMIT_TRAVERSE_FLAG_NONE)
        traverse(it)
        return ret
    def create_disks(self, outputdir):
        [res,rev] = self.repo.resolve_rev(self.ref, False)
        [res,commit] = self.repo.load_variant(OSTree.ObjectType.COMMIT, rev)

        commitdate = GLib.DateTime.new_from_unix_utc(OSTree.commit_get_timestamp(commit)).format("%c")
        print commitdate

        imagestmpdir = os.path.join(self.workdir, 'images')
        os.mkdir(imagestmpdir)

        generated = []

        imgtargetinstaller=os.path.join(imagestmpdir, 'install', '%s-installer.iso' % self.os_name)
        self.create_installer_image(self.workdir, imgtargetinstaller)
        generated.append(imgtargetinstaller)

        for f in generated:
            destpath = os.path.join(outputdir, os.path.basename(f))
            print "Created: " + destpath
            shutil.move(f, destpath)
def _look_up_ostree_ref_for_app_id(repo_path, app_id, remote_filter=None):
    ostree_repo = OSTree.Repo.new(Gio.File.new_for_path(repo_path))
    ostree_repo.open()

    target_ref = None
    _, refs = ostree_repo.list_refs()

    logging.info("Searching for {} ref in {}".format(app_id, str(refs)))
    for refspec in refs.keys():
        _, remote, ref = OSTree.parse_refspec(refspec)

        if remote_filter and remote != remote_filter:
            logging.info("{} != {}, skipping...".format(remote, remote_filter))
            continue

        if str.startswith(ref, 'app/{}/'.format(app_id)):
            target_ref = ref
            break

    return target_ref
Beispiel #27
0
 def traverse(it):
     def get_out_content_checksum(obj): return obj.out_content_checksum if hasattr(obj, 'out_content_checksum') else obj[1]
     def get_out_checksum(obj): return obj.out_checksum if hasattr(obj, 'out_checksum') else obj[1]
     while True:
         res = it.next()  # pylint: disable=next-method-called
         if res == OSTree.RepoCommitIterResult.DIR:
             dir_checksum = get_out_content_checksum(it.get_dir())
             dir_it = OSTree.RepoCommitTraverseIter()
             dirtree = repo.load_variant(OSTree.ObjectType.DIR_TREE, dir_checksum)
             dir_it.init_dirtree(repo, dirtree[1], OSTree.RepoCommitTraverseFlags.REPO_COMMIT_TRAVERSE_FLAG_NONE)
             traverse(dir_it)
         elif res == OSTree.RepoCommitIterResult.FILE:
             new_checksum = validate_ostree_file(get_out_checksum(it.get_file()))
             if new_checksum != get_out_checksum(it.get_file()):
                 ret.append({"name" : it.get_file().out_name,
                             "old-checksum" : it.get_file().out_checksum,
                             "new-checksum" : new_checksum})
         elif res == OSTree.RepoCommitIterResult.ERROR:
             raise ValueError("Internal error while validating the layer")
         elif res == OSTree.RepoCommitIterResult.END:
             break
Beispiel #28
0
    def checkout_container(self, container_name, rev_number):
        res = True
        rootfs_fd = None
        try:
            options = OSTree.RepoCheckoutAtOptions()
            options.overwrite_mode = OSTree.RepoCheckoutOverwriteMode.UNION_IDENTICAL
            options.process_whiteouts = True
            options.bareuseronly_dirs = True
            options.no_copy_fallback = True
            options.mode = OSTree.RepoCheckoutMode.USER

            self.logger.info(
                "Getting rev from repo:{}".format(container_name + ':' +
                                                  container_name))

            if rev_number == None:
                rev = self.repo_containers.resolve_rev(
                    container_name + ':' + container_name, False)[1]
            else:
                rev = rev_number
            self.logger.info("Rev value:{}".format(rev))
            if os.path.isdir(PATH_APPS + '/' + container_name):
                shutil.rmtree(PATH_APPS + '/' + container_name)
            os.mkdir(PATH_APPS + '/' + container_name)
            self.logger.info("Create directory {}/{}".format(
                PATH_APPS, container_name))
            rootfs_fd = os.open(PATH_APPS + '/' + container_name,
                                os.O_DIRECTORY)
            res = self.repo_containers.checkout_at(
                options, rootfs_fd, PATH_APPS + '/' + container_name, rev)
            open(PATH_APPS + '/' + container_name + '/' + VALIDATE_CHECKOUT,
                 'a').close()

        except GLib.Error as e:
            self.logger.error("Checking out {} failed ({})".format(
                container_name, str(e)))
            res = False
        if rootfs_fd != None:
            os.close(rootfs_fd)
        return res
Beispiel #29
0
    def _inspect_system_branch(self, repo, imagebranch):
        commit_rev = repo.resolve_rev(imagebranch, False)[1]
        commit = repo.load_commit(commit_rev)[1]

        branch_id = SystemContainers._decode_from_ostree_ref(imagebranch.replace(OSTREE_OCIIMAGE_PREFIX, ""))
        tag = ":".join(branch_id.rsplit(":", 1))
        timestamp = OSTree.commit_get_timestamp(commit)
        labels = {}

        manifest = self._image_manifest(repo, commit_rev)
        if len(branch_id) == 64:
            image_id = branch_id
            tag = "<none>"
        else:
            image_id = commit_rev

        if manifest:
            manifest = json.loads(manifest)
            if "Labels" in manifest:
                labels = manifest["Labels"]

            if "Digest" in manifest:
                image_id = manifest["Digest"].replace("sha256:", "")

        if self.user:
            image_type = "user"
        else:
            image_type = "system"

        return {
            "Id": image_id,
            "ImageId": image_id,
            "RepoTags": [tag],
            "Names": [],
            "Created": timestamp,
            "ImageType": image_type,
            "Labels": labels,
            "OSTree-rev": commit_rev,
        }
Beispiel #30
0
    def _inspect_system_branch(self, repo, imagebranch):
        commit_rev = repo.resolve_rev(imagebranch, False)[1]
        commit = repo.load_commit(commit_rev)[1]

        branch_id = imagebranch.replace(OSTREE_OCIIMAGE_PREFIX, "")
        tag = ":".join(branch_id.rsplit('-', 1))
        timestamp = OSTree.commit_get_timestamp(commit)
        labels = {}

        manifest = self._image_manifest(repo, commit_rev)
        if len(branch_id) == 64:
            image_id = branch_id
            tag = "<none>"
        else:
            image_id = commit_rev

        if manifest:
            manifest = json.loads(manifest)
            if 'Labels' in manifest:
                labels = manifest['Labels']

            if 'Digest' in manifest:
                image_id = manifest['Digest'].replace("sha256:", "")

        if self.user:
            image_type = "User"
        else:
            image_type = "System"

        return {
            'Id': image_id,
            'RepoTags': [tag],
            'Names': [],
            'Created': timestamp,
            'ImageType': image_type,
            'Labels': labels,
            'OSTree-rev': commit_rev
        }
Beispiel #31
0
    def prune_ostree_images(self):
        repo = self._get_ostree_repo()
        if not repo:
            return
        refs = {}
        app_refs = []

        for i in repo.list_refs()[1]:
            if i.startswith(OSTREE_OCIIMAGE_PREFIX):
                if len(i) == len(OSTREE_OCIIMAGE_PREFIX) + 64:
                    refs[i] = False
                else:
                    invalid_encoding = False
                    for c in i.replace(OSTREE_OCIIMAGE_PREFIX, ""):
                        if not str.isalnum(str(c)) and c not in ".-_":
                            invalid_encoding = True
                            break
                    if invalid_encoding:
                        refs[i] = False
                    else:
                        app_refs.append(i)

        def visit(rev):
            manifest = self._image_manifest(repo, repo.resolve_rev(rev, True)[1])
            if not manifest:
                return
            for layer in SystemContainers.get_layers_from_manifest(json.loads(manifest)):
                refs[OSTREE_OCIIMAGE_PREFIX + layer.replace("sha256:", "")] = True

        for app in app_refs:
            visit(app)

        for k, v in refs.items():
            if not v:
                ref = OSTree.parse_refspec(k)
                util.write_out("Deleting %s" % k)
                repo.set_ref_immediate(ref[1], ref[2], None)
Beispiel #32
0
    def _inspect_system_branch(self, repo, imagebranch):
        if imagebranch.startswith(OSTREE_OCIIMAGE_PREFIX):
            commit_rev = repo.resolve_rev(imagebranch, False)[1]
        else:
            _, commit_rev = self._resolve_image(repo, imagebranch)
            if commit_rev is None:
                raise ValueError("Image %s not found" % imagebranch)
        commit = repo.load_commit(commit_rev)[1]

        branch_id = SystemContainers._decode_from_ostree_ref(imagebranch.replace(OSTREE_OCIIMAGE_PREFIX, ""))
        tag = ":".join(branch_id.rsplit(':', 1))
        timestamp = OSTree.commit_get_timestamp(commit)
        labels = {}

        manifest = self._image_manifest(repo, commit_rev)
        if len(branch_id) == 64:
            image_id = branch_id
            tag = "<none>"
        else:
            image_id = commit_rev

        if manifest:
            manifest = json.loads(manifest)
            if 'Labels' in manifest:
                labels = manifest['Labels']

            if 'Digest' in manifest:
                image_id = manifest['Digest'].replace("sha256:", "")

        if self.user:
            image_type = "user"
        else:
            image_type = "system"

        return {'Id' : image_id, 'Version' : tag, 'ImageId' : image_id, 'RepoTags' : [tag], 'Names' : [],
                'Created': timestamp, 'ImageType' : image_type, 'Labels' : labels, 'OSTree-rev' : commit_rev}
Beispiel #33
0
    def _import_layers_into_ostree(repo, imagebranch, manifest, layers):
        repo.prepare_transaction()
        for layer, tar in layers.items():
            mtree = OSTree.MutableTree()

            def filter_func(*args):
                info = args[2]
                if info.get_file_type() == Gio.FileType.DIRECTORY:
                    info.set_attribute_uint32("unix::mode", info.get_attribute_uint32("unix::mode") | stat.S_IWUSR)
                return OSTree.RepoCommitFilterResult.ALLOW

            modifier = OSTree.RepoCommitModifier.new(0, filter_func, None)

            metav = GLib.Variant("a{sv}", {"docker.layer": GLib.Variant("s", layer)})

            imported = False
            try:
                repo.write_archive_to_mtree(Gio.File.new_for_path(tar), mtree, modifier, True)
                root = repo.write_mtree(mtree)[1]
                csum = repo.write_commit(None, "", None, metav, root)[1]
                imported = True
            except GLib.GError as e:  # pylint: disable=catching-non-exception
                # libarchive which is used internally by OSTree to import a tarball doesn't support correctly
                # files with xattrs.  If that happens, extract the tarball and import the directory.
                if e.domain != "g-io-error-quark":  # pylint: disable=no-member
                    raise e  # pylint: disable=raising-non-exception

            if not imported:
                try:
                    temp_dir = tempfile.mkdtemp()
                    with tarfile.open(tar, "r") as t:
                        t.extractall(temp_dir)
                    repo.write_directory_to_mtree(Gio.File.new_for_path(temp_dir), mtree, modifier)
                    root = repo.write_mtree(mtree)[1]
                    csum = repo.write_commit(None, "", None, metav, root)[1]
                finally:
                    shutil.rmtree(temp_dir)

            root = repo.write_mtree(mtree)[1]
            csum = repo.write_commit(None, "", None, metav, root)[1]
            repo.transaction_set_ref(None, "%s%s" % (OSTREE_OCIIMAGE_PREFIX, layer), csum)

        # create a $OSTREE_OCIIMAGE_PREFIX$image-$tag branch
        if not isinstance(manifest, str):
            manifest = json.dumps(manifest)

        metadata = GLib.Variant("a{sv}", {"docker.manifest": GLib.Variant("s", manifest)})
        mtree = OSTree.MutableTree()
        file_info = Gio.FileInfo()
        file_info.set_attribute_uint32("unix::uid", 0)
        file_info.set_attribute_uint32("unix::gid", 0)
        file_info.set_attribute_uint32("unix::mode", 0o755 | stat.S_IFDIR)

        dirmeta = OSTree.create_directory_metadata(file_info, None)
        csum_dirmeta = repo.write_metadata(OSTree.ObjectType.DIR_META, None, dirmeta)[1]
        mtree.set_metadata_checksum(OSTree.checksum_from_bytes(csum_dirmeta))

        root = repo.write_mtree(mtree)[1]
        csum = repo.write_commit(None, "", None, metadata, root)[1]
        repo.transaction_set_ref(None, imagebranch, csum)

        repo.commit_transaction(None)
    def create(self, outputdir, name, ksfile, tdl, imageouttypes):
        self._name = name
        self._tdl = tdl
        self._kickstart = ksfile
        imgfunc = ImageFunctions()

        [res, rev] = self.repo.resolve_rev(self.ref, False)
        [res, commit] = self.repo.load_variant(OSTree.ObjectType.COMMIT, rev)

        commitdate = GLib.DateTime.new_from_unix_utc(OSTree.commit_get_timestamp(commit)).format("%c")
        print commitdate

        port_file_path = self.workdir + '/repo-port'

        # Start trivial-httpd

        trivhttp = TrivialHTTP()
        trivhttp.start(self.ostree_repo)
        httpd_port = str(trivhttp.http_port)
        print "trivial httpd port=%s, pid=%s" % (httpd_port, trivhttp.http_pid)

        ks_basename = os.path.basename(ksfile)
        flattened_ks = os.path.join(self.workdir, ks_basename)

        # FIXME - eventually stop hardcoding this via some mapping
        if ks_basename.find('fedora') >= 0:
            kickstart_version = 'F21'
        else:
            kickstart_version = 'RHEL7'
        run_sync(['ksflatten', '--version', kickstart_version,
                  '-c', ksfile, '-o', flattened_ks])

        # TODO: Pull kickstart from separate git repo
        ksdata = open(flattened_ks).read()
        substitutions = { 'OSTREE_PORT': httpd_port,
                          'OSTREE_REF':  self.ref,
                          'OSTREE_OSNAME':  self.os_name}
        if '@OSTREE_HOST_IP@' in ksdata:
            host_ip = getDefaultIP(hostnet=self.virtnetwork)
            substitutions['OSTREE_HOST_IP'] = host_ip

        for subname, subval in substitutions.iteritems():
            print subname, subval
            ksdata = ksdata.replace('@%s@' % (subname, ), subval)

        imgfunc.checkoz()
        parameters =  { "install_script": ksdata,
                        "generate_icicle": False,
                        "oz_overrides": json.dumps(imgfunc.ozoverrides)
                      }
        print "Starting build"
        image = self.builder.build(template=open(self._tdl).read(), parameters=parameters)

        # For debug, you can comment out the above and enable the code below
        # to skip the initial image creation.  Just point myuuid at the proper
        # image uuid

        # self.builder.download()
        # myuuid = "fd301dce-fba3-421d-a2e8-182cf2cefaf8"
        # pim = PersistentImageManager.default_manager()
        # image = pim.image_with_id(myuuid)

        imageoutputdir = os.path.join(outputdir, "images")
        if not os.path.exists(imageoutputdir):
            os.mkdir(imageoutputdir)

        # Copy the qcow2 file to the outputdir
        outputname = os.path.join(imageoutputdir, '%s.qcow2' % (self.os_nr))
        shutil.copyfile(image.data, outputname)
        print "Created: {0}".format(outputname)

        if 'raw' in imageouttypes:
            print "Processing image from qcow2 to raw"
            print image.data
            outputname = os.path.join(imageoutputdir, '%s.raw' % (self.os_nr))
            print outputname

            qemucmd = ['qemu-img', 'convert', '-f', 'qcow2', '-O', 'raw', image.data, outputname]
            subprocess.check_call(qemucmd)
            imageouttypes.pop(imageouttypes.index("raw"))
            print "Created: {0}".format(outputname)

        for imagetype in imageouttypes:
            if imagetype in ['vsphere', 'rhevm']:

                # Imgfac will ensure proper qemu type is used
                print "Creating {0} image".format(imagetype)
                target_image = self.builder.buildimagetype(imagetype, image.identifier)
                infile = target_image.data
                outfile = os.path.join(imageoutputdir, '%s-%s.ova' % (self._name, imagetype))
                shutil.copyfile(infile, outfile)
                print "Created: {0}".format(outfile)

        trivhttp.stop()
Beispiel #35
0
    def install(self):
        mainctx = GLib.MainContext.new()
        mainctx.push_thread_default()

        cancellable = None
        gi.require_version("OSTree", "1.0")
        gi.require_version("RpmOstree", "1.0")
        from gi.repository import OSTree, RpmOstree
        ostreesetup = self.data.ostreesetup
        log.info("executing ostreesetup=%r", ostreesetup)

        # Initialize the filesystem - this will create the repo as well
        self._safeExecWithRedirect("ostree",
                                   ["admin", "--sysroot=" + iutil.getTargetPhysicalRoot(),
                                   "init-fs", iutil.getTargetPhysicalRoot()])

        # Here, we use the physical root as sysroot, because we haven't
        # yet made a deployment.
        sysroot_file = Gio.File.new_for_path(iutil.getTargetPhysicalRoot())
        sysroot = OSTree.Sysroot.new(sysroot_file)
        sysroot.load(cancellable)
        repo = sysroot.get_repo(None)[1]
        # We don't support resuming from interrupted installs
        repo.set_disable_fsync(True)

        self._remoteOptions = {}

        if hasattr(ostreesetup, 'nogpg') and ostreesetup.nogpg:
            self._remoteOptions['gpg-verify'] = GLib.Variant('b', False)

        if flags.noverifyssl:
            self._remoteOptions['tls-permissive'] = GLib.Variant('b', True)

        repo.remote_change(None, OSTree.RepoRemoteChange.ADD_IF_NOT_EXISTS,
                           ostreesetup.remote, ostreesetup.url,
                           GLib.Variant('a{sv}', self._remoteOptions),
                           cancellable)

        # Variable substitute the ref: https://pagure.io/atomic-wg/issue/299
        ref = RpmOstree.varsubst_basearch(ostreesetup.ref)

        progressQ.send_message(_("Starting pull of %(branchName)s from %(source)s") % \
                               {"branchName": ref, "source": ostreesetup.remote})

        progress = OSTree.AsyncProgress.new()
        progress.connect('changed', self._pullProgressCb)

        pull_opts = {'refs': GLib.Variant('as', [ref])}
        # If we're doing a kickstart, we can at least use the content as a reference:
        # See <https://github.com/rhinstaller/anaconda/issues/1117>
        # The first path here is used by <https://pagure.io/fedora-lorax-templates>
        # and the second by <https://github.com/projectatomic/rpm-ostree-toolbox/>
        if OSTree.check_version(2017, 8):
            for path in ['/ostree/repo', '/install/ostree/repo']:
                if os.path.isdir(path + '/objects'):
                    pull_opts['localcache-repos'] = GLib.Variant('as', [path])
                    break

        try:
            repo.pull_with_options(ostreesetup.remote,
                                   GLib.Variant('a{sv}', pull_opts),
                                   progress, cancellable)
        except GLib.GError as e:
            exn = PayloadInstallError("Failed to pull from repository: %s" % e)
            log.error(str(exn))
            if errors.errorHandler.cb(exn) == errors.ERROR_RAISE:
                progressQ.send_quit(1)
                iutil.ipmi_abort(scripts=self.data.scripts)
                sys.exit(1)

        log.info("ostree pull: " + (progress.get_status() or ""))
        progressQ.send_message(_("Preparing deployment of %s") % (ref, ))

        # Now that we have the data pulled, delete the remote for now.
        # This will allow a remote configuration defined in the tree
        # (if any) to override what's in the kickstart.  Otherwise,
        # we'll re-add it in post.  Ideally, ostree would support a
        # pull without adding a remote, but that would get quite
        # complex.
        repo.remote_delete(self.data.ostreesetup.remote, None)

        self._safeExecWithRedirect("ostree",
                                   ["admin", "--sysroot=" + iutil.getTargetPhysicalRoot(),
                                   "os-init", ostreesetup.osname])

        admin_deploy_args = ["admin", "--sysroot=" + iutil.getTargetPhysicalRoot(),
                             "deploy", "--os=" + ostreesetup.osname]

        admin_deploy_args.append(ostreesetup.remote + ':' + ref)

        log.info("ostree admin deploy starting")
        progressQ.send_message(_("Deployment starting: %s") % (ref, ))
        self._safeExecWithRedirect("ostree", admin_deploy_args)
        log.info("ostree admin deploy complete")
        progressQ.send_message(_("Deployment complete: %s") % (ref, ))

        # Reload now that we've deployed, find the path to the new deployment
        sysroot.load(None)
        deployments = sysroot.get_deployments()
        assert len(deployments) > 0
        deployment = deployments[0]
        deployment_path = sysroot.get_deployment_directory(deployment)
        iutil.setSysroot(deployment_path.get_path())

        try:
            self._copyBootloaderData()
        except (OSError, RuntimeError) as e:
            exn = PayloadInstallError("Failed to copy bootloader data: %s" % e)
            log.error(str(exn))
            if errors.errorHandler.cb(exn) == errors.ERROR_RAISE:
                progressQ.send_quit(1)
                iutil.ipmi_abort(scripts=self.data.scripts)
                sys.exit(1)

        mainctx.pop_thread_default()
Beispiel #36
0
    def _do_checkout(self, repo, name, img, upgrade, values, destination, unitfileout, tmpfilesout, extract_only, remote):
        if not values:
            values = {}

        remote_path = self._resolve_remote_path(remote)

        _, rev = self._resolve_image(repo, img)
        if rev is None:
            raise ValueError("Image %s not found" % img)

        if remote_path:
            remote_rootfs = os.path.join(remote_path, "rootfs")
            if os.path.exists(remote_rootfs):
                util.write_out("The remote rootfs for this container is set to be %s" % remote_rootfs)
            elif os.path.exists(os.path.join(remote, "usr")): # Assume that the user directly gave the location of the rootfs
                remote_rootfs = remote
                remote_path = os.path.dirname(remote_path) # Use the parent directory as the "container location"
            else:
                raise ValueError("--remote was specified but the given location does not contain a rootfs")
            exports = os.path.join(remote_path, "rootfs/exports")
        else:
            exports = os.path.join(destination, "rootfs/exports")

        unitfile = os.path.join(exports, "service.template")
        tmpfiles = os.path.join(exports, "tmpfiles.template")

        util.write_out("Extracting to %s" % destination)

        # upgrade will not restart the service if it was not already running
        was_service_active = self._is_service_active(name)

        if self.display:
            return

        if self.user:
            rootfs = os.path.join(destination, "rootfs")
        elif extract_only:
            rootfs = destination
        elif remote_path:
            rootfs = os.path.join(remote_path, "rootfs")
        else:
            # Under Atomic, get the real deployment location if we're using the
            # system repo. It is needed to create the hard links.
            if self.get_ostree_repo_location() == '/ostree/repo':
                try:
                    sysroot = OSTree.Sysroot()
                    sysroot.load()
                    osname = sysroot.get_booted_deployment().get_osname()
                    destination = os.path.join("/ostree/deploy/", osname, os.path.relpath(destination, "/"))
                    destination = os.path.realpath(destination)
                except: #pylint: disable=bare-except
                    pass
            rootfs = os.path.join(destination, "rootfs")

        if os.path.exists(destination):
            shutil.rmtree(destination)

        if remote_path:
            os.makedirs(destination)
        else:
            os.makedirs(rootfs)

        manifest = self._image_manifest(repo, rev)

        if not remote_path:
            rootfs_fd = None
            try:
                rootfs_fd = os.open(rootfs, os.O_DIRECTORY)
                if manifest is None:
                    self._checkout_layer(repo, rootfs_fd, rootfs, rev)
                else:
                    layers = SystemContainers.get_layers_from_manifest(json.loads(manifest))
                    for layer in layers:
                        rev_layer = repo.resolve_rev("%s%s" % (OSTREE_OCIIMAGE_PREFIX, layer.replace("sha256:", "")), True)[1]
                        if not rev_layer:
                            raise ValueError("Layer not found: %s.  Please pull again the image" % layer.replace("sha256:", ""))

                        self._checkout_layer(repo, rootfs_fd, rootfs, rev_layer)
                self._do_syncfs(rootfs, rootfs_fd)
            finally:
                if rootfs_fd:
                    os.close(rootfs_fd)

        if extract_only:
            return

        if self.user:
            values["RUN_DIRECTORY"] = os.environ.get("XDG_RUNTIME_DIR", "/run/user/%s" % (os.getuid()))
            values["STATE_DIRECTORY"] = "%s/.data" % HOME
        else:
            values["RUN_DIRECTORY"] = "/run"
            values["STATE_DIRECTORY"] = "/var/lib"

        # When installing a new system container, set values in this order:
        #
        # 1) What comes from manifest.json, if present, as default value.
        # 2) What the user sets explictly as --set
        # 3) Values for DESTDIR and NAME
        manifest_file = os.path.join(exports, "manifest.json")
        if os.path.exists(manifest_file):
            with open(manifest_file, "r") as f:
                manifest = json.loads(f.read())
                if "defaultValues" in manifest:
                    for key, val in manifest["defaultValues"].items():
                        if key not in values:
                            values[key] = val

        if self.args.setvalues is not None:
            for i in self.args.setvalues:
                split = i.find("=")
                if split < 0:
                    raise ValueError("Invalid value '%s'.  Expected form NAME=VALUE" % i)
                key, val = i[:split], i[split+1:]
                values[key] = val

        values["DESTDIR"] = destination
        values["NAME"] = name
        values["EXEC_START"], values["EXEC_STOP"] = self._generate_systemd_startstop_directives(name)
        values["HOST_UID"] = os.getuid()
        values["HOST_GID"] = os.getgid()

        def _write_template(inputfilename, data, values, destination):
            try:
                os.makedirs(os.path.dirname(destination))
            except OSError:
                pass
            with open(destination, "w") as outfile:
                template = Template(data)
                result = template.safe_substitute(values)
                missing = {"".join(x) for x in template.pattern.findall(data) if "".join(x) not in values} # pylint: disable=no-member
                if len(missing):
                    raise ValueError("The template file '%s' still contains unreplaced values for: %s" % \
                                     (inputfilename, ", ".join(missing)))
                outfile.write(result)

        src = os.path.join(exports, "config.json")
        destination_path = os.path.join(destination, "config.json")
        if os.path.exists(src):
            shutil.copyfile(src, destination_path)
        elif os.path.exists(src + ".template"):
            with open(src + ".template", 'r') as infile:
                _write_template(src + ".template", infile.read(), values, destination_path)
        else:
            self._generate_default_oci_configuration(destination)

        if remote_path:
            with open(destination_path, 'r') as config_file:
                config = json.loads(config_file.read())
                config['root']['path'] = remote_rootfs
            with open(destination_path, 'w') as config_file:
                config_file.write(json.dumps(config, indent=4))

        # When upgrading, stop the service and remove previously installed
        # tmpfiles, before restarting the service.
        if upgrade:
            if was_service_active:
                self._systemctl_command("stop", name)
            if os.path.exists(tmpfilesout):
                try:
                    self._systemd_tmpfiles("--remove", tmpfilesout)
                except subprocess.CalledProcessError:
                    pass

        missing_bind_paths = self._check_oci_configuration_file(destination_path, remote_path)

        image_manifest = self._image_manifest(repo, rev)
        image_id = rev
        if image_manifest:
            image_manifest = json.loads(image_manifest)
            if 'Digest' in image_manifest:
                image_id = image_manifest['Digest'].replace("sha256:", "")

        with open(os.path.join(destination, "info"), 'w') as info_file:
            info = {"image" : img,
                    "revision" : image_id,
                    "ostree-commit": rev,
                    'created' : calendar.timegm(time.gmtime()),
                    "values" : values,
                    "remote" : remote}
            info_file.write(json.dumps(info, indent=4))

        if os.path.exists(unitfile):
            with open(unitfile, 'r') as infile:
                systemd_template = infile.read()
        else:
            systemd_template = SYSTEMD_UNIT_FILE_DEFAULT_TEMPLATE

        if os.path.exists(tmpfiles):
            with open(tmpfiles, 'r') as infile:
                tmpfiles_template = infile.read()
        else:
            tmpfiles_template = SystemContainers._generate_tmpfiles_data(missing_bind_paths, values["STATE_DIRECTORY"])

        _write_template(unitfile, systemd_template, values, unitfileout)
        shutil.copyfile(unitfileout, os.path.join(destination, "%s.service" % name))
        if (tmpfiles_template):
            _write_template(unitfile, tmpfiles_template, values, tmpfilesout)
            shutil.copyfile(unitfileout, os.path.join(destination, "tmpfiles-%s.conf" % name))

        sym = "%s/%s" % (self._get_system_checkout_path(), name)
        if os.path.exists(sym):
            os.unlink(sym)
        os.symlink(destination, sym)

        self._systemctl_command("daemon-reload")
        if (tmpfiles_template):
            self._systemd_tmpfiles("--create", tmpfilesout)

        if not upgrade:
            self._systemctl_command("enable", name)
        elif was_service_active:
            self._systemctl_command("start", name)
Beispiel #37
0
def copy_commit(repo, src_rev, dest_ref):
    """Copy commit src_rev to dest_ref

    This makes the new commit at dest_ref have the proper collection
    binding for this repo. The caller is expected to manage the
    ostree transaction.

    This is like "flatpak build-commit-from", but we need more
    control over the transaction.
    """
    logging.info('Copying commit %s to %s', src_rev, dest_ref)

    _, src_root, _ = repo.read_commit(src_rev)
    _, src_variant, src_state = repo.load_commit(src_rev)

    # Only copy normal commits
    if src_state != 0:
        raise Exception('Cannot copy irregular commit {}'.format(src_rev))

    # If the dest ref exists, use the current commit as the new
    # commit's parent
    _, dest_parent = repo.resolve_rev_ext(
        dest_ref,
        allow_noent=True,
        flags=OSTree.RepoResolveRevExtFlags.REPO_RESOLVE_REV_EXT_NONE)
    if dest_parent is not None:
        logging.info('Using %s as new commit parent', dest_parent)

    # Make a copy of the commit metadata to update. Like flatpak
    # build-commit-from, the detached metadata is not copied since
    # the only known usage is for GPG signatures, which would become
    # invalid.
    commit_metadata = GLib.VariantDict.new(src_variant.get_child_value(0))

    # Set the collection binding if the repo has a collection ID,
    # otherwise remove it
    collection_id = get_collection_id(repo)
    if collection_id is not None:
        commit_metadata.insert_value(OSTree.COMMIT_META_KEY_COLLECTION_BINDING,
                                     GLib.Variant('s', collection_id))
    else:
        commit_metadata.remove(OSTree.COMMIT_META_KEY_COLLECTION_BINDING)

    # Include the destination ref in the ref bindings
    ref_bindings = commit_metadata.lookup_value(
        OSTree.COMMIT_META_KEY_REF_BINDING, GLib.VariantType('as'))
    if ref_bindings is None:
        ref_bindings = []
    ref_bindings = set(ref_bindings)
    ref_bindings.add(dest_ref)
    commit_metadata.insert_value(OSTree.COMMIT_META_KEY_REF_BINDING,
                                 GLib.Variant('as', sorted(ref_bindings)))

    # Add flatpak specific metadata. xa.ref is deprecated, but some
    # flatpak clients might expect it. xa.from_commit will be used
    # by the app verifier to make sure the commit it sent actually
    # got there
    commit_metadata.insert_value('xa.ref', GLib.Variant('s', dest_ref))
    commit_metadata.insert_value('xa.from_commit', GLib.Variant('s', src_rev))

    # Convert from GVariantDict to GVariant vardict
    commit_metadata = commit_metadata.end()

    # Copy other commit data from source commit
    commit_subject = src_variant[COMMIT_SUBJECT_INDEX]
    commit_body = src_variant[COMMIT_BODY_INDEX]
    commit_time = OSTree.commit_get_timestamp(src_variant)

    # Make the new commit assuming the caller started a transaction
    mtree = OSTree.MutableTree.new()
    repo.write_directory_to_mtree(src_root, mtree, None)
    _, dest_root = repo.write_mtree(mtree)
    _, dest_checksum = repo.write_commit_with_time(dest_parent, commit_subject,
                                                   commit_body,
                                                   commit_metadata, dest_root,
                                                   commit_time)
    logging.info('Created new commit %s', dest_checksum)

    return dest_checksum
Beispiel #38
0
    def _import_layers_into_ostree(repo, imagebranch, manifest, layers):
        repo.prepare_transaction()
        for layer, tar in layers.items():
            mtree = OSTree.MutableTree()
            def filter_func(*args):
                info = args[2]
                if info.get_file_type() == Gio.FileType.DIRECTORY:
                    info.set_attribute_uint32("unix::mode", info.get_attribute_uint32("unix::mode") | stat.S_IWUSR)
                return OSTree.RepoCommitFilterResult.ALLOW

            modifier = OSTree.RepoCommitModifier.new(0, filter_func, None)

            metav = GLib.Variant("a{sv}", {'docker.layer': GLib.Variant('s', layer)})

            imported = False
            try:
                repo.write_archive_to_mtree(Gio.File.new_for_path(tar), mtree, modifier, True)
                root = repo.write_mtree(mtree)[1]
                csum = repo.write_commit(None, "", None, metav, root)[1]
                imported = True
            except GLib.GError as e:  #pylint: disable=catching-non-exception
                # libarchive which is used internally by OSTree to import a tarball doesn't support correctly
                # files with xattrs.  If that happens, extract the tarball and import the directory.
                if e.domain != "g-io-error-quark":  # pylint: disable=no-member
                    raise e  #pylint: disable=raising-non-exception

            if not imported:
                try:
                    temp_dir = tempfile.mkdtemp()
                    with tarfile.open(tar, 'r') as t:
                        t.extractall(temp_dir)
                    repo.write_directory_to_mtree(Gio.File.new_for_path(temp_dir), mtree, modifier)
                    root = repo.write_mtree(mtree)[1]
                    csum = repo.write_commit(None, "", None, metav, root)[1]
                finally:
                    shutil.rmtree(temp_dir)

            root = repo.write_mtree(mtree)[1]
            csum = repo.write_commit(None, "", None, metav, root)[1]
            repo.transaction_set_ref(None, "%s%s" % (OSTREE_OCIIMAGE_PREFIX, layer), csum)

        # create a $OSTREE_OCIIMAGE_PREFIX$image-$tag branch
        if not isinstance(manifest, str):
            manifest = json.dumps(manifest)

        metadata = GLib.Variant("a{sv}", {'docker.manifest': GLib.Variant('s', manifest)})
        mtree = OSTree.MutableTree()
        file_info = Gio.FileInfo()
        file_info.set_attribute_uint32("unix::uid", 0)
        file_info.set_attribute_uint32("unix::gid", 0)
        file_info.set_attribute_uint32("unix::mode", 0o755 | stat.S_IFDIR)

        dirmeta = OSTree.create_directory_metadata(file_info, None)
        csum_dirmeta = repo.write_metadata(OSTree.ObjectType.DIR_META, None, dirmeta)[1]
        mtree.set_metadata_checksum(OSTree.checksum_from_bytes(csum_dirmeta))

        root = repo.write_mtree(mtree)[1]
        csum = repo.write_commit(None, "", None, metadata, root)[1]
        repo.transaction_set_ref(None, imagebranch, csum)

        repo.commit_transaction(None)
Beispiel #39
0
    def _checkout_system_container(self,
                                   repo,
                                   name,
                                   img,
                                   deployment,
                                   upgrade,
                                   values=None,
                                   destination=None,
                                   extract_only=False):
        if not values:
            values = {}
        imagebranch = SystemContainers._get_ostree_image_branch(img)

        destination = destination or "%s/%s.%d" % (
            self._get_system_checkout_path(), name, deployment)
        exports = os.path.join(destination, "rootfs/exports")
        unitfile = os.path.join(exports, "service.template")
        if self.user:
            home = os.path.expanduser("~")
            unitfileout = os.path.join(SYSTEMD_UNIT_FILES_DEST_USER % home,
                                       "%s.service" % name)
        else:
            unitfileout = os.path.join(SYSTEMD_UNIT_FILES_DEST,
                                       "%s.service" % name)

        if not upgrade and os.path.exists(unitfileout):
            raise ValueError("The file %s already exists." % unitfileout)

        util.write_out("Extracting to %s" % destination)

        if hasattr(self.args, 'display') and self.args.display:
            return

        if self.user:
            rootfs = os.path.join(destination, "rootfs")
        elif extract_only:
            rootfs = destination
        else:
            # Under Atomic, get the real deployment location.  It is needed to create the hard links.
            try:
                sysroot = OSTree.Sysroot()
                sysroot.load()
                osname = sysroot.get_booted_deployment().get_osname()
                destination = os.path.join("/ostree/deploy/", osname,
                                           os.path.relpath(destination, "/"))
                destination = os.path.realpath(destination)
            except:  #pylint: disable=bare-except
                pass
            rootfs = os.path.join(destination, "rootfs")

        if os.path.exists(destination):
            shutil.rmtree(destination)

        os.makedirs(rootfs)

        rev = repo.resolve_rev(imagebranch, False)[1]
        manifest = self._image_manifest(repo, rev)

        rootfs_fd = None
        try:
            rootfs_fd = os.open(rootfs, os.O_DIRECTORY)
            if manifest is None:
                self._checkout_layer(repo, rootfs_fd, rootfs, rev)
            else:
                layers = SystemContainers.get_layers_from_manifest(
                    json.loads(manifest))
                for layer in layers:
                    rev_layer = repo.resolve_rev(
                        "%s%s" %
                        (OSTREE_OCIIMAGE_PREFIX, layer.replace("sha256:", "")),
                        False)[1]
                    self._checkout_layer(repo, rootfs_fd, rootfs, rev_layer)
            self._do_syncfs(rootfs, rootfs_fd)
        finally:
            if rootfs_fd:
                os.close(rootfs_fd)

        if extract_only:
            return

        # When installing a new system container, set values in this order:
        #
        # 1) What comes from manifest.json, if present, as default value.
        # 2) What the user sets explictly as --set
        # 3) Values for DESTDIR and NAME
        manifest_file = os.path.join(exports, "manifest.json")
        if os.path.exists(manifest_file):
            with open(manifest_file, "r") as f:
                manifest = json.loads(f.read())
                if "defaultValues" in manifest:
                    for key, val in manifest["defaultValues"].items():
                        if key not in values:
                            values[key] = val

        if self.args.setvalues is not None:
            for i in self.args.setvalues:
                split = i.find("=")
                if split < 0:
                    raise ValueError(
                        "Invalid value '%s'.  Expected form NAME=VALUE" % i)
                key, val = i[:split], i[split + 1:]
                values[key] = val

        values["DESTDIR"] = destination
        values["NAME"] = name
        values["EXEC_START"], values[
            "EXEC_STOP"] = self._generate_systemd_startstop_directives(name)

        def _write_template(inputfilename, data, values, outfile):
            template = Template(data)
            result = template.safe_substitute(values)
            if '$' in result.replace("$$", ""):
                missing = {
                    x[1]
                    for x in template.pattern.findall(data, template.flags)
                    if len(x[1]) > 0 and x[1] not in values
                }  # pylint: disable=no-member
                raise ValueError("The template file %s still contains unreplaced values for: %s" % \
                                 (inputfilename, ", ".join(missing)))

            outfile.write(result)

        src = os.path.join(exports, "config.json")
        destination_path = os.path.join(destination, "config.json")
        if os.path.exists(src):
            shutil.copyfile(src, destination_path)
        elif os.path.exists(src + ".template"):
            with open(src + ".template",
                      'r') as infile, open(destination_path, "w") as outfile:
                _write_template(src + ".template", infile.read(), values,
                                outfile)
        else:
            self._generate_default_oci_configuration(destination)

        self._check_oci_configuration_file(destination_path)

        image_manifest = self._image_manifest(repo, rev)
        image_id = rev
        if image_manifest:
            image_manifest = json.loads(image_manifest)
            if 'Digest' in image_manifest:
                image_id = image_manifest['Digest'].replace("sha256:", "")

        with open(os.path.join(destination, "info"), 'w') as info_file:
            info = {
                "image": img,
                "revision": image_id,
                "ostree-commit": rev,
                'created': calendar.timegm(time.gmtime()),
                "values": values
            }
            info_file.write(json.dumps(info, indent=4))

        if os.path.exists(unitfile):
            with open(unitfile, 'r') as infile:
                systemd_template = infile.read()
        else:
            systemd_template = SYSTEMD_UNIT_FILE_DEFAULT_TEMPLATE

        try:
            os.makedirs(os.path.dirname(unitfileout))
        except OSError:
            pass
        with open(unitfileout, "w") as outfile:
            _write_template(unitfile, systemd_template, values, outfile)

        sym = "%s/%s" % (self._get_system_checkout_path(), name)
        if os.path.exists(sym):
            os.unlink(sym)
        os.symlink(destination, sym)

        self._systemctl_command("enable", name)
        if upgrade:
            self._systemctl_command("restart", name)
        else:
            self._systemctl_command("start", name)
        return
    def install(self):
        mainctx = create_new_context()
        mainctx.push_thread_default()

        cancellable = None
        gi.require_version("OSTree", "1.0")
        gi.require_version("RpmOstree", "1.0")
        from gi.repository import OSTree, RpmOstree
        ostreesetup = self.data.ostreesetup
        log.info("executing ostreesetup=%r", ostreesetup)

        # Initialize the filesystem - this will create the repo as well
        self._safe_exec_with_redirect("ostree", [
            "admin", "--sysroot=" + conf.target.physical_root, "init-fs",
            conf.target.physical_root
        ])

        # Here, we use the physical root as sysroot, because we haven't
        # yet made a deployment.
        sysroot_file = Gio.File.new_for_path(conf.target.physical_root)
        sysroot = OSTree.Sysroot.new(sysroot_file)
        sysroot.load(cancellable)
        repo = sysroot.get_repo(None)[1]
        # We don't support resuming from interrupted installs
        repo.set_disable_fsync(True)

        self._remoteOptions = {}

        if hasattr(ostreesetup, 'nogpg') and ostreesetup.nogpg:
            self._remoteOptions['gpg-verify'] = Variant('b', False)

        if not conf.payload.verify_ssl:
            self._remoteOptions['tls-permissive'] = Variant('b', True)

        repo.remote_change(None, OSTree.RepoRemoteChange.ADD_IF_NOT_EXISTS,
                           ostreesetup.remote, ostreesetup.url,
                           Variant('a{sv}', self._remoteOptions), cancellable)

        # Variable substitute the ref: https://pagure.io/atomic-wg/issue/299
        ref = RpmOstree.varsubst_basearch(ostreesetup.ref)

        progressQ.send_message(
            _("Starting pull of %(branchName)s from %(source)s") % {
                "branchName": ref,
                "source": ostreesetup.remote
            })

        progress = OSTree.AsyncProgress.new()
        progress.connect('changed', self._pull_progress_cb)

        pull_opts = {'refs': Variant('as', [ref])}
        # If we're doing a kickstart, we can at least use the content as a reference:
        # See <https://github.com/rhinstaller/anaconda/issues/1117>
        # The first path here is used by <https://pagure.io/fedora-lorax-templates>
        # and the second by <https://github.com/projectatomic/rpm-ostree-toolbox/>
        if OSTree.check_version(2017, 8):
            for path in ['/ostree/repo', '/install/ostree/repo']:
                if os.path.isdir(path + '/objects'):
                    pull_opts['localcache-repos'] = Variant('as', [path])
                    break

        try:
            repo.pull_with_options(ostreesetup.remote,
                                   Variant('a{sv}', pull_opts), progress,
                                   cancellable)
        except GError as e:
            exn = PayloadInstallError("Failed to pull from repository: %s" % e)
            log.error(str(exn))
            if errors.errorHandler.cb(exn) == errors.ERROR_RAISE:
                progressQ.send_quit(1)
                util.ipmi_abort(scripts=self.data.scripts)
                sys.exit(1)

        log.info("ostree pull: %s", progress.get_status() or "")
        progressQ.send_message(_("Preparing deployment of %s") % (ref, ))

        # Now that we have the data pulled, delete the remote for now.
        # This will allow a remote configuration defined in the tree
        # (if any) to override what's in the kickstart.  Otherwise,
        # we'll re-add it in post.  Ideally, ostree would support a
        # pull without adding a remote, but that would get quite
        # complex.
        repo.remote_delete(self.data.ostreesetup.remote, None)

        self._safe_exec_with_redirect("ostree", [
            "admin", "--sysroot=" + conf.target.physical_root, "os-init",
            ostreesetup.osname
        ])

        admin_deploy_args = [
            "admin", "--sysroot=" + conf.target.physical_root, "deploy",
            "--os=" + ostreesetup.osname
        ]

        admin_deploy_args.append(ostreesetup.remote + ':' + ref)

        log.info("ostree admin deploy starting")
        progressQ.send_message(_("Deployment starting: %s") % (ref, ))
        self._safe_exec_with_redirect("ostree", admin_deploy_args)
        log.info("ostree admin deploy complete")
        progressQ.send_message(_("Deployment complete: %s") % (ref, ))

        # Reload now that we've deployed, find the path to the new deployment
        sysroot.load(None)
        deployments = sysroot.get_deployments()
        assert len(deployments) > 0
        deployment = deployments[0]
        deployment_path = sysroot.get_deployment_directory(deployment)
        util.set_system_root(deployment_path.get_path())

        try:
            self._copy_bootloader_data()
        except (OSError, RuntimeError) as e:
            exn = PayloadInstallError("Failed to copy bootloader data: %s" % e)
            log.error(str(exn))
            if errors.errorHandler.cb(exn) == errors.ERROR_RAISE:
                progressQ.send_quit(1)
                util.ipmi_abort(scripts=self.data.scripts)
                sys.exit(1)

        mainctx.pop_thread_default()
    def create(self, outputdir, name, ksfile, tdl, imageouttypes):
        self._name = name
        self._tdl = tdl
        self._kickstart = ksfile 

        [res, rev] = self.repo.resolve_rev(self.ref, False)
        [res, commit] = self.repo.load_variant(OSTree.ObjectType.COMMIT, rev)

        commitdate = GLib.DateTime.new_from_unix_utc(OSTree.commit_get_timestamp(commit)).format("%c")
        print commitdate

        target = os.path.join(outputdir, '%s.raw' % (self._name))

        port_file_path = self.workdir + '/repo-port'
        subprocess.check_call(['ostree',
                               'trivial-httpd', '--autoexit', '--daemonize',
                               '--port-file', port_file_path],
                              cwd=self.ostree_repo)

        httpd_port = open(port_file_path).read().strip()
        print "trivial httpd port=%s" % (httpd_port, )

        ks_basename = os.path.basename(ksfile)
        flattened_ks = os.path.join(self.workdir, ks_basename)

        # FIXME - eventually stop hardcoding this via some mapping
        if ks_basename.find('fedora') >= 0:
            kickstart_version = 'F21'
        else:
            kickstart_version = 'RHEL7'
        run_sync(['ksflatten', '--version', kickstart_version,
                  '-c', ksfile, '-o', flattened_ks])

        # TODO: Pull kickstart from separate git repo
        ksdata = open(flattened_ks).read()
        substitutions = { 'OSTREE_PORT': httpd_port,
                          'OSTREE_REF':  self.ref,
                          'OSTREE_OSNAME':  self.os_name }
        for subname, subval in substitutions.iteritems():
            ksdata = ksdata.replace('@%s@' % (subname, ), subval)

        parameters =  { "install_script": ksdata,
                        "generate_icicle": False,
                      }
        defaultimagetype = checkoz()
        print "Starting build"
        image = self.builder.build(template=open(self._tdl).read(), parameters=parameters)

        # For debug, you can comment out the above and enable the code below
        # to skip the initial image creation.  Just point myuuid at the proper
        # image uuid

        # self.builder.download()
        # myuuid = "32a2d0b8-da84-415c-aec6-90bb5a7f8e8b"
        # pim = PersistentImageManager.default_manager()
        # image = pim.image_with_id(myuuid)

        # This should probably be broken out to a sep. function

        if defaultimagetype == "raw": 

            # Always create a qcow2

            print "Processing image from raw to qcow2"
            print image.data
            outputname = os.path.join(outputdir, '%s.qcow2' % (self._name))
            print outputname

            qemucmd = ['qemu-img', 'convert', '-f', 'raw', '-O', 'qcow2', image.data, outputname]
            imageouttypes.pop(imageouttypes.index("kvm"))
            subprocess.check_call(qemucmd)

        if 'kvm' in imageouttypes:
            print "Processing image from qcow2 to raw"
            print image.data
            outputname = os.path.join(outputdir, '%s.raw' % (self._name))
            print outputname 

            # We use compat=0.10 to ensure we run on RHEL6 era QEMU
            qemucmd = ['qemu-img', 'convert', '-f', 'raw', '-O', 'qcow2', '-o', 'compat=0.10', image.data, outputname]
            imageouttypes.pop(imageouttypes.index("raw"))
            subprocess.check_call(qemucmd)

        shutil.copyfile(image.data, target)
        print "Created: " + target

        for imagetype in imageouttypes:
            print "Creating {0} image".format(imagetype)
            target_image = self.builder.buildimagetype(imagetype, image.identifier)
            infile = target_image.data
            outfile = os.path.join(outputdir, '%s-%s.ova' % (self._name, imagetype))
            shutil.copyfile(infile, outfile)