Multiple fixes for maas machine processing
- Strict object type check before skipping the node in case if it
is already in deployed state
- Implementing skipped list for machine processing
- Check for duplicate mac address from the maas nodes list
- Check if machine in machines list before assigning an ip
- Check if machine in machines list before deploying it
Prod-Related: PROD-35006
Change-Id: Ic7675563bac4205305d682bf8ee151305b73fb8a
diff --git a/_modules/maas.py b/_modules/maas.py
index 368507c..0152eba 100644
--- a/_modules/maas.py
+++ b/_modules/maas.py
@@ -100,10 +100,8 @@
None, **data).read()
def process(self, objects_name=None):
- # FIXME: probably, should be extended with "skipped" return.
- # For example, currently "DEPLOYED" nodes are skipped, and no changes
- # applied - but they fall into 'updated' list.
ret = {
+ 'skipped': [],
'success': [],
'errors': {},
'updated': [],
@@ -140,10 +138,11 @@
def process_single(name, config_data):
self._update = False
+ err_msgs = []
try:
data = self.fill_data(name, config_data, **extra)
if data is None:
- ret['updated'].append(name)
+ ret['skipped'].append(name)
return
if name in all_elements:
self._update = True
@@ -151,11 +150,24 @@
self.send(data)
ret['updated'].append(name)
else:
- self.send(data)
- ret['success'].append(name)
+ # Check for duplicate mac_addresses
+ skip_send = False
+ for _obj_name, _obj_val in all_elements.iteritems():
+ if isinstance(_obj_val, dict) and 'boot_interface' in _obj_val:
+ if isinstance(data, dict) and isinstance(data['mac_addresses'], (unicode, str)):
+ for mac_addr in data['mac_addresses'].split(','):
+ if mac_addr == _obj_val['boot_interface']['mac_address']:
+ err_msg = 'Error: %s uses same mac [%s] as node %s in maas' % (data['hostname'], mac_addr, _obj_val['fqdn'])
+ err_msgs.append(err_msg)
+ LOG.error(err_msg)
+ skip_send = True
+ # Decision
+ if skip_send:
+ ret['errors'][name] = ','.join(err_msgs)
+ else:
+ self.send(data)
+ ret['success'].append(name)
except urllib2.HTTPError as e:
- # FIXME add exception's for response:
- # '{"mode": ["Interface is already set to DHCP."]}
etxt = e.read()
LOG.error('Failed for object %s reason %s', name, etxt)
ret['errors'][name] = str(etxt)
@@ -172,8 +184,9 @@
process_single(object_name, config[object_name])
else:
for name, config_data in config.iteritems():
- if isinstance(config_data, dict) and 'status_name' in all_elements[name] and all_elements[name]['status_name'] == 'Deployed':
+ if isinstance(config_data, dict) and isinstance(all_elements, dict) and name in all_elements and 'status_name' in all_elements[name] and all_elements[name]['status_name'] == 'Deployed':
LOG.info('Machine %s already deployed, skipping it.' % name)
+ ret['skipped'].append(name)
else:
process_single(name, config_data)
@@ -625,7 +638,12 @@
raise Exception(str(e))
def fill_data(self, name, data, machines):
- machine = machines[name]
+ if name in machines:
+ machine = machines[name]
+ else:
+ LOG.debug("Skipping node:{} "
+ "since it is not in maas yet".format(name))
+ return
if machine['status'] == self.DEPLOYED:
LOG.debug("Skipping node:{} "
"since it in status:DEPLOYED".format(name))
@@ -671,7 +689,12 @@
None, 'hostname')}
def fill_data(self, name, machine_data, machines):
- machine = machines[name]
+ if name in machines:
+ machine = machines[name]
+ else:
+ LOG.debug("Skipping node:{} "
+ "since it is not in maas yet".format(name))
+ return
if machine['status'] == self.DEPLOYED:
return
if machine['status'] != self.READY: