pymtt
 All Classes Namespaces Files Functions Variables Groups
WWulf3.py
Go to the documentation of this file.
1 # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: f; python-indent: 4 -*-
2 #
3 # Copyright (c) 2015-2018 Intel, Inc. All rights reserved.
4 # $COPYRIGHT$
5 #
6 # Additional copyrights may follow
7 #
8 # $HEADER$
9 #
10 
11 from __future__ import print_function
12 from ProvisionMTTStage import *
13 import shlex
14 
15 ## @addtogroup Stages
16 # @{
17 # @addtogroup Provision
18 # @section WWulf3
19 # Plugin for provisioning nodes using the Warewulf v3 image manager
20 # @param target List of remote host names or LAN interfaces to be provisioned
21 # @param image Name of image to be instantiated
22 # @param bootstrap Name of bootstrap to be used
23 # @param controller List of IP addresses of remote node controllers/BMCs
24 # @param username Remote controller username
25 # @param password Remote controller password
26 # @param pwfile File containing remote controller password
27 # @param sudo Use sudo to execute privileged commands
28 # @param allocate_cmd Command to use for allocating nodes from the resource manager
29 # @param deallocate_cmd Command to use for deallocating nodes from the resource manager
30 # @}
32 
33  def __init__(self):
34  # initialise parent class
35  ProvisionMTTStage.__init__(self)
36  self.options = {}
37  self.options['target'] = (None, "List of remote host names or LAN interfaces to be provisioned")
38  self.options['image'] = (None, "Name of image to be instantiated")
39  self.options['bootstrap'] = (None, "Name of bootstrap to be used")
40  self.options['controller'] = (None, "List of IP addresses of remote node controllers/BMCs")
41  self.options['username'] = (None, "Remote controller username")
42  self.options['password'] = (None, "Remote controller password")
43  self.options['pwfile'] = (None, "File containing remote controller password")
44  self.options['sudo'] = (False, "Use sudo to execute privileged commands")
45  self.options['allocate_cmd'] = (None, "Command to use for allocating nodes from the resource manager")
46  self.options['deallocate_cmd'] = (None, "Command to use for deallocating nodes from the resource manager")
47 
48  self.allocated = False
49  self.testDef = None
50  self.cmds = None
51  return
52 
53 
54  def activate(self):
55  # use the automatic procedure from IPlugin
56  IPlugin.activate(self)
57  return
58 
59 
60  def deactivate(self):
61  IPlugin.deactivate(self)
62  if self.allocated and self.testDef and self.cmds:
63  self.deallocate({}, self.cmds, self.testDef)
64 
65  def print_name(self):
66  return "WWulf3"
67 
68 
69  def print_options(self, testDef, prefix):
70  lines = testDef.printOptions(self.options)
71  for line in lines:
72  print(prefix + line)
73  return
74 
75  def allocate(self, log, cmds, testDef):
76  self.allocated = False
77  if cmds['allocate_cmd'] is not None and cmds['deallocate_cmd'] is not None:
78  allocate_cmdargs = shlex.split(cmds['allocate_cmd'])
79  status,stdout,stderr,time = testDef.execmd.execute(cmds, allocate_cmdargs, testDef)
80  if 0 != status:
81  log['status'] = status
82  if log['stderr']:
83  log['stderr'].extend(stderr)
84  else:
85  log['stderr'] = stderr
86  return False
87  self.allocated = True
88  return True
89 
90  def deallocate(self, log, cmds, testDef):
91  if cmds['allocate_cmd'] is not None and cmds['deallocate_cmd'] is not None and self.allocated == True:
92  deallocate_cmdargs = shlex.split(cmds['deallocate_cmd'])
93  status,stdout,stderr,time = testDef.execmd.execute(cmds, deallocate_cmdargs, testDef)
94  if 0 != status:
95  log['status'] = status
96  if log['stderr']:
97  log['stderr'].extend(stderr)
98  else:
99  log['stderr'] = stderr
100  return False
101  self.allocated = False
102  return True
103 
104  def execute(self, log, keyvals, testDef):
105  testDef.logger.verbose_print("Warewulf 3 Provisioner")
106  # parse what we were given against our defined options
107  cmds = {}
108  testDef.parseOptions(log, self.options, keyvals, cmds)
109 
110  mylog = {}
111  if cmds['target']:
112  mylog['target'] = cmds['target']
113  if cmds['image']:
114  mylog['image'] = cmds['image']
115  if cmds['controller']:
116  mylog['controller'] = cmds['controller']
117  if cmds['bootstrap']:
118  mylog['bootstrap'] = cmds['bootstrap']
119  log['provisioning'] = mylog
120 
121  # they had to at least give us one target node and controller
122  try:
123  if cmds['target'] is None:
124  log['status'] = 1
125  log['stderr'] = "No target hosts identified"
126  return
127  else:
128  # convert to a list
129  targets = cmds['target'].split(',')
130  except:
131  log['status'] = 1
132  log['stderr'] = "No target hosts identified"
133  return
134  try:
135  if cmds['controller'] is None:
136  log['status'] = 1
137  log['stderr'] = "No target controllers identified"
138  return
139  else:
140  # convert to a list
141  controllers = cmds['controller'].split(',')
142  except:
143  log['status'] = 1
144  log['stderr'] = "No target controllers identified"
145  return
146  # must give us an image
147  try:
148  if cmds['image'] is None:
149  log['status'] = 1
150  log['stderr'] = "No image specified"
151  return
152  except:
153  log['status'] = 1
154  log['stderr'] = "No image specified"
155  return
156 
157  # sanity check the provided nodes to ensure they are in the
158  # database - output goes to stdout
159  wwcmd = ["wwsh", "node", "list"]
160  if cmds['sudo']:
161  wwcmd.insert(0, "sudo")
162  status,stdout,stderr,_ = testDef.execmd.execute(cmds, wwcmd, testDef)
163  if 0 != status or stdout is None:
164  log['status'] = status
165  log['stderr'] = "Node list was not obtained"
166  return
167  # skip first two lines as they are headers
168  del stdout[0:2]
169  # parse each line to collect out the individual nodes
170  nodes = []
171  for line in stdout:
172  # node name is at the front, ended by a space
173  nodes.append(line[0:line.find(' ')])
174  # now check that each target is in the list of nodes - no
175  # way around just a big double-loop, i fear
176  for tgt in targets:
177  found = False
178  for node in nodes:
179  if tgt == node:
180  found = True
181  break
182  if not found:
183  log['status'] = 1
184  log['stderr'] = "Target " + tgt + " is not included in Warewulf node table"
185  return
186 
187  # Allocate cluster
188  if False == self.allocate(log, cmds, testDef):
189  return
190 
191  # if we get here, then all the targets are known!
192  # so cycle thru the targets and update the provisioning
193  # database for each of them
194  for tgt in targets:
195  wwcmd = ["wwsh", "provision", "set"]
196  if cmds['sudo']:
197  wwcmd.insert(0, "sudo")
198  wwcmd.append(tgt)
199  wwcmd.append("--vnfs=" + cmds['image'])
200  if cmds['bootstrap']:
201  wwcmd.append("--bootstrap=" + cmds['bootstrap'])
202  # update the provisioning database to the new image
203  status,stdout,stderr,_ = testDef.execmd.execute(cmds, wwcmd, testDef)
204  if 0 != status:
205  log['status'] = status
206  log['stderr'] = stderr
207  self.deallocate(log, cmds, testDef)
208  return
209  # assemble command to power cycle each node. Note that
210  # we will be passing a set of keyvals down, so we can
211  # include directives such as 'sudo' there
212  ipmicmd = {}
213  ipmicmd['command'] = ["power", "cycle"]
214  ipmicmd['target'] = cmds['target']
215  ipmicmd['controller'] = cmds['controller']
216  ipmicmd['username'] = cmds['username']
217  ipmicmd['password'] = cmds['password']
218  ipmicmd['pwfile'] = cmds['pwfile']
219  ipmicmd['sudo'] = cmds['sudo']
220  # find the IPMITool plugin
221  # order the nodes to power cycle
222  ipmitool = testDef.selectPlugin("IPMITool", "tool")
223  if ipmitool is None:
224  log['status'] = 1
225  log['stderr'] = "IPMITool was not found"
226  self.deallocate(log, cmds, testDef)
227  return
228  # execute the request
229  ipmilog = {}
230  ipmitool.execute(ipmilog, ipmicmd, testDef)
231 
232  # update our results to reflect the overall status
233  log['status'] = ipmilog['status']
234  if ipmilog['stdout'] is not None:
235  log['stdout'] = ipmilog['stdout']
236  if ipmilog['stderr'] is not None:
237  log['stderr'] = ipmilog['stderr']
238 
239  # Deallocate cluster
240  self.deallocate(log, cmds, testDef)
241 
242  return
def __init__
Definition: WWulf3.py:33
def print_name
Definition: WWulf3.py:65
def print_options
Definition: WWulf3.py:69
def deactivate
Definition: WWulf3.py:60
def activate
Definition: WWulf3.py:54
def allocate
Definition: WWulf3.py:75
def execute
Definition: WWulf3.py:104
def deallocate
Definition: WWulf3.py:90