Initial implementation for "qvm-template search".
This commit is contained in:
parent
d656554822
commit
c573faa9c0
@ -1,9 +1,11 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import collections
|
||||||
import datetime
|
import datetime
|
||||||
import enum
|
import enum
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
import functools
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@ -80,6 +82,7 @@ class TemplateState(enum.Enum):
|
|||||||
UPGRADABLE = 'upgradable'
|
UPGRADABLE = 'upgradable'
|
||||||
|
|
||||||
def title(self):
|
def title(self):
|
||||||
|
#pylint: disable=invalid-name
|
||||||
TEMPLATE_TITLES = {
|
TEMPLATE_TITLES = {
|
||||||
TemplateState.INSTALLED: 'Installed Templates',
|
TemplateState.INSTALLED: 'Installed Templates',
|
||||||
TemplateState.AVAILABLE: 'Available Templates',
|
TemplateState.AVAILABLE: 'Available Templates',
|
||||||
@ -273,6 +276,8 @@ def qrexec_payload(args, app, spec):
|
|||||||
return payload
|
return payload
|
||||||
|
|
||||||
def qrexec_repoquery(args, app, spec='*'):
|
def qrexec_repoquery(args, app, spec='*'):
|
||||||
|
# TODO: Perhaps expose stderr for error messages?
|
||||||
|
# At least need to provide message that, e.g., repoid does not exist.
|
||||||
proc = qrexec_popen(args, app, 'qubes.TemplateSearch')
|
proc = qrexec_popen(args, app, 'qubes.TemplateSearch')
|
||||||
payload = qrexec_payload(args, app, spec)
|
payload = qrexec_payload(args, app, spec)
|
||||||
stdout, _ = proc.communicate(payload.encode('UTF-8'))
|
stdout, _ = proc.communicate(payload.encode('UTF-8'))
|
||||||
@ -338,6 +343,7 @@ def list_templates(args, app, operation):
|
|||||||
tpl_list = []
|
tpl_list = []
|
||||||
|
|
||||||
def append_list(data, status):
|
def append_list(data, status):
|
||||||
|
#pylint: disable=unused-variable
|
||||||
name, epoch, version, release, reponame, dlsize, summary = data
|
name, epoch, version, release, reponame, dlsize, summary = data
|
||||||
version_str = build_version_str((epoch, version, release))
|
version_str = build_version_str((epoch, version, release))
|
||||||
tpl_list.append((status, name, version_str, reponame))
|
tpl_list.append((status, name, version_str, reponame))
|
||||||
@ -386,7 +392,7 @@ def list_templates(args, app, operation):
|
|||||||
|
|
||||||
if args.installed or args.all:
|
if args.installed or args.all:
|
||||||
for vm in app.domains:
|
for vm in app.domains:
|
||||||
if 'template-install-date' in vm.features:
|
if 'template-name' in vm.features:
|
||||||
if not args.templates or \
|
if not args.templates or \
|
||||||
any(is_match_spec(
|
any(is_match_spec(
|
||||||
vm.features['template-name'],
|
vm.features['template-name'],
|
||||||
@ -403,6 +409,7 @@ def list_templates(args, app, operation):
|
|||||||
|
|
||||||
if args.extras:
|
if args.extras:
|
||||||
remote = set()
|
remote = set()
|
||||||
|
#pylint: disable=unused-variable
|
||||||
for name, epoch, version, release, reponame, dlsize, summary \
|
for name, epoch, version, release, reponame, dlsize, summary \
|
||||||
in query_res:
|
in query_res:
|
||||||
remote.add(name)
|
remote.add(name)
|
||||||
@ -428,12 +435,88 @@ def list_templates(args, app, operation):
|
|||||||
if len(tpl_list) == 0:
|
if len(tpl_list) == 0:
|
||||||
parser.error('No matching templates to list')
|
parser.error('No matching templates to list')
|
||||||
|
|
||||||
for k, g in itertools.groupby(tpl_list, lambda x: x[0]):
|
for k, grp in itertools.groupby(tpl_list, lambda x: x[0]):
|
||||||
print(k.title())
|
print(k.title())
|
||||||
qubesadmin.tools.print_table(list(map(lambda x: x[1:], g)))
|
qubesadmin.tools.print_table(list(map(lambda x: x[1:], grp)))
|
||||||
|
|
||||||
def search(args, app):
|
def search(args, app):
|
||||||
raise NotImplementedError
|
# Search in both installed and available templates
|
||||||
|
query_res = qrexec_repoquery(args, app)
|
||||||
|
for vm in app.domains:
|
||||||
|
if 'template-name' in vm.features:
|
||||||
|
query_res.append((
|
||||||
|
vm.features['template-name'],
|
||||||
|
vm.features['template-epoch'],
|
||||||
|
vm.features['template-version'],
|
||||||
|
vm.features['template-release'],
|
||||||
|
vm.features['template-reponame'],
|
||||||
|
vm.get_disk_utilization(),
|
||||||
|
vm.features['template-summary']))
|
||||||
|
|
||||||
|
# Get latest version for each template
|
||||||
|
query_res_tmp = []
|
||||||
|
for name, grp in itertools.groupby(query_res, lambda x: x[0]):
|
||||||
|
def compare(lhs, rhs):
|
||||||
|
return lhs if rpm.labelCompare(lhs[1:4], rhs[1:4]) < 0 else rhs
|
||||||
|
query_res_tmp.append(functools.reduce(compare, grp))
|
||||||
|
query_res = query_res_tmp
|
||||||
|
|
||||||
|
#pylint: disable=invalid-name
|
||||||
|
WEIGHT_NAME_EXACT = 1 << 3
|
||||||
|
WEIGHT_NAME = 1 << 2
|
||||||
|
WEIGHT_SUMMARY = 1 << 1
|
||||||
|
|
||||||
|
search_res = collections.defaultdict(int)
|
||||||
|
first = True
|
||||||
|
for keyword in args.templates:
|
||||||
|
local_res = collections.defaultdict(int)
|
||||||
|
#pylint: disable=unused-variable
|
||||||
|
for idx, (name, epoch, version, release, reponame, dlsize, summary) \
|
||||||
|
in enumerate(query_res):
|
||||||
|
if fnmatch.fnmatch(name, '*' + keyword + '*'):
|
||||||
|
local_res[idx] += WEIGHT_NAME
|
||||||
|
if fnmatch.fnmatch(summary, '*' + keyword + '*'):
|
||||||
|
local_res[idx] += WEIGHT_SUMMARY
|
||||||
|
if keyword == name:
|
||||||
|
local_res[idx] += WEIGHT_NAME_EXACT
|
||||||
|
for key, val in local_res.items():
|
||||||
|
if args.all or first or key in search_res:
|
||||||
|
search_res[key] += val
|
||||||
|
first = False
|
||||||
|
|
||||||
|
def key_func(x):
|
||||||
|
# Order by weight DESC, name ASC
|
||||||
|
weight = x[1]
|
||||||
|
name = query_res[x[0]][0]
|
||||||
|
return (-weight, name)
|
||||||
|
|
||||||
|
search_res = sorted(search_res.items(), key=key_func)
|
||||||
|
|
||||||
|
def gen_header(idx, weight):
|
||||||
|
# FIXME: "Exactly Matched" is printed even if the summary is not
|
||||||
|
# exactly matching
|
||||||
|
# TODO: Print matching keywords
|
||||||
|
#pylint: disable=unused-variable
|
||||||
|
name, epoch, version, release, reponame, dlsize, summary = \
|
||||||
|
query_res[idx]
|
||||||
|
keys = []
|
||||||
|
if weight & WEIGHT_NAME:
|
||||||
|
keys.append('Name')
|
||||||
|
if weight & WEIGHT_SUMMARY:
|
||||||
|
keys.append('Summary')
|
||||||
|
match = 'Exactly Matched' if weight & WEIGHT_NAME_EXACT else 'Matched'
|
||||||
|
return ' & '.join(keys) + ' ' + match
|
||||||
|
|
||||||
|
last_weight = -1
|
||||||
|
for idx, weight in search_res:
|
||||||
|
if last_weight != weight:
|
||||||
|
last_weight = weight
|
||||||
|
# Print headers
|
||||||
|
# XXX: The style is different from that of DNF
|
||||||
|
print(gen_header(idx, weight))
|
||||||
|
name, epoch, version, release, reponame, dlsize, summary = \
|
||||||
|
query_res[idx]
|
||||||
|
print(name, ':', summary)
|
||||||
|
|
||||||
def get_dl_list(args, app, version_selector=VersionSelector.LATEST):
|
def get_dl_list(args, app, version_selector=VersionSelector.LATEST):
|
||||||
full_candid = {}
|
full_candid = {}
|
||||||
@ -513,6 +596,7 @@ def download(args, app, path_override=None,
|
|||||||
|
|
||||||
path = path_override if path_override is not None else args.downloaddir
|
path = path_override if path_override is not None else args.downloaddir
|
||||||
for name, (ver, dlsize, reponame) in dl_list.items():
|
for name, (ver, dlsize, reponame) in dl_list.items():
|
||||||
|
_ = reponame # unused
|
||||||
version_str = build_version_str(ver)
|
version_str = build_version_str(ver)
|
||||||
spec = PACKAGE_NAME_PREFIX + name + '-' + version_str
|
spec = PACKAGE_NAME_PREFIX + name + '-' + version_str
|
||||||
target = os.path.join(path, '%s.rpm' % spec)
|
target = os.path.join(path, '%s.rpm' % spec)
|
||||||
@ -543,7 +627,7 @@ def download(args, app, path_override=None,
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def remove(args, app):
|
def remove(args, app):
|
||||||
_ = app # unused
|
_ = args, app # unused
|
||||||
|
|
||||||
# Remove 'remove' entry from the args...
|
# Remove 'remove' entry from the args...
|
||||||
operation_idx = sys.argv.index('remove')
|
operation_idx = sys.argv.index('remove')
|
||||||
|
Loading…
Reference in New Issue
Block a user