Files
python-fuelclient/fuelclient/cli/formatting.py
Ilya Kutukov 40f7a87eca Minor fixes to the formatter and task_names parameter getter
Change-Id: I6e10180bdc2c427a7d1aaa04bdf444dbaf4404a0
Related-Bug: #1563317
2016-05-31 16:35:13 +03:00

257 lines
7.8 KiB
Python

# Copyright 2014 Mirantis, Inc.
#
# 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.
import curses
from functools import partial
from itertools import chain
import math
from operator import itemgetter
import sys
from time import sleep
import six
from fuelclient.cli.error import DeployProgressError
def format_table(data, acceptable_keys=None, column_to_join=None):
"""Format list of dicts to table in a string form
:acceptable_keys list(str): list of keys for which to create table
also specifies their order
"""
# prepare columns
if column_to_join is not None:
for data_dict in data:
for column_name in column_to_join:
data_dict[column_name] = u", ".join(
sorted(data_dict[column_name])
)
if acceptable_keys is not None:
rows = [tuple(value.get(key, "") for key in acceptable_keys)
for value in data]
header = tuple(acceptable_keys)
else:
rows = [tuple(x.values()) for x in data]
header = tuple(data[0].keys())
number_of_columns = len(header)
# split multi-lines cells if there is no automatic columns merge
if column_to_join:
def format_cell(cell):
return [cell or ""]
else:
def format_cell(cell):
return six.text_type(cell).split('\n')
rows = [
[format_cell(cell) if cell is not None else [''] for cell in row]
for row in rows
]
# calculate columns widths
column_widths = dict(
zip(
range(number_of_columns),
(len(str(x)) for x in header)
)
)
for row in rows:
column_widths.update(
(
index,
max(
column_widths[index],
max(len(six.text_type(line)) for line in cell)
)
)
for index, cell in enumerate(row)
)
# make output
hor_delimeter = u'-+-'.join(column_widths[column_index] * u'-'
for column_index in range(number_of_columns))
row_template = u' | '.join(
u"{{{0}:{1}}}".format(idx, width)
for idx, width in column_widths.items()
)
output_lines = [
row_template.format(*header),
hor_delimeter
]
for row in rows:
max_cell_lines = max(len(cell) for cell in row)
for cell_line_no in range(max_cell_lines):
output_lines.append(
row_template.format(
*(
cell[cell_line_no] if len(cell) > cell_line_no else u""
for cell in row
)
)
)
return u'\n'.join(output_lines)
def quote_and_join(words):
"""quote_and_join - performs listing of objects and returns string.
"""
words = list(words)
if len(words) > 1:
return '{0} and "{1}"'.format(
", ".join(
['"{0}"'.format(x) for x in words][:-1]
),
words[-1]
)
else:
return '"{0}"'.format(words[0])
def get_bar_for_progress(full_width, progress):
"""get_bar_for_progress - returns string with a width of 'full_width'
which illustrates specific progress value.
"""
number_of_equal_signs = int(
math.ceil(progress * float(full_width - 2) / 100)
)
return "[{0}{1}{2}]".format(
"=" * number_of_equal_signs,
">" if number_of_equal_signs < full_width - 2 else "",
" " * (full_width - 3 - number_of_equal_signs)
)
def print_deploy_progress(deploy_task):
"""Receives 'deploy_task' and depending on terminal availability
starts progress printing routines with or without curses.
"""
try:
terminal_screen = curses.initscr()
print_deploy_progress_with_terminal(deploy_task, terminal_screen)
except curses.error:
print_deploy_progress_without_terminal(deploy_task)
def print_deploy_progress_without_terminal(deploy_task):
print("Deploying changes to environment with id={0}".format(
deploy_task.env.id
))
message_len = 0
try:
for progress, nodes in deploy_task:
sys.stdout.write("\r" * message_len)
message_len = 0
deployment_message = "[Deployment: {0:3}%]".format(progress)
sys.stdout.write(deployment_message)
message_len += len(deployment_message)
for index, node in enumerate(nodes):
node_message = "[Node{id:2} {progress:3}%]".format(
**node.data
)
message_len += len(node_message)
sys.stdout.write(node_message)
print("\nFinished deployment!")
except DeployProgressError as de:
print(de.message)
def print_deploy_progress_with_terminal(deploy_task, terminal_screen):
scr_width = terminal_screen.getmaxyx()[1]
curses.noecho()
curses.cbreak()
total_progress_bar = partial(get_bar_for_progress, scr_width - 17)
node_bar = partial(get_bar_for_progress, scr_width - 28)
env_id = deploy_task.env.id
try:
for progress, nodes in deploy_task:
terminal_screen.refresh()
terminal_screen.addstr(
0, 0,
"Deploying changes to environment with id={0}".format(
env_id
)
)
terminal_screen.addstr(
1, 0,
"Deployment: {0} {1:3}%".format(
total_progress_bar(progress),
progress
)
)
for index, node in enumerate(nodes):
terminal_screen.addstr(
index + 2, 0,
"Node{id:3} {status:13}: {bar} {progress:3}%"
.format(bar=node_bar(node.progress), **node.data)
)
except DeployProgressError as de:
close_curses()
print(de.message)
finally:
close_curses()
def close_curses():
curses.echo()
curses.nocbreak()
curses.endwin()
def print_health_check(env):
tests_states = [{"status": "not finished"}]
finished_tests = set()
test_counter, total_tests_count = 1, None
while not all(map(
lambda t: t["status"] == "finished",
tests_states
)):
tests_states = env.get_state_of_tests()
all_tests = list(chain(*map(
itemgetter("tests"),
filter(
env.is_in_running_test_sets,
tests_states
))))
if total_tests_count is None:
total_tests_count = len(all_tests)
all_finished_tests = filter(
lambda t: "running" not in t["status"],
all_tests
)
new_finished_tests = filter(
lambda t: t["name"] not in finished_tests,
all_finished_tests
)
finished_tests.update(
map(
itemgetter("name"),
new_finished_tests
)
)
for test in new_finished_tests:
print(
u"[{0:2} of {1}] [{status}] '{name}' "
u"({taken:.4} s) {message}".format(
test_counter,
total_tests_count,
**test
)
)
test_counter += 1
sleep(1)