blob: 377c061bb554eb8dbe1c1aca37913902926c1d86 [file] [log] [blame]
savex4448e132018-04-25 15:51:14 +02001import abc
Alex41485522019-04-12 17:26:18 -05002import jinja2
savex4448e132018-04-25 15:51:14 +02003import os
Alex41485522019-04-12 17:26:18 -05004import six
5import time
savex4448e132018-04-25 15:51:14 +02006
Alex Savatieiev5118de02019-02-20 15:50:42 -06007from cfg_checker.common import const
Alex41485522019-04-12 17:26:18 -05008from cfg_checker.common import logger, logger_cli
9from cfg_checker.helpers.console_utils import Progress
savex4448e132018-04-25 15:51:14 +020010
11pkg_dir = os.path.dirname(__file__)
Alex Savatieiev6d010be2019-03-11 10:36:59 -050012pkg_dir = os.path.join(pkg_dir, os.pardir, os.pardir)
savex4448e132018-04-25 15:51:14 +020013pkg_dir = os.path.normpath(pkg_dir)
14
15
savex4448e132018-04-25 15:51:14 +020016def line_breaks(text):
17 # replace python linebreaks with html breaks
18 return text.replace("\n", "<br />")
19
20
Alex41485522019-04-12 17:26:18 -050021def get_sorted_keys(td):
22 # detect if we can sort by desc
23 # Yes, this is slow, but bullet-proof from empty desc
24 _desc = all([bool(td[k]['desc']) for k in td.keys()])
25 # Get sorted list
26 if not _desc:
27 return sorted(td.keys())
28 else:
29 return sorted(
30 td.keys(),
31 key=lambda k: (
32 td[k]['desc']['component'],
33 td[k]['desc']['app'],
34 k
35 )
36 )
37
38
39def get_max(_list):
40 return sorted(_list)[-1]
41
42
43def make_action_label(act):
44 _act_labels = {
45 const.ACT_UPGRADE: "Upgrade possible",
46 const.ACT_NEED_UP: "Needs upgrade",
47 const.ACT_NEED_DOWN: "Needs downgrade",
48 const.ACT_REPO: "Needs repo update",
49 const.ACT_NA: ""
50 }
51 return _act_labels[act]
52
53
54def make_action_class(act):
55 _act_classes = {
56 const.ACT_UPGRADE: "possible",
57 const.ACT_NEED_UP: "needs_up",
58 const.ACT_NEED_DOWN: "needs_down",
59 const.ACT_REPO: "needs_repo",
60 const.ACT_NA: ""
61 }
62 return _act_classes[act]
63
64
65def make_status_label(sts):
66 _status_labels = {
67 const.VERSION_OK: "OK",
68 const.VERSION_UP: "Upgraded",
69 const.VERSION_DOWN: "Downgraded",
70 const.VERSION_ERR: "ERROR",
71 const.VERSION_NA: "N/A"
72 }
73 return _status_labels[sts]
74
75
76def make_status_class(sts):
77 return const.all_statuses[sts]
78
79
savex4448e132018-04-25 15:51:14 +020080@six.add_metaclass(abc.ABCMeta)
81class _Base(object):
82 def __init__(self):
83 self.jinja2_env = self.init_jinja2_env()
84
85 @abc.abstractmethod
86 def __call__(self, payload):
87 pass
88
89 @staticmethod
90 def init_jinja2_env():
91 return jinja2.Environment(
92 loader=jinja2.FileSystemLoader(os.path.join(pkg_dir, 'templates')),
93 trim_blocks=True,
94 lstrip_blocks=True)
95
96
97class _TMPLBase(_Base):
98 @abc.abstractproperty
99 def tmpl(self):
100 pass
101
102 @staticmethod
103 def _count_totals(data):
104 data['counters']['total_nodes'] = len(data['nodes'])
105
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100106 def __call__(self, payload):
savex4448e132018-04-25 15:51:14 +0200107 # init data structures
108 data = self.common_data()
Alex41485522019-04-12 17:26:18 -0500109 # payload should have pre-sorted structure according to report called
110 # nodes, openstack_release, mcp_release, etc...
111 data.update(payload)
savex4448e132018-04-25 15:51:14 +0200112
113 # add template specific data
114 self._extend_data(data)
115
116 # do counts global
117 self._count_totals(data)
118
119 # specific filters
savex4448e132018-04-25 15:51:14 +0200120 self.jinja2_env.filters['linebreaks'] = line_breaks
Alex41485522019-04-12 17:26:18 -0500121 self.jinja2_env.filters['get_max'] = get_max
122
123 self.jinja2_env.filters['get_sorted_keys'] = get_sorted_keys
124 self.jinja2_env.filters['make_status_label'] = make_status_label
125 self.jinja2_env.filters['make_status_class'] = make_status_class
126 self.jinja2_env.filters['make_action_label'] = make_action_label
127 self.jinja2_env.filters['make_action_class'] = make_action_class
savex4448e132018-04-25 15:51:14 +0200128
129 # render!
Alex41485522019-04-12 17:26:18 -0500130 logger_cli.info("-> Using template: {}".format(self.tmpl))
savex4448e132018-04-25 15:51:14 +0200131 tmpl = self.jinja2_env.get_template(self.tmpl)
Alex41485522019-04-12 17:26:18 -0500132 logger_cli.info("-> Rendering")
savex4448e132018-04-25 15:51:14 +0200133 return tmpl.render(data)
134
135 def common_data(self):
136 return {
137 'counters': {},
Alex41485522019-04-12 17:26:18 -0500138 'salt_info': {},
139 'gen_date': time.strftime("%m/%d/%Y %H:%M:%S")
savex4448e132018-04-25 15:51:14 +0200140 }
141
142 def _extend_data(self, data):
143 pass
144
145
Alex41485522019-04-12 17:26:18 -0500146# HTML Package versions report
147class CSVAllPackages(_TMPLBase):
148 tmpl = "pkg_versions_csv.j2"
149
150
151# HTML Package versions report
savexce010ba2018-04-27 09:49:23 +0200152class HTMLPackageCandidates(_TMPLBase):
Alex41485522019-04-12 17:26:18 -0500153 tmpl = "pkg_versions_html.j2"
savex4448e132018-04-25 15:51:14 +0200154
155
Alex Savatieievd48994d2018-12-13 12:13:00 +0100156# Package versions report
157class HTMLModelCompare(_TMPLBase):
158 tmpl = "model_tree_cmp_tmpl.j2"
159
160 def _extend_data(self, data):
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100161 # move names into separate place
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500162 data["names"] = data["rc_diffs"].pop("diff_names")
163 data["tabs"] = data.pop("rc_diffs")
Alex Savatieiev4f149d02019-02-28 17:15:29 -0600164
Alex Savatieievd48994d2018-12-13 12:13:00 +0100165 # counters - mdl_diff
Alex Savatieiev4f149d02019-02-28 17:15:29 -0600166 for _tab in data["tabs"].keys():
167 data['counters'][_tab] = len(data["tabs"][_tab]["diffs"].keys())
Alex Savatieievd48994d2018-12-13 12:13:00 +0100168
169
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600170class HTMLNetworkReport(_TMPLBase):
171 tmpl = "network_check_tmpl.j2"
172
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600173
savex4448e132018-04-25 15:51:14 +0200174class ReportToFile(object):
175 def __init__(self, report, target):
176 self.report = report
177 self.target = target
178
179 def __call__(self, payload):
180 payload = self.report(payload)
181
182 if isinstance(self.target, six.string_types):
183 self._wrapped_dump(payload)
184 else:
185 self._dump(payload, self.target)
186
187 def _wrapped_dump(self, payload):
188 with open(self.target, 'wt') as target:
189 self._dump(payload, target)
190
191 @staticmethod
192 def _dump(payload, target):
193 target.write(payload)