nodepool/nodepool/status.py
Clark Boylan dfcf770260 Ignore unparsable/empty image build ZNode data
As with the parent it seems that we can end up with empty image build
records as well as empty image upload records. Handle the build case as
well so that we can still list image builds properly when in this state.
This logs the problem as well so that you can debug it further.

Change-Id: Ic5521f5e2d2b65db94c2a5794f474913631a7a1b
2020-10-06 12:44:18 +02:00

278 lines
8.4 KiB
Python

# Copyright 2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from collections import OrderedDict
import json
import time
from prettytable import PrettyTable
# General notes:
#
# All the _list functions should return a tuple
#
# ([ {obj}, {obj}, ...], headers_table)
#
# The headers_table is an OrderedDict that maps the fields in the
# returned objs to pretty-printable headers. Each obj in the list
# should be a dictionary with fields as described by the
# headers_table.
#
# e.g.
#
# headers_table = OrderedDict({
# 'key1': 'Key One',
# 'key2': 'Key Two'})
# objs = [ { 'key1': 'value', 'key2': 123 },
# { 'key1': 'value2', 'key2': 456 } ]
# return(objs, headers_table)
#
# The output() function takes this tuple result and a format to
# produce results for consumption by the caller.
def age(timestamp):
now = time.time()
dt = now - timestamp
m, s = divmod(dt, 60)
h, m = divmod(m, 60)
d, h = divmod(h, 24)
return '%02d:%02d:%02d:%02d' % (d, h, m, s)
def _to_pretty_table(objs, headers_table, fields):
'''Construct a pretty table output
:param objs: list of output objects
:param headers_table: list of (key, desr) header tuples
:param fields: list of fields to show; None means all
:return str: text output
'''
if fields:
headers_table = OrderedDict(
[h for h in headers_table.items() if h[0] in fields])
headers = headers_table.values()
t = PrettyTable(headers)
t.align = 'l'
for obj in objs:
values = []
for k in headers_table:
if fields and k not in fields:
continue
if k == 'age':
try:
obj_age = age(int(obj[k]))
except ValueError:
# already converted
obj_age = obj[k]
values.append(obj_age)
else:
if isinstance(obj[k], list):
values.append(','.join(obj[k]))
else:
values.append(obj[k])
t.add_row(values)
return t
def output(results, fmt, fields=None):
'''Generate output for webapp results
:param results: tuple (objs, headers) as returned by various _list
functions
:param fmt: select from ascii pretty-table or json
:param fields: list of fields to show in pretty-table output
'''
objs, headers_table = results
if fmt == 'pretty':
t = _to_pretty_table(objs, headers_table, fields)
return str(t)
elif fmt == 'json':
return json.dumps(objs)
else:
raise ValueError('Unknown format "%s"' % fmt)
def node_list(zk, node_id=None):
headers_table = [
("id", "ID"),
("provider", "Provider"),
("label", "Label"),
("server_id", "Server ID"),
("public_ipv4", "Public IPv4"),
("ipv6", "IPv6"),
("state", "State"),
("age", "Age"),
("locked", "Locked"),
("pool", "Pool"),
("hostname", "Hostname"),
("private_ipv4", "Private IPv4"),
("AZ", "AZ"),
("connection_port", "Port"),
("launcher", "Launcher"),
("allocated_to", "Allocated To"),
("hold_job", "Hold Job"),
("comment", "Comment")
]
headers_table = OrderedDict(headers_table)
def _get_node_values(node):
locked = "unlocked"
try:
zk.lockNode(node, blocking=False)
except Exception:
locked = "locked"
else:
zk.unlockNode(node)
values = [
node.id,
node.provider,
node.type,
node.external_id,
node.public_ipv4,
node.public_ipv6,
node.state,
age(node.state_time),
locked,
node.pool,
node.hostname,
node.private_ipv4,
node.az,
node.connection_port,
node.launcher,
node.allocated_to,
node.hold_job,
node.comment
]
return values
objs = []
if node_id:
node = zk.getNode(node_id)
if node:
values = _get_node_values(node)
objs.append(dict(zip(headers_table.keys(),
values)))
else:
for node in zk.nodeIterator():
values = _get_node_values(node)
objs.append(dict(zip(headers_table.keys(),
values)))
return (objs, headers_table)
def dib_image_list(zk):
headers_table = OrderedDict([
("id", "ID"),
("image", "Image"),
("builder", "Builder"),
("formats", "Formats"),
("state", "State"),
("age", "Age")])
objs = []
for image_name in zk.getImageNames():
paused = zk.getImagePaused(image_name)
for build_no in zk.getBuildNumbers(image_name):
build = zk.getBuild(image_name, build_no)
if build:
state = paused and 'paused' or build.state
objs.append({'id': '-'.join([image_name, build_no]),
'image': image_name,
'builder': build.builder,
'formats': build.formats,
'state': state,
'age': int(build.state_time)
})
return (objs, headers_table)
def image_list(zk):
headers_table = OrderedDict([
("id", "Build ID"),
("upload_id", "Upload ID"),
("provider", "Provider"),
("image", "Image"),
("external_name", "Provider Image Name"),
("external_id", "Provider Image ID"),
("state", "State"),
("age", "Age")])
objs = []
for image_name in zk.getImageNames():
for build_no in zk.getBuildNumbers(image_name):
for provider in zk.getBuildProviders(image_name, build_no):
for upload_no in zk.getImageUploadNumbers(
image_name, build_no, provider):
upload = zk.getImageUpload(image_name, build_no,
provider, upload_no)
if not upload:
continue
values = [build_no, upload_no, provider, image_name,
upload.external_name,
upload.external_id,
upload.state,
int(upload.state_time)]
objs.append(dict(zip(headers_table.keys(),
values)))
return (objs, headers_table)
def request_list(zk):
headers_table = OrderedDict([
("id", "Request ID"),
("relative_priority", "Priority"),
("state", "State"),
("requestor", "Requestor"),
("node_types", "Node Types"),
("nodes", "Nodes"),
("declined_by", "Declined By"),
("event_id", "Event ID"),
])
objs = []
for req in zk.nodeRequestIterator():
values = [req.id, req.relative_priority,
req.state, req.requestor,
req.node_types,
req.nodes,
req.declined_by,
req.event_id]
objs.append(dict(zip(headers_table.keys(),
values)))
return (objs, headers_table)
def label_list(zk):
headers_table = OrderedDict([
("label", "Label"),
])
objs = []
# Walk all launchers, find the labels they support and stick it
# all in a set.
# NOTE(ianw): maybe add to each entry a list of which
# launchers support the label?
labels = set()
launchers = zk.getRegisteredLaunchers()
for launcher in launchers:
labels.update(launcher.supported_labels)
for label in labels:
objs.append({'label': label})
return (objs, headers_table)