app: support full offline operations with --cache-only

As Colin mentioned in #1035, the new `--cache-only` implemented only the
rpmmd half of the story. Here we complete that story by also ensuring
that when in cache-only mode, we don't download new ostree data nor new
packages. We try to complete the requested operation with what we have.

To do this, we add support for the same `SYNTHETIC` pull that was added
in ostree[1] so that we don't actually pull, but still perform timestamp
checking.

On the pkgcache side, we disable all remote repos and instead insert all
our cached RPMs into the `DnfSack`. Care is taken to still perform
SHA256 verification for local pkg installs/replacements.

[1] https://github.com/ostreedev/ostree/pull/642

Closes: #687

Closes: #1049
Approved by: cgwalters
This commit is contained in:
Jonathan Lebon 2017-10-13 13:22:15 +00:00 committed by Atomic Bot
parent cd3da57453
commit b811eb61c0
16 changed files with 175 additions and 43 deletions

View File

@ -182,6 +182,12 @@ Boston, MA 02111-1307, USA.
inspect the RPM diff, but do not actually create a new
deployment.
</para>
<para>
<option>--cache-only</option> or <command>-C</command> to
perform the operation without trying to download the target
tree from the remote nor the latest packages.
</para>
</listitem>
</varlistentry>
@ -206,6 +212,12 @@ Boston, MA 02111-1307, USA.
exit after printing the transaction rather than downloading
the packages and creating a new deployment.
</para>
<para>
<option>--cache-only</option> or <command>-C</command> to
perform the operation without trying to download the latest
packages.
</para>
</listitem>
</varlistentry>
@ -265,6 +277,12 @@ Boston, MA 02111-1307, USA.
to pick a remote name.
</para>
<para>
<option>--cache-only</option> or <command>-C</command> to
perform the rebase without trying to download the target
tree from the remote nor the latest packages.
</para>
</listitem>
</varlistentry>
@ -350,6 +368,12 @@ Boston, MA 02111-1307, USA.
available, without downloading it or performing a
package-level diff.
</para>
<para>
<option>--cache-only</option> or <command>-C</command> to
perform the upgrade without trying to download the latest
tree from the remote nor the latest packages.
</para>
</listitem>
</varlistentry>

View File

@ -40,7 +40,7 @@ static GOptionEntry option_entries[] = {
* A --preview option would work for both commands if we wanted to
* deprecate --check-diff. */
{ "preview", 0, 0, G_OPTION_ARG_NONE, &opt_preview, "Just preview package differences", NULL },
{ "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not update repo metadata cache", NULL },
{ "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not download latest ostree and RPM data", NULL },
{ NULL }
};

View File

@ -43,7 +43,7 @@ static GOptionEntry option_entries[] = {
{ "remote", 'm', 0, G_OPTION_ARG_STRING, &opt_remote, "Rebase to current branch name using REMOTE; may also be combined with --branch", "REMOTE" },
{ "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Initiate a reboot after rebase is finished", NULL },
{ "skip-purge", 0, 0, G_OPTION_ARG_NONE, &opt_skip_purge, "Keep previous refspec after rebase", NULL },
{ "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not update repo metadata cache", NULL },
{ "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not download latest ostree and RPM data", NULL },
{ NULL }
};

View File

@ -76,6 +76,7 @@ rpmostree_builtin_rollback (int argc,
return EXIT_FAILURE;
g_autoptr(GVariant) previous_deployment = rpmostree_os_dup_default_deployment (os_proxy);
/* really, rollback only supports the "reboot" option; all others are ignored */
g_autoptr(GVariant) options =
rpmostree_get_options_variant (opt_reboot,
FALSE, /* allow-downgrade */

View File

@ -47,7 +47,7 @@ static GOptionEntry option_entries[] = {
{ "check-diff", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_preview, "Check for upgrades and print package diff only", NULL },
{ "preview", 0, 0, G_OPTION_ARG_NONE, &opt_preview, "Just preview package differences", NULL },
{ "check", 0, 0, G_OPTION_ARG_NONE, &opt_check, "Just check if an upgrade is available", NULL },
{ "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not update repo metadata cache", NULL },
{ "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not download latest ostree and RPM data", NULL },
{ "upgrade-unchanged-exit-77", 0, 0, G_OPTION_ARG_NONE, &opt_upgrade_unchanged_exit_77, "If no upgrade is available, exit 77", NULL },
{ NULL }
};

View File

@ -1075,7 +1075,7 @@ rpmostree_get_options_variant (gboolean reboot,
g_variant_dict_init (&dict, NULL);
g_variant_dict_insert (&dict, "reboot", "b", reboot);
g_variant_dict_insert (&dict, "allow-downgrade", "b", allow_downgrade);
g_variant_dict_insert (&dict, "rpmmd-cache-only", "b", cache_only);
g_variant_dict_insert (&dict, "cache-only", "b", cache_only);
g_variant_dict_insert (&dict, "skip-purge", "b", skip_purge);
g_variant_dict_insert (&dict, "no-pull-base", "b", no_pull_base);
g_variant_dict_insert (&dict, "dry-run", "b", dry_run);

View File

@ -50,7 +50,7 @@ static GOptionEntry uninstall_option_entry[] = {
static GOptionEntry install_option_entry[] = {
{ "uninstall", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_uninstall, "Uninstall a package", "PKG" },
{ "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not update repo metadata cache", NULL },
{ "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not download latest ostree and RPM data", NULL },
{ NULL }
};

View File

@ -255,13 +255,14 @@
Do not pull a base layer from the remote. Not valid if
either "set-refspec" or "set-revision" is specified.
"dry-run" (type 'b')
Stop short of deploying the new tree. If
layering packages, the pkg diff is printed.
Stop short of deploying the new tree. If layering packages,
the pkg diff is printed but packages are not downloaded or
imported.
"no-overrides" (type 'b')
Remove all active overrides. Not valid if any override
modifiers are specified.
"rpmmd-cache-only" (type 'b')
Do not update rpmmd repo metadata cache.
"cache-only" (type 'b')
Do not update rpmmd repo metadata cache or ostree refspec.
-->
<method name="UpdateDeployment">
<arg type="a{sv}" name="modifiers" direction="in"/>

View File

@ -382,11 +382,13 @@ rpmostree_sysroot_upgrader_pull_base (RpmOstreeSysrootUpgrader *self,
const gboolean allow_older =
(self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER) > 0;
const gboolean synthetic =
(self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL) > 0;
const char *override_commit = rpmostree_origin_get_override_commit (self->origin);
g_assert (self->origin_merge_deployment);
if (origin_remote)
if (origin_remote && !synthetic)
{
g_autoptr(GVariantBuilder) optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
if (dir_to_pull && *dir_to_pull)
@ -428,8 +430,8 @@ rpmostree_sysroot_upgrader_pull_base (RpmOstreeSysrootUpgrader *self,
gboolean changed = !g_str_equal (new_base_rev, self->base_revision);
if (changed)
{
/* check timestamps here too in case the commit was already pulled (or the
* refspec is local) */
/* check timestamps here too in case the commit was already pulled, or the pull was
* synthetic, or the refspec is local */
if (!allow_older)
{
if (!ostree_sysroot_upgrader_check_timestamps (self->repo, self->base_revision,
@ -785,6 +787,10 @@ prepare_context_for_assembly (RpmOstreeSysrootUpgrader *self,
return FALSE;
rpmostree_context_set_sepolicy (self->ctx, sepolicy);
if (self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY)
rpmostree_context_set_pkgcache_only (self->ctx, TRUE);
return TRUE;
}
@ -814,12 +820,6 @@ prep_local_assembly (RpmOstreeSysrootUpgrader *self,
cancellable, error))
return FALSE;
if (self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_RPMMD_CACHE_ONLY)
{
DnfContext *hifctx = rpmostree_context_get_hif (self->ctx);
dnf_context_set_cache_age (hifctx, G_MAXUINT);
}
g_autoptr(OstreeRepo) pkgcache_repo = NULL;
if (!rpmostree_get_pkgcache_repo (self->repo, &pkgcache_repo, cancellable, error))
return FALSE;

View File

@ -41,7 +41,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (RpmOstreeSysrootUpgrader, g_object_unref)
* @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED: Do not error if the origin has an unconfigured-state key
* @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER: Do not error if the new deployment was composed earlier than the current deployment
* @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN: Don't deploy new base. If layering packages, only print the transaction
* @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN: Don't update rpmmd cache.
* @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY: Don't try to update cached packages.
* @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL: Don't actually pull, just resolve ref and timestamp check
*
* Flags controlling operation of an #RpmOstreeSysrootUpgrader.
*/
@ -50,7 +51,8 @@ typedef enum {
RPMOSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED = (1 << 1),
RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER = (1 << 2),
RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN = (1 << 3),
RPMOSTREE_SYSROOT_UPGRADER_FLAGS_RPMMD_CACHE_ONLY = (1 << 4)
RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY = (1 << 4),
RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL = (1 << 5),
} RpmOstreeSysrootUpgraderFlags;
/* _NONE means we're doing pure ostree, no client-side computation.

View File

@ -546,8 +546,8 @@ deploy_flags_from_options (GVariant *options,
ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DRY_RUN;
if (vardict_lookup_bool (&dict, "no-overrides", FALSE))
ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES;
if (vardict_lookup_bool (&dict, "rpmmd-cache-only", FALSE))
ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_RPMMD_CACHE_ONLY;
if (vardict_lookup_bool (&dict, "cache-only", FALSE))
ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_CACHE_ONLY;
return ret;
}

View File

@ -558,11 +558,20 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER;
if (self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DRY_RUN)
upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN;
if (self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_RPMMD_CACHE_ONLY)
upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_RPMMD_CACHE_ONLY;
const gboolean no_overrides =
((self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES) > 0);
if (self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_CACHE_ONLY)
{
/* practically, we could unite those two into a single flag, though it's nice to be
* able to keep them separate as well */
/* don't pull, just resolve ref locally and timestamp check */
upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL;
/* turn on rpmmd cache only in the upgrader */
upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY;
}
/* this should have been checked already */
if (no_overrides)
{
@ -803,7 +812,10 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
rpmostree_sysroot_upgrader_set_origin (upgrader, origin);
/* Mainly for the `install` and `override` commands */
if (!(self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE))
const gboolean no_pull_base =
((self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE) > 0);
if (!no_pull_base)
{
gboolean base_changed;

View File

@ -54,7 +54,7 @@ typedef enum {
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE = (1 << 4),
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DRY_RUN = (1 << 5),
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES = (1 << 6),
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_RPMMD_CACHE_ONLY = (1 << 7),
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_CACHE_ONLY = (1 << 7),
} RpmOstreeTransactionDeployFlags;

View File

@ -243,6 +243,7 @@ struct _RpmOstreeContext {
RpmOstreeTreespec *spec;
gboolean empty;
gboolean pkgcache_only;
DnfContext *hifctx;
OstreeRepo *ostreerepo;
OstreeRepo *pkgcache_repo;
@ -403,6 +404,15 @@ rpmostree_context_ensure_tmpdir (RpmOstreeContext *self,
return TRUE;
}
void
rpmostree_context_set_pkgcache_only (RpmOstreeContext *self,
gboolean pkgcache_only)
{
/* if called, must always be before setup() */
g_assert (!self->spec);
self->pkgcache_only = pkgcache_only;
}
/* Pick up repos dir and passwd from @cfg_deployment. */
void
rpmostree_context_configure_from_deployment (RpmOstreeContext *self,
@ -600,13 +610,22 @@ rpmostree_context_setup (RpmOstreeContext *self,
if (!dnf_context_setup (self->hifctx, cancellable, error))
return FALSE;
/* NB: missing "repos" --> let hif figure it out for itself */
if (g_variant_dict_lookup (self->spec->dict, "repos", "^a&s", &enabled_repos))
if (!context_repos_enable_only (self, (const char *const*)enabled_repos, error))
return FALSE;
/* disable all repos in pkgcache-only mode, otherwise obey "repos" key */
if (self->pkgcache_only)
{
if (!context_repos_enable_only (self, NULL, error))
return FALSE;
}
else
{
/* NB: missing "repos" --> let hif figure it out for itself */
if (g_variant_dict_lookup (self->spec->dict, "repos", "^a&s", &enabled_repos))
if (!context_repos_enable_only (self, (const char *const*)enabled_repos, error))
return FALSE;
}
g_autoptr(GPtrArray) repos = get_enabled_rpmmd_repos (self->hifctx, DNF_REPO_ENABLED_PACKAGES);
if (repos->len == 0)
if (repos->len == 0 && !self->pkgcache_only)
{
/* To be nice, let's only make this fatal if "packages" is empty (e.g. if
* we're only installing local RPMs. Missing deps will cause the regular
@ -616,6 +635,7 @@ rpmostree_context_setup (RpmOstreeContext *self,
g_strv_length (pkgs) > 0)
return glnx_throw (error, "No enabled repositories");
}
/* Ensure that each repo that's enabled is marked as required; this should be
* the default, but we make sure. This is a bit of a messy topic, but for
* rpm-ostree we're being more strict about requiring repos.
@ -807,8 +827,7 @@ rpmostree_find_cache_branch_by_nevra (OstreeRepo *pkgcache,
g_autoptr(GHashTable) refs = NULL;
if (!ostree_repo_list_refs_ext (pkgcache, "rpmostree/pkg", &refs,
OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable,
error))
OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable, error))
return FALSE;
GLNX_HASH_TABLE_FOREACH (refs, const char*, ref)
@ -872,8 +891,7 @@ checkout_pkg_metadata_by_nevra (RpmOstreeContext *self,
&header, cancellable, error))
return FALSE;
return checkout_pkg_metadata (self, nevra, header,
cancellable, error);
return checkout_pkg_metadata (self, nevra, header, cancellable, error);
}
/* Fetches decomposed NEVRA information from pkgcache for a given nevra string. Requires the
@ -921,7 +939,21 @@ rpmostree_context_download_metadata (RpmOstreeContext *self,
{
g_assert (!self->empty);
g_autoptr(GPtrArray) rpmmd_repos = get_enabled_rpmmd_repos (self->hifctx, DNF_REPO_ENABLED_PACKAGES);
g_autoptr(GPtrArray) rpmmd_repos =
get_enabled_rpmmd_repos (self->hifctx, DNF_REPO_ENABLED_PACKAGES);
if (self->pkgcache_only)
{
g_assert_cmpint (rpmmd_repos->len, ==, 0);
/* this is essentially a no-op */
g_autoptr(DnfState) hifstate = dnf_state_new ();
if (!dnf_context_setup_sack (self->hifctx, hifstate, error))
return FALSE;
/* Note early return; no repos to fetch. */
return TRUE;
}
g_autoptr(GString) enabled_repos = g_string_new ("Enabled rpm-md repositories:");
for (guint i = 0; i < rpmmd_repos->len; i++)
@ -1507,7 +1539,8 @@ install_pkg_from_cache (RpmOstreeContext *self,
/* This is the great lie: we make libdnf et al. think that they're dealing with a full
* RPM, all while crossing our fingers that they don't try to look past the header.
* Ideally, it would be best if libdnf could learn to treat the pkgcache repo as another
* DnfRepo. */
* DnfRepo. We could do this all in-memory though it doesn't seem like libsolv has an
* appropriate API for this. */
g_autofree char *rpm = g_strdup_printf ("%s/metarpm/%s.rpm", self->tmpdir.path, nevra);
DnfPackage *pkg = dnf_sack_add_cmdline_package (dnf_context_get_sack (self->hifctx), rpm);
if (!pkg)
@ -1517,6 +1550,49 @@ install_pkg_from_cache (RpmOstreeContext *self,
return TRUE;
}
/* This is a hacky way to bridge the gap between libdnf and our pkgcache. We extract the
* metarpm for every RPM in our cache and present that as the cmdline repo to libdnf. But we
* do still want all the niceties of the libdnf stack, e.g. HyGoal, libsolv depsolv, etc...
*/
static gboolean
add_remaining_pkgcache_pkgs (RpmOstreeContext *self,
GHashTable *already_added,
GCancellable *cancellable,
GError **error)
{
g_assert (self->pkgcache_repo);
g_assert (self->pkgcache_only);
DnfSack *sack = dnf_context_get_sack (self->hifctx);
g_assert (sack);
g_autoptr(GHashTable) refs = NULL;
if (!ostree_repo_list_refs_ext (self->pkgcache_repo, "rpmostree/pkg", &refs,
OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable, error))
return FALSE;
GLNX_HASH_TABLE_FOREACH (refs, const char*, ref)
{
g_autofree char *nevra = rpmostree_cache_branch_to_nevra (ref);
if (g_hash_table_contains (already_added, nevra))
continue;
g_autoptr(GVariant) header = NULL;
if (!get_header_variant (self->pkgcache_repo, ref, &header, cancellable, error))
return FALSE;
if (!checkout_pkg_metadata (self, nevra, header, cancellable, error))
return FALSE;
g_autofree char *rpm = g_strdup_printf ("%s/metarpm/%s.rpm", self->tmpdir.path, nevra);
DnfPackage *pkg = dnf_sack_add_cmdline_package (sack, rpm);
if (!pkg)
return glnx_throw (error, "Failed to add local pkg %s to sack", nevra);
}
return TRUE;
}
/* Check for/download new rpm-md, then depsolve */
gboolean
rpmostree_context_prepare (RpmOstreeContext *self,
@ -1562,6 +1638,9 @@ rpmostree_context_prepare (RpmOstreeContext *self,
g_ptr_array_add (removed_pkgnames, (gpointer)pkgname);
}
/* track cached pkgs already added to the sack so far */
g_autoptr(GHashTable) already_added = g_hash_table_new (g_str_hash, g_str_equal);
/* Handle packages to replace */
g_autoptr(GPtrArray) replaced_nevras = g_ptr_array_new ();
for (char **it = cached_replace_pkgs; it && *it; it++)
@ -1575,6 +1654,7 @@ rpmostree_context_prepare (RpmOstreeContext *self,
return FALSE;
g_ptr_array_add (replaced_nevras, (gpointer)nevra);
g_hash_table_add (already_added, (gpointer)nevra);
}
/* For each new local package, tell libdnf to add it to the goal */
@ -1587,6 +1667,18 @@ rpmostree_context_prepare (RpmOstreeContext *self,
if (!install_pkg_from_cache (self, nevra, sha256, cancellable, error))
return FALSE;
g_hash_table_add (already_added, (gpointer)nevra);
}
/* If we're in cache-only mode, add all the remaining pkgs now. We do this *after* the
* replace & local pkgs since they already handle a subset of the cached pkgs and have
* SHA256 checks. But we do it *before* dnf_context_install() since those subjects are not
* directly linked to a cached pkg, so we need to teach libdnf about them beforehand. */
if (self->pkgcache_only)
{
if (!add_remaining_pkgcache_pkgs (self, already_added, cancellable, error))
return FALSE;
}
/* Loop over each named package, and tell libdnf to add it to the goal */

View File

@ -44,6 +44,9 @@ RpmOstreeContext *rpmostree_context_new_tree (int basedir_dfd,
GCancellable *cancellable,
GError **error);
void rpmostree_context_set_pkgcache_only (RpmOstreeContext *self,
gboolean pkgcache_only);
DnfContext * rpmostree_context_get_hif (RpmOstreeContext *self);
RpmOstreeTreespec *rpmostree_treespec_new_from_keyfile (GKeyFile *keyfile, GError **error);

View File

@ -173,15 +173,12 @@ vm_build_rpm_repo_mode skip refresh-md-old-pkg
vm_rpmostree refresh-md
vm_build_rpm_repo_mode skip refresh-md-new-pkg
vm_rpmostree refresh-md # shouldn't do anything since it hasn't expired yet
if ! vm_rpmostree install -C refresh-md-old-pkg --dry-run; then
assert_not_reached "failed to dry-run install old pkg from cached rpmmd"
fi
if vm_rpmostree install -C refresh-md-new-pkg --dry-run; then
if vm_rpmostree install refresh-md-new-pkg --dry-run; then
assert_not_reached "successfully dry-run installed new pkg from cached rpmmd?"
fi
vm_rpmostree refresh-md -f
if ! vm_rpmostree install -C refresh-md-new-pkg --dry-run; then
if ! vm_rpmostree install refresh-md-new-pkg --dry-run; then
assert_not_reached "failed to dry-run install new pkg from cached rpmmd?"
fi
vm_stop_httpd vmcheck
echo "ok refresh-md and --cache-only"
echo "ok refresh-md"