1import re
2import time
3import os
4import datetime
5from behave import given, when, then
6from crmsh import corosync, parallax
7from utils import check_cluster_state, check_service_state, online, run_command, me, \
8                  run_command_local_or_remote, file_in_archive
9import const
10
11@when('Write multi lines to file "{f}"')
12def step_impl(context, f):
13    with open(f, 'w') as fd:
14        fd.write(context.text)
15
16@given('Cluster service is "{state}" on "{addr}"')
17def step_impl(context, state, addr):
18    assert check_cluster_state(context, state, addr) is True
19
20
21@given('Service "{name}" is "{state}" on "{addr}"')
22def step_impl(context, name, state, addr):
23    assert check_service_state(context, name, state, addr) is True
24
25
26@given('Has disk "{disk}" on "{addr}"')
27def step_impl(context, disk, addr):
28    out = run_command_local_or_remote(context, "fdisk -l", addr)
29    assert re.search(r'{} '.format(disk), out) is not None
30
31
32@given('Online nodes are "{nodelist}"')
33def step_impl(context, nodelist):
34    assert online(context, nodelist) is True
35
36
37@given('IP "{addr}" is belong to "{iface}"')
38def step_impl(context, addr, iface):
39    cmd = 'ip address show dev {}'.format(iface)
40    res = re.search(r' {}/'.format(addr), run_command(context, cmd)[1])
41    assert bool(res) is True
42
43
44@when('Run "{cmd}" on "{addr}"')
45def step_impl(context, cmd, addr):
46    out = run_command_local_or_remote(context, cmd, addr)
47    context.stdout = out
48
49
50@then('Print stdout')
51def step_impl(context):
52    context.logger.info("\n{}".format(context.stdout))
53
54
55@then('Print stderr')
56def step_impl(context):
57    context.logger.info("\n{}".format(context.command_error_output))
58
59
60@when('Try "{cmd}" on "{addr}"')
61def step_impl(context, cmd, addr):
62    run_command_local_or_remote(context, cmd, addr, err_record=True)
63
64
65@when('Try "{cmd}"')
66def step_impl(context, cmd):
67    rc, out = run_command(context, cmd, err_record=True)
68    context.return_code = rc
69
70
71@when('Wait "{second}" seconds')
72def step_impl(context, second):
73    time.sleep(int(second))
74
75
76@then('Got output "{msg}"')
77def step_impl(context, msg):
78    assert context.stdout == msg
79    context.stdout = None
80
81
82@then('Expected multiple lines')
83def step_impl(context):
84    assert context.stdout == context.text
85    context.stdout = None
86
87
88@then('Expected "{msg}" in stdout')
89def step_impl(context, msg):
90    assert msg in context.stdout
91    context.stdout = None
92
93
94@then('Expected regrex "{reg_str}" in stdout')
95def step_impl(context, reg_str):
96    res = re.search(reg_str, context.stdout)
97    assert res is not None
98    context.stdout = None
99
100
101@then('Expected return code is "{num}"')
102def step_impl(context, num):
103    assert context.return_code == int(num)
104
105
106@then('Expected "{msg}" not in stdout')
107def step_impl(context, msg):
108    assert msg not in context.stdout
109    context.stdout = None
110
111
112@then('Except "{msg}"')
113def step_impl(context, msg):
114    assert msg in context.command_error_output
115    context.command_error_output = None
116
117
118@then('Except multiple lines')
119def step_impl(context):
120    assert context.command_error_output.split('\n') == context.text.split('\n')
121    context.command_error_output = None
122
123
124@then('Expected multiple lines in output')
125def step_impl(context):
126    assert context.text in context.stdout
127    context.stdout = None
128
129
130@then('Except "{msg}" in stderr')
131def step_impl(context, msg):
132    assert msg in context.command_error_output
133    context.command_error_output = None
134
135
136@then('Cluster service is "{state}" on "{addr}"')
137def step_impl(context, state, addr):
138    assert check_cluster_state(context, state, addr) is True
139
140
141@then('Service "{name}" is "{state}" on "{addr}"')
142def step_impl(context, name, state, addr):
143    assert check_service_state(context, name, state, addr) is True
144
145
146@then('Online nodes are "{nodelist}"')
147def step_impl(context, nodelist):
148    assert online(context, nodelist) is True
149
150
151@then('IP "{addr}" is used by corosync on "{node}"')
152def step_impl(context, addr, node):
153    out = run_command_local_or_remote(context, 'corosync-cfgtool -s', node)
154    res = re.search(r' {}\n'.format(addr), out)
155    assert bool(res) is True
156
157
158@then('Cluster name is "{name}"')
159def step_impl(context, name):
160    _, out = run_command(context, 'corosync-cmapctl -b totem.cluster_name')
161    assert out.split()[-1] == name
162
163
164@then('Cluster virtual IP is "{addr}"')
165def step_impl(context, addr):
166    _, out = run_command(context, 'crm configure show|grep -A1 IPaddr2')
167    res = re.search(r' ip={}'.format(addr), out)
168    assert bool(res) is True
169
170
171@then('Cluster is using udpu transport mode')
172def step_impl(context):
173    assert corosync.get_value('totem.transport') == 'udpu'
174
175
176@then('Show cluster status on "{addr}"')
177def step_impl(context, addr):
178    out = run_command_local_or_remote(context, 'crm_mon -1', addr)
179    if out:
180        context.logger.info("\n{}".format(out))
181
182
183@then('Show corosync ring status')
184def step_impl(context):
185    _, out = run_command(context, 'crm corosync status ring')
186    if out:
187        context.logger.info("\n{}".format(out))
188
189
190@then('Show crm configure')
191def step_impl(context):
192    _, out = run_command(context, 'crm configure show')
193    if out:
194        context.logger.info("\n{}".format(out))
195
196
197@then('Show status from qnetd')
198def step_impl(context):
199    _, out = run_command(context, 'crm corosync status qnetd')
200    if out:
201        context.logger.info("\n{}".format(out))
202
203
204@then('Show corosync qdevice configuration')
205def step_impl(context):
206    _, out = run_command(context, "sed -n -e '/quorum/,/^}/ p' /etc/corosync/corosync.conf")
207    if out:
208        context.logger.info("\n{}".format(out))
209
210
211@then('Resource "{res}" type "{res_type}" is "{state}"')
212def step_impl(context, res, res_type, state):
213    try_count = 0
214    result = None
215    while try_count < 5:
216        time.sleep(1)
217        _, out = run_command(context, "crm_mon -1rR")
218        if out:
219            result = re.search(r'\s{}\s+.*:+{}\):\s+{} '.format(res, res_type, state), out)
220            if not result:
221                try_count += 1
222            else:
223                break
224    assert result is not None
225
226
227@then('Resource "{res}" failcount on "{node}" is "{number}"')
228def step_impl(context, res, node, number):
229    cmd = "crm resource failcount {} show {}".format(res, node)
230    _, out = run_command(context, cmd)
231    if out:
232        result = re.search(r'name=fail-count-{} value={}'.format(res, number), out)
233        assert result is not None
234
235
236@then('Resource "{res_type}" not configured')
237def step_impl(context, res_type):
238    _, out = run_command(context, "crm configure show")
239    result = re.search(r' {} '.format(res_type), out)
240    assert result is None
241
242
243@then('Output is the same with expected "{cmd}" help output')
244def step_impl(context, cmd):
245    cmd_help = {}
246    cmd_help["crm"] = const.CRM_H_OUTPUT
247    cmd_help["crm_cluster_init"] = const.CRM_CLUSTER_INIT_H_OUTPUT
248    cmd_help["crm_cluster_join"] = const.CRM_CLUSTER_JOIN_H_OUTPUT
249    cmd_help["crm_cluster_add"] = const.CRM_CLUSTER_ADD_H_OUTPUT
250    cmd_help["crm_cluster_remove"] = const.CRM_CLUSTER_REMOVE_H_OUTPUT
251    cmd_help["crm_cluster_geo-init"] = const.CRM_CLUSTER_GEO_INIT_H_OUTPUT
252    cmd_help["crm_cluster_geo-join"] = const.CRM_CLUSTER_GEO_JOIN_H_OUTPUT
253    cmd_help["crm_cluster_geo-init-arbitrator"] = const.CRM_CLUSTER_GEO_INIT_ARBIT_H_OUTPUT
254    key = '_'.join(cmd.split())
255    assert context.stdout == cmd_help[key]
256
257
258@then('Corosync working on "{transport_type}" mode')
259def step_impl(context, transport_type):
260    if transport_type == "multicast":
261        assert corosync.get_value("totem.transport") != "udpu"
262    if transport_type == "unicast":
263        assert corosync.get_value("totem.transport") == "udpu"
264
265
266@then('Expected votes will be "{votes}"')
267def step_impl(context, votes):
268    assert int(corosync.get_value("quorum.expected_votes")) == int(votes)
269
270
271@then('Default hb_report tar file created')
272def step_impl(context):
273    default_file_name = 'hb_report-{}.tar.bz2'.format(datetime.datetime.now().strftime("%w-%d-%m-%Y"))
274    assert os.path.exists(default_file_name) is True
275
276
277@when('Remove default hb_report tar file')
278def step_impl(context):
279    default_file_name = 'hb_report-{}.tar.bz2'.format(datetime.datetime.now().strftime("%w-%d-%m-%Y"))
280    os.remove(default_file_name)
281
282
283@then('File "{f}" in "{archive}"')
284def step_impl(context, f, archive):
285    assert file_in_archive(f, archive) is True
286
287
288@then('File "{f}" not in "{archive}"')
289def step_impl(context, f, archive):
290    assert file_in_archive(f, archive) is False
291
292
293@then('File "{f}" was synced in cluster')
294def step_impl(context, f):
295    cmd = "crm cluster diff {}".format(f)
296    rc, out = run_command(context, cmd)
297    assert out == ""
298