|
1 | 1 | from __future__ import print_function |
2 | 2 |
|
3 | 3 | import click |
4 | | -from cmd2 import Cmd |
5 | 4 |
|
6 | 5 | from stackdio.cli.mixins.blueprints import get_blueprint_id |
| 6 | +from stackdio.cli.polling import poll_and_wait |
7 | 7 | from stackdio.cli.utils import print_summary |
8 | 8 | from stackdio.client.exceptions import StackException |
9 | 9 |
|
@@ -141,16 +141,75 @@ def perform_action(obj, stack_title, action): |
141 | 141 | raise click.UsageError(e.message) |
142 | 142 |
|
143 | 143 |
|
| 144 | +def print_command_output(json_blob): |
| 145 | + for host in sorted(json_blob['std_out'], key=lambda x: x['host']): |
| 146 | + click.secho('{0}:'.format(host['host']), fg='green') |
| 147 | + click.echo(host['output']) |
| 148 | + click.echo() |
| 149 | + |
| 150 | + |
144 | 151 | @stacks.command(name='run') |
145 | | -@click.pass_obj |
| 152 | +@click.pass_context |
146 | 153 | @click.argument('stack_title') |
| 154 | +@click.argument('host_target') |
147 | 155 | @click.argument('command') |
148 | | -@click.option('-w', '--wait', is_flag=True, help='Wait for the command to finish running') |
149 | | -def run_command(obj, stack_title, command, wait): |
| 156 | +@click.option('-w', '--wait', is_flag=True, default=False, |
| 157 | + help='Wait for the command to finish running') |
| 158 | +@click.option('-t', '--timeout', type=click.INT, default=120, |
| 159 | + help='The amount of time to wait for the command in seconds. ' |
| 160 | + 'Ignored if used without the -w option.') |
| 161 | +def run_command(ctx, stack_title, host_target, command, wait, timeout): |
150 | 162 | """ |
151 | 163 | Run a command on all hosts in the stack |
152 | 164 | """ |
153 | | - pass |
| 165 | + client = ctx.obj['client'] |
| 166 | + |
| 167 | + stack_id = get_stack_id(client, stack_title) |
| 168 | + |
| 169 | + resp = client.run_command(stack_id, host_target, command) |
| 170 | + |
| 171 | + if not wait: |
| 172 | + # Grab the parent info name |
| 173 | + name = ctx.parent.parent.info_name |
| 174 | + |
| 175 | + click.echo('Command "{0}" running on "{1}" hosts. ' |
| 176 | + 'Check the status by running:'.format(command, host_target)) |
| 177 | + click.echo() |
| 178 | + click.secho(' {0} stacks command-output {1}'.format(name, resp['id']), fg='yellow') |
| 179 | + click.echo() |
| 180 | + return |
| 181 | + |
| 182 | + @poll_and_wait |
| 183 | + def check_status(): |
| 184 | + command_out = client.get_command(resp['id']) |
| 185 | + |
| 186 | + if command_out['status'] != 'finished': |
| 187 | + return False |
| 188 | + |
| 189 | + click.echo() |
| 190 | + print_command_output(command_out) |
| 191 | + |
| 192 | + return True |
| 193 | + |
| 194 | + check_status(max_time=timeout) |
| 195 | + |
| 196 | + |
| 197 | +@stacks.command(name='command-output') |
| 198 | +@click.pass_obj |
| 199 | +@click.argument('command_id') |
| 200 | +def get_command_output(obj, command_id): |
| 201 | + """ |
| 202 | + Get the status and output of a command |
| 203 | + """ |
| 204 | + client = obj['client'] |
| 205 | + |
| 206 | + resp = client.get_command(command_id) |
| 207 | + |
| 208 | + if resp['status'] != 'finished': |
| 209 | + click.secho('Status: {0}'.format(resp['status']), fg='yellow') |
| 210 | + return |
| 211 | + |
| 212 | + print_command_output(resp) |
154 | 213 |
|
155 | 214 |
|
156 | 215 | def print_logs(client, stack_id): |
@@ -215,95 +274,3 @@ def stack_logs(obj, stack_title, log_type, lines): |
215 | 274 | print_logs(client, stack_id) |
216 | 275 |
|
217 | 276 | raise click.UsageError('Invalid log') |
218 | | - |
219 | | - |
220 | | -class StackMixin(Cmd): |
221 | | - |
222 | | - def _stack_access_rules(self, args): |
223 | | - """Get access rules for a stack""" |
224 | | - |
225 | | - COMMANDS = ["list", "add", "delete"] |
226 | | - |
227 | | - if len(args) < 2 or args[0] not in COMMANDS: |
228 | | - print("Usage: stacks access_rules COMMAND STACK_NAME") |
229 | | - print("Where COMMAND is one of: %s" % (", ".join(COMMANDS))) |
230 | | - return |
231 | | - |
232 | | - if args[0] == "list": |
233 | | - stack_id = self.stacks.get_stack_id(args[1]) |
234 | | - groups = self.stacks.list_access_rules(stack_id) |
235 | | - print("## {0} Access Groups".format(len(groups))) |
236 | | - for group in groups: |
237 | | - print("- Name: {0}".format(group['blueprint_host_definition']['title'])) |
238 | | - print(" Description: {0}".format(group['blueprint_host_definition']['description'])) |
239 | | - print(" Rules:") |
240 | | - for rule in group['rules']: |
241 | | - print(" {0}".format(rule['protocol']), end='') |
242 | | - if rule['from_port'] == rule['to_port']: |
243 | | - print("port {0} allows".format(rule['from_port']), end='') |
244 | | - else: |
245 | | - print("ports {0}-{1} allow".format(rule['from_port'], |
246 | | - rule['to_port']), end='') |
247 | | - print(rule['rule']) |
248 | | - print('') |
249 | | - return |
250 | | - |
251 | | - elif args[0] == "add": |
252 | | - if len(args) < 3: |
253 | | - print("Usage: stacks access_rules add STACK_NAME GROUP_NAME") |
254 | | - return |
255 | | - |
256 | | - stack_id = self.stacks.get_stack_id(args[1]) |
257 | | - group_id = self.stacks.get_access_rule_id(stack_id, args[2]) |
258 | | - |
259 | | - protocol = raw_input("Protocol (tcp, udp, or icmp): ") |
260 | | - from_port = raw_input("From port: ") |
261 | | - to_port = raw_input("To port: ") |
262 | | - rule = raw_input("Rule (IP address or group name): ") |
263 | | - |
264 | | - data = { |
265 | | - "action": "authorize", |
266 | | - "protocol": protocol, |
267 | | - "from_port": from_port, |
268 | | - "to_port": to_port, |
269 | | - "rule": rule |
270 | | - } |
271 | | - |
272 | | - self.stacks.edit_access_rule(group_id, data) |
273 | | - |
274 | | - elif args[0] == "delete": |
275 | | - if len(args) < 3: |
276 | | - print("Usage: stacks access_rules delete STACK_NAME GROUP_NAME") |
277 | | - return |
278 | | - |
279 | | - stack_id = self.stacks.get_stack_id(args[1]) |
280 | | - group_id = self.stacks.get_access_rule_id(stack_id, args[2]) |
281 | | - |
282 | | - index = 0 |
283 | | - |
284 | | - rules = self.stacks.list_rules_for_group(group_id) |
285 | | - |
286 | | - print('') |
287 | | - for rule in rules: |
288 | | - print("{0}) {1}".format(index, rule['protocol']), end='') |
289 | | - if rule['from_port'] == rule['to_port']: |
290 | | - print("port {0} allows".format(rule['from_port']), end='') |
291 | | - else: |
292 | | - print("ports {0}-{1} allow".format(rule['from_port'], rule['to_port']), end='') |
293 | | - print(rule['rule']) |
294 | | - index += 1 |
295 | | - print('') |
296 | | - delete_index = int(raw_input("Enter the index of the rule to delete: ")) |
297 | | - |
298 | | - data = rules[delete_index] |
299 | | - data['from_port'] = int(data['from_port']) |
300 | | - data['to_port'] = int(data['to_port']) |
301 | | - data['action'] = "revoke" |
302 | | - |
303 | | - self.stacks.edit_access_rule(group_id, data) |
304 | | - |
305 | | - print('') |
306 | | - |
307 | | - args[0] = "list" |
308 | | - |
309 | | - self._stack_access_rules(args) |
0 commit comments