blob: c9c2127a22497aa3a136d90a4f5d0242355122df [file] [log] [blame]
Dan Shi784df0c2014-11-26 10:11:15 -08001# Copyright 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6The server module contains the objects and methods used to manage servers in
7Autotest.
8
9The valid actions are:
10list: list all servers in the database
11create: create a server
12delete: deletes a server
13modify: modify a server's role or status.
14
15The common options are:
16--role / -r: role that's related to server actions.
17
18See topic_common.py for a High Level Design and Algorithm.
19"""
20
21import common
22
23from autotest_lib.cli import action_common
Ningning Xia84190b82018-04-16 15:01:40 -070024from autotest_lib.cli import skylab_utils
Dan Shi784df0c2014-11-26 10:11:15 -080025from autotest_lib.cli import topic_common
26from autotest_lib.client.common_lib import error
Shuqian Zhaodb205af2018-02-28 15:13:03 -080027from autotest_lib.client.common_lib import global_config
Ningning Xia84190b82018-04-16 15:01:40 -070028from autotest_lib.client.common_lib import revision_control
Dan Shib9144a42014-12-01 16:09:32 -080029# The django setup is moved here as test_that uses sqlite setup. If this line
30# is in server_manager, test_that unittest will fail.
Dan Shi56f1ba72014-12-03 19:16:53 -080031from autotest_lib.frontend import setup_django_environment
Dan Shi784df0c2014-11-26 10:11:15 -080032from autotest_lib.site_utils import server_manager
Dan Shi56f1ba72014-12-03 19:16:53 -080033from autotest_lib.site_utils import server_manager_utils
Ningning Xiaa043aad2018-04-23 15:07:09 -070034from chromite.lib import gob_util
Ningning Xia9c188b92018-04-27 15:34:23 -070035
36try:
37 from skylab_inventory import text_manager
38 from skylab_inventory import translation_utils
39 from skylab_inventory.lib import server as skylab_server
40except ImportError:
41 pass
Ningning Xia84190b82018-04-16 15:01:40 -070042
43
Shuqian Zhaodb205af2018-02-28 15:13:03 -080044RESPECT_SKYLAB_SERVERDB = global_config.global_config.get_config_value(
45 'SKYLAB', 'respect_skylab_serverdb', type=bool, default=False)
46ATEST_DISABLE_MSG = ('Updating server_db via atest server command has been '
47 'disabled. Please use use go/cros-infra-inventory-tool '
48 'to update it in skylab inventory service.')
49
Dan Shi784df0c2014-11-26 10:11:15 -080050
51class server(topic_common.atest):
52 """Server class
53
54 atest server [list|create|delete|modify] <options>
55 """
56 usage_action = '[list|create|delete|modify]'
57 topic = msg_topic = 'server'
58 msg_items = '<server>'
59
60 def __init__(self, hostname_required=True):
61 """Add to the parser the options common to all the server actions.
62
63 @param hostname_required: True to require the command has hostname
64 specified. Default is True.
65 """
66 super(server, self).__init__()
67
68 self.parser.add_option('-r', '--role',
69 help='Name of a role',
70 type='string',
71 default=None,
72 metavar='ROLE')
Dan Shi56f1ba72014-12-03 19:16:53 -080073 self.parser.add_option('-x', '--action',
74 help=('Set to True to apply actions when role '
75 'or status is changed, e.g., restart '
Ningning Xia3748b5f2018-05-21 16:33:08 -070076 'scheduler when a drone is removed. %s' %
77 skylab_utils.MSG_INVALID_IN_SKYLAB),
Dan Shi56f1ba72014-12-03 19:16:53 -080078 action='store_true',
79 default=False,
80 metavar='ACTION')
Dan Shi784df0c2014-11-26 10:11:15 -080081
Ningning Xia84190b82018-04-16 15:01:40 -070082 self.add_skylab_options()
83
Dan Shi784df0c2014-11-26 10:11:15 -080084 self.topic_parse_info = topic_common.item_parse_info(
85 attribute_name='hostname', use_leftover=True)
86
87 self.hostname_required = hostname_required
88
89
90 def parse(self):
91 """Parse command arguments.
92 """
93 role_info = topic_common.item_parse_info(attribute_name='role')
94 kwargs = {}
95 if self.hostname_required:
96 kwargs['req_items'] = 'hostname'
97 (options, leftover) = super(server, self).parse([role_info], **kwargs)
98 if options.web_server:
99 self.invalid_syntax('Server actions will access server database '
100 'defined in your local global config. It does '
101 'not rely on RPC, no autotest server needs to '
102 'be specified.')
103
104 # self.hostname is a list. Action on server only needs one hostname at
105 # most.
106 if ((not self.hostname and self.hostname_required) or
107 len(self.hostname) > 1):
108 self.invalid_syntax('`server` topic can only manipulate 1 server. '
109 'Use -h to see available options.')
110 if self.hostname:
111 # Override self.hostname with the first hostname in the list.
112 self.hostname = self.hostname[0]
113 self.role = options.role
Ningning Xia84190b82018-04-16 15:01:40 -0700114
115 if self.skylab and self.role:
116 translation_utils.validate_server_role(self.role)
117
Dan Shi784df0c2014-11-26 10:11:15 -0800118 return (options, leftover)
119
120
121 def output(self, results):
122 """Display output.
123
124 For most actions, the return is a string message, no formating needed.
125
126 @param results: return of the execute call.
127 """
128 print results
129
130
131class server_help(server):
132 """Just here to get the atest logic working. Usage is set by its parent.
133 """
134 pass
135
136
137class server_list(action_common.atest_list, server):
138 """atest server list [--role <role>]"""
139
140 def __init__(self):
141 """Initializer.
142 """
143 super(server_list, self).__init__(hostname_required=False)
Ningning Xia84190b82018-04-16 15:01:40 -0700144
Dan Shi784df0c2014-11-26 10:11:15 -0800145 self.parser.add_option('-t', '--table',
146 help=('List details of all servers in a table, '
147 'e.g., \tHostname | Status | Roles | '
148 'note\t\tserver1 | primary | scheduler | '
Ningning Xia3748b5f2018-05-21 16:33:08 -0700149 'lab. %s' %
150 skylab_utils.MSG_INVALID_IN_SKYLAB),
Dan Shi784df0c2014-11-26 10:11:15 -0800151 action='store_true',
Allen Lica17e7c2016-10-27 15:37:17 -0700152 default=False)
Dan Shi784df0c2014-11-26 10:11:15 -0800153 self.parser.add_option('-s', '--status',
Ningning Xia84190b82018-04-16 15:01:40 -0700154 help='Only show servers with given status.',
Dan Shi784df0c2014-11-26 10:11:15 -0800155 type='string',
156 default=None,
157 metavar='STATUS')
158 self.parser.add_option('-u', '--summary',
159 help=('Show the summary of roles and status '
160 'only, e.g.,\tscheduler: server1(primary) '
161 'server2(backup)\t\tdrone: server3(primary'
Ningning Xia84190b82018-04-16 15:01:40 -0700162 ') server4(backup). %s' %
Ningning Xia3748b5f2018-05-21 16:33:08 -0700163 skylab_utils.MSG_INVALID_IN_SKYLAB),
Dan Shi784df0c2014-11-26 10:11:15 -0800164 action='store_true',
Allen Lica17e7c2016-10-27 15:37:17 -0700165 default=False)
166 self.parser.add_option('--json',
Ningning Xia84190b82018-04-16 15:01:40 -0700167 help=('Format output as JSON. %s' %
Ningning Xia3748b5f2018-05-21 16:33:08 -0700168 skylab_utils.MSG_INVALID_IN_SKYLAB),
Allen Lica17e7c2016-10-27 15:37:17 -0700169 action='store_true',
170 default=False)
Aviv Keshete1729bb2017-05-31 13:27:09 -0700171 self.parser.add_option('-N', '--hostnames-only',
Ningning Xia3748b5f2018-05-21 16:33:08 -0700172 help=('Only return hostnames.'),
Aviv Keshete1729bb2017-05-31 13:27:09 -0700173 action='store_true',
174 default=False)
Dan Shi784df0c2014-11-26 10:11:15 -0800175
176
177 def parse(self):
178 """Parse command arguments.
179 """
180 (options, leftover) = super(server_list, self).parse()
Allen Lica17e7c2016-10-27 15:37:17 -0700181 self.json = options.json
Dan Shi784df0c2014-11-26 10:11:15 -0800182 self.table = options.table
183 self.status = options.status
184 self.summary = options.summary
Aviv Keshete1729bb2017-05-31 13:27:09 -0700185 self.namesonly = options.hostnames_only
Ningning Xia84190b82018-04-16 15:01:40 -0700186
187 # TODO(nxia): support all formats for skylab inventory.
188 if (self.skylab and (self.json or self.table or self.summary)):
Ningning Xia3748b5f2018-05-21 16:33:08 -0700189 self.invalid_syntax('The format (table|summary|json)'
Ningning Xia84190b82018-04-16 15:01:40 -0700190 ' is not supported with --skylab.')
191
Aviv Keshete1729bb2017-05-31 13:27:09 -0700192 if sum([self.table, self.summary, self.json, self.namesonly]) > 1:
193 self.invalid_syntax('May only specify up to 1 output-format flag.')
Dan Shi784df0c2014-11-26 10:11:15 -0800194 return (options, leftover)
195
196
197 def execute(self):
198 """Execute the command.
199
200 @return: A list of servers matched given hostname and role.
201 """
Ningning Xia84190b82018-04-16 15:01:40 -0700202 if self.skylab:
203 try:
204 inventory_repo = skylab_utils.InventoryRepo(
205 self.inventory_repo_dir)
206 inventory_repo.initialize()
207 infrastructure = text_manager.load_infrastructure(
Ningning Xiae8714052018-04-30 18:58:54 -0700208 inventory_repo.get_data_dir())
Ningning Xia84190b82018-04-16 15:01:40 -0700209
210 return skylab_server.get_servers(
211 infrastructure,
Ningning Xiae8714052018-04-30 18:58:54 -0700212 self.environment,
Ningning Xia84190b82018-04-16 15:01:40 -0700213 hostname=self.hostname,
214 role=self.role,
215 status=self.status)
216 except (skylab_server.SkylabServerActionError,
217 revision_control.GitError) as e:
218 self.failure(e, what_failed='Failed to list servers from skylab'
219 ' inventory.', item=self.hostname, fatal=True)
220 else:
221 try:
222 return server_manager_utils.get_servers(
223 hostname=self.hostname,
224 role=self.role,
225 status=self.status)
226 except (server_manager_utils.ServerActionError,
227 error.InvalidDataError) as e:
228 self.failure(e, what_failed='Failed to find servers',
229 item=self.hostname, fatal=True)
Dan Shi784df0c2014-11-26 10:11:15 -0800230
231
232 def output(self, results):
233 """Display output.
234
235 @param results: return of the execute call, a list of server object that
236 contains server information.
237 """
Allen Li90a84ea2016-10-27 15:07:42 -0700238 if results:
Allen Lica17e7c2016-10-27 15:37:17 -0700239 if self.json:
240 formatter = server_manager_utils.format_servers_json
241 elif self.table:
Allen Li90a84ea2016-10-27 15:07:42 -0700242 formatter = server_manager_utils.format_servers_table
243 elif self.summary:
244 formatter = server_manager_utils.format_servers_summary
Aviv Keshete1729bb2017-05-31 13:27:09 -0700245 elif self.namesonly:
246 formatter = server_manager_utils.format_servers_nameonly
Allen Li90a84ea2016-10-27 15:07:42 -0700247 else:
248 formatter = server_manager_utils.format_servers
249 print formatter(results)
250 else:
Dan Shi784df0c2014-11-26 10:11:15 -0800251 self.failure('No server is found.',
252 what_failed='Failed to find servers',
253 item=self.hostname, fatal=True)
Dan Shi784df0c2014-11-26 10:11:15 -0800254
255
256class server_create(server):
257 """atest server create hostname --role <role> --note <note>
258 """
259
260 def __init__(self):
261 """Initializer.
262 """
263 super(server_create, self).__init__()
264 self.parser.add_option('-n', '--note',
265 help='note of the server',
266 type='string',
267 default=None,
268 metavar='NOTE')
269
270
271 def parse(self):
272 """Parse command arguments.
273 """
274 (options, leftover) = super(server_create, self).parse()
275 self.note = options.note
276
277 if not self.role:
278 self.invalid_syntax('--role is required to create a server.')
279
280 return (options, leftover)
281
282
Ningning Xia84190b82018-04-16 15:01:40 -0700283 def execute_skylab(self):
284 """Execute the command for skylab inventory changes."""
285 inventory_repo = skylab_utils.InventoryRepo(
286 self.inventory_repo_dir)
287 inventory_repo.initialize()
288 data_dir = inventory_repo.get_data_dir()
Ningning Xiae8714052018-04-30 18:58:54 -0700289 infrastructure = text_manager.load_infrastructure(data_dir)
Ningning Xia84190b82018-04-16 15:01:40 -0700290
291 new_server = skylab_server.create(
292 infrastructure,
293 self.hostname,
294 self.environment,
295 role=self.role,
296 note=self.note)
Ningning Xiae8714052018-04-30 18:58:54 -0700297 text_manager.dump_infrastructure(data_dir, infrastructure)
Ningning Xia84190b82018-04-16 15:01:40 -0700298
299 message = skylab_utils.construct_commit_message(
300 'Add new server: %s' % self.hostname)
Ningning Xiaa043aad2018-04-23 15:07:09 -0700301 self.change_number = inventory_repo.upload_change(
302 message, draft=self.draft, dryrun=self.dryrun,
303 submit=self.submit)
Ningning Xia84190b82018-04-16 15:01:40 -0700304
305 return new_server
306
307
Dan Shi784df0c2014-11-26 10:11:15 -0800308 def execute(self):
309 """Execute the command.
310
311 @return: A Server object if it is created successfully.
312 """
Shuqian Zhaodb205af2018-02-28 15:13:03 -0800313 if RESPECT_SKYLAB_SERVERDB:
314 self.failure(ATEST_DISABLE_MSG,
315 what_failed='Failed to create server',
316 item=self.hostname, fatal=True)
317
Ningning Xia84190b82018-04-16 15:01:40 -0700318 if self.skylab:
319 try:
320 return self.execute_skylab()
321 except (skylab_server.SkylabServerActionError,
Ningning Xiaa043aad2018-04-23 15:07:09 -0700322 revision_control.GitError,
323 gob_util.GOBError) as e:
Ningning Xiae8714052018-04-30 18:58:54 -0700324 self.failure(e, what_failed='Failed to create server in skylab '
Ningning Xia84190b82018-04-16 15:01:40 -0700325 'inventory.', item=self.hostname, fatal=True)
326 else:
327 try:
328 return server_manager.create(
329 hostname=self.hostname,
330 role=self.role,
331 note=self.note)
332 except (server_manager_utils.ServerActionError,
333 error.InvalidDataError) as e:
334 self.failure(e, what_failed='Failed to create server',
335 item=self.hostname, fatal=True)
Dan Shi784df0c2014-11-26 10:11:15 -0800336
337
338 def output(self, results):
339 """Display output.
340
341 @param results: return of the execute call, a server object that
342 contains server information.
343 """
344 if results:
Ningning Xia84190b82018-04-16 15:01:40 -0700345 print 'Server %s is added.\n' % self.hostname
Dan Shi784df0c2014-11-26 10:11:15 -0800346 print results
347
Ningning Xiaa043aad2018-04-23 15:07:09 -0700348 if self.skylab and not self.dryrun and not self.submit:
Ningning Xiaef35cb52018-05-04 17:58:20 -0700349 print skylab_utils.get_cl_message(self.change_number)
Ningning Xiaa043aad2018-04-23 15:07:09 -0700350
Ningning Xia84190b82018-04-16 15:01:40 -0700351
Dan Shi784df0c2014-11-26 10:11:15 -0800352
353class server_delete(server):
354 """atest server delete hostname"""
355
Ningning Xia84190b82018-04-16 15:01:40 -0700356 def execute_skylab(self):
357 """Execute the command for skylab inventory changes."""
358 inventory_repo = skylab_utils.InventoryRepo(
359 self.inventory_repo_dir)
360 inventory_repo.initialize()
361 data_dir = inventory_repo.get_data_dir()
Ningning Xiae8714052018-04-30 18:58:54 -0700362 infrastructure = text_manager.load_infrastructure(data_dir)
Ningning Xia84190b82018-04-16 15:01:40 -0700363
Ningning Xiae8714052018-04-30 18:58:54 -0700364 skylab_server.delete(infrastructure, self.hostname, self.environment)
365 text_manager.dump_infrastructure(data_dir, infrastructure)
Ningning Xia84190b82018-04-16 15:01:40 -0700366
367 message = skylab_utils.construct_commit_message(
368 'Delete server: %s' % self.hostname)
Ningning Xiaa043aad2018-04-23 15:07:09 -0700369 self.change_number = inventory_repo.upload_change(
370 message, draft=self.draft, dryrun=self.dryrun,
371 submit=self.submit)
Ningning Xia84190b82018-04-16 15:01:40 -0700372
373
Dan Shi784df0c2014-11-26 10:11:15 -0800374 def execute(self):
375 """Execute the command.
376
377 @return: True if server is deleted successfully.
378 """
Shuqian Zhaodb205af2018-02-28 15:13:03 -0800379 if RESPECT_SKYLAB_SERVERDB:
380 self.failure(ATEST_DISABLE_MSG,
381 what_failed='Failed to delete server',
382 item=self.hostname, fatal=True)
383
Ningning Xia84190b82018-04-16 15:01:40 -0700384 if self.skylab:
385 try:
386 self.execute_skylab()
387 return True
388 except (skylab_server.SkylabServerActionError,
Ningning Xiaa043aad2018-04-23 15:07:09 -0700389 revision_control.GitError,
390 gob_util.GOBError) as e:
Ningning Xia84190b82018-04-16 15:01:40 -0700391 self.failure(e, what_failed='Failed to delete server from '
392 'skylab inventory.', item=self.hostname,
393 fatal=True)
394 else:
395 try:
396 server_manager.delete(hostname=self.hostname)
397 return True
398 except (server_manager_utils.ServerActionError,
399 error.InvalidDataError) as e:
400 self.failure(e, what_failed='Failed to delete server',
401 item=self.hostname, fatal=True)
Dan Shi784df0c2014-11-26 10:11:15 -0800402
403
404 def output(self, results):
405 """Display output.
406
407 @param results: return of the execute call.
408 """
409 if results:
Ningning Xia84190b82018-04-16 15:01:40 -0700410 print ('Server %s is deleted.\n' %
Dan Shi784df0c2014-11-26 10:11:15 -0800411 self.hostname)
412
Ningning Xiaa043aad2018-04-23 15:07:09 -0700413 if self.skylab and not self.dryrun and not self.submit:
Ningning Xiaef35cb52018-05-04 17:58:20 -0700414 print skylab_utils.get_cl_message(self.change_number)
Ningning Xiaa043aad2018-04-23 15:07:09 -0700415
Ningning Xia84190b82018-04-16 15:01:40 -0700416
Dan Shi784df0c2014-11-26 10:11:15 -0800417
418class server_modify(server):
419 """atest server modify hostname
420
421 modify action can only change one input at a time. Available inputs are:
422 --status: Status of the server.
423 --note: Note of the server.
424 --role: New role to be added to the server.
425 --delete_role: Existing role to be deleted from the server.
426 """
427
428 def __init__(self):
429 """Initializer.
430 """
431 super(server_modify, self).__init__()
432 self.parser.add_option('-s', '--status',
433 help='Status of the server',
434 type='string',
435 metavar='STATUS')
436 self.parser.add_option('-n', '--note',
437 help='Note of the server',
438 type='string',
439 default=None,
440 metavar='NOTE')
441 self.parser.add_option('-d', '--delete',
442 help=('Set to True to delete given role.'),
443 action='store_true',
444 default=False,
445 metavar='DELETE')
446 self.parser.add_option('-a', '--attribute',
447 help='Name of the attribute of the server',
448 type='string',
449 default=None,
450 metavar='ATTRIBUTE')
451 self.parser.add_option('-e', '--value',
452 help='Value for the attribute of the server',
453 type='string',
454 default=None,
455 metavar='VALUE')
456
457
458 def parse(self):
459 """Parse command arguments.
460 """
461 (options, leftover) = super(server_modify, self).parse()
462 self.status = options.status
463 self.note = options.note
464 self.delete = options.delete
465 self.attribute = options.attribute
466 self.value = options.value
Dan Shi56f1ba72014-12-03 19:16:53 -0800467 self.action = options.action
Dan Shi784df0c2014-11-26 10:11:15 -0800468
469 # modify supports various options. However, it's safer to limit one
470 # option at a time so no complicated role-dependent logic is needed
471 # to handle scenario that both role and status are changed.
472 # self.parser is optparse, which does not have function in argparse like
473 # add_mutually_exclusive_group. That's why the count is used here.
474 flags = [self.status is not None, self.role is not None,
475 self.attribute is not None, self.note is not None]
476 if flags.count(True) != 1:
477 msg = ('Action modify only support one option at a time. You can '
478 'try one of following 5 options:\n'
479 '1. --status: Change server\'s status.\n'
480 '2. --note: Change server\'s note.\n'
481 '3. --role with optional -d: Add/delete role from server.\n'
482 '4. --attribute --value: Set/change the value of a '
483 'server\'s attribute.\n'
484 '5. --attribute -d: Delete the attribute from the '
485 'server.\n'
486 '\nUse option -h to see a complete list of options.')
487 self.invalid_syntax(msg)
488 if (self.status != None or self.note != None) and self.delete:
489 self.invalid_syntax('--delete does not apply to status or note.')
490 if self.attribute != None and not self.delete and self.value == None:
491 self.invalid_syntax('--attribute must be used with option --value '
492 'or --delete.')
Ningning Xia84190b82018-04-16 15:01:40 -0700493
494 # TODO(nxia): crbug.com/832964 support --action with --skylab
495 if self.skylab and self.action:
496 self.invalid_syntax('--action is currently not supported with'
497 ' --skylab.')
498
Dan Shi784df0c2014-11-26 10:11:15 -0800499 return (options, leftover)
500
501
Ningning Xia84190b82018-04-16 15:01:40 -0700502 def execute_skylab(self):
503 """Execute the command for skylab inventory changes."""
504 inventory_repo = skylab_utils.InventoryRepo(
505 self.inventory_repo_dir)
506 inventory_repo.initialize()
507 data_dir = inventory_repo.get_data_dir()
Ningning Xiae8714052018-04-30 18:58:54 -0700508 infrastructure = text_manager.load_infrastructure(data_dir)
Ningning Xia84190b82018-04-16 15:01:40 -0700509
510 target_server = skylab_server.modify(
511 infrastructure,
512 self.hostname,
Ningning Xiae8714052018-04-30 18:58:54 -0700513 self.environment,
Ningning Xia84190b82018-04-16 15:01:40 -0700514 role=self.role,
515 status=self.status,
516 delete_role=self.delete,
517 note=self.note,
518 attribute=self.attribute,
519 value=self.value,
520 delete_attribute=self.delete)
Ningning Xiae8714052018-04-30 18:58:54 -0700521 text_manager.dump_infrastructure(data_dir, infrastructure)
Ningning Xia84190b82018-04-16 15:01:40 -0700522
523 status = inventory_repo.git_repo.status()
524 if not status:
525 print('Nothing is changed for server %s.' % self.hostname)
526 return
527
528 message = skylab_utils.construct_commit_message(
529 'Modify server: %s' % self.hostname)
Ningning Xiaa043aad2018-04-23 15:07:09 -0700530 self.change_number = inventory_repo.upload_change(
531 message, draft=self.draft, dryrun=self.dryrun,
532 submit=self.submit)
Ningning Xia84190b82018-04-16 15:01:40 -0700533
534 return target_server
535
536
Dan Shi784df0c2014-11-26 10:11:15 -0800537 def execute(self):
538 """Execute the command.
539
540 @return: The updated server object if it is modified successfully.
541 """
Shuqian Zhaodb205af2018-02-28 15:13:03 -0800542 if RESPECT_SKYLAB_SERVERDB:
543 self.failure(ATEST_DISABLE_MSG,
544 what_failed='Failed to modify server',
545 item=self.hostname, fatal=True)
546
Ningning Xia84190b82018-04-16 15:01:40 -0700547 if self.skylab:
548 try:
549 return self.execute_skylab()
550 except (skylab_server.SkylabServerActionError,
Ningning Xiaa043aad2018-04-23 15:07:09 -0700551 revision_control.GitError,
552 gob_util.GOBError) as e:
Ningning Xia84190b82018-04-16 15:01:40 -0700553 self.failure(e, what_failed='Failed to modify server in skylab'
554 ' inventory.', item=self.hostname, fatal=True)
555 else:
556 try:
557 return server_manager.modify(
558 hostname=self.hostname, role=self.role,
559 status=self.status, delete=self.delete,
560 note=self.note, attribute=self.attribute,
561 value=self.value, action=self.action)
562 except (server_manager_utils.ServerActionError,
563 error.InvalidDataError) as e:
564 self.failure(e, what_failed='Failed to modify server',
565 item=self.hostname, fatal=True)
Dan Shi784df0c2014-11-26 10:11:15 -0800566
567
568 def output(self, results):
569 """Display output.
570
571 @param results: return of the execute call, which is the updated server
572 object.
573 """
574 if results:
Ningning Xia84190b82018-04-16 15:01:40 -0700575 print 'Server %s is modified.\n' % self.hostname
Dan Shi784df0c2014-11-26 10:11:15 -0800576 print results
Ningning Xia84190b82018-04-16 15:01:40 -0700577
Ningning Xiaa043aad2018-04-23 15:07:09 -0700578 if self.skylab and not self.dryrun and not self.submit:
Ningning Xiaef35cb52018-05-04 17:58:20 -0700579 print skylab_utils.get_cl_message(self.change_number)