def test_value_diff(self): current = {"cat": "meow", "dog": "woof", "horse": ["neigh", "whinny"]} new_values = {"dog": "bark", "horse": "snort"} original = copy.deepcopy(current) backup = utils.value_diff(current, new_values) self.assertEqual({"dog": "woof", "horse": ["neigh", "whinny"]}, backup) # current is unchanged self.assertEqual(original, current)
def add_cluster_templates(ctx, clusters, ng_dict): '''Add cluster templates to the database. The value of any node_group_template_id fields in cluster templates which reference a node group template in ng_dict by name will be changed to the id of the node group template. If there is an error in creating or updating a template, any templates that have already been created will be delete and any updates will be reversed. :param clusters: a list of dictionaries. Each dictionary has a "template" entry holding the cluster template and a "path" entry holding the path of the file from which the template was read. :param ng_dict: a dictionary of node group template ids keyed by node group template names ''' error = False created = [] updated = [] def do_reversals(created, updated): reverse_cluster_template_updates(ctx, updated) reverse_cluster_template_creates(ctx, created) return True try: for cl in clusters: template = cl['template'] # Fix up node_group_template_id fields u.substitute_ng_ids(template, ng_dict) # Name + tenant_id is unique, so search by name current = u.find_cluster_template_by_name(ctx, template['name']) if current: # Track what we see in the current template that is different # from our update values. Save it for possible rollback. # Note, this is not perfect because it does not recurse through # nested structures to get an exact diff, but it ensures that # we track only fields that are valid in the JSON schema updated_fields = u.value_diff(current.to_dict(), template) # Always attempt to update. Since the template value is a # combination of JSON and config values, there is no useful # timestamp we can use to skip an update. # If sqlalchemy determines no change in fields, it will not # mark it as updated. # TODO(tmckay): why when I change the count in an # entry in node_groups does it not count as an update? # Probably a bug try: template = conductor.API.cluster_template_update( ctx, current['id'], template, ignore_default=True) except Exception as e: LOG.warning(_LW("Update of cluster template {info} " "failed, {reason}").format( info=u.name_and_id(current), reason=e)) raise Handled() if template['updated_at'] != current['updated_at']: updated.append((template, updated_fields)) LOG.info(_LI("Updated cluster template {info} " "from {path}").format( info=u.name_and_id(template), path=cl['path'])) else: LOG.debug("No change to cluster template {info} " "from {path}".format(info=u.name_and_id(current), path=cl["path"])) else: template["is_default"] = True try: template = conductor.API.cluster_template_create(ctx, template) except Exception as e: LOG.warning(_LW("Creation of cluster template " "from {path} failed, {reason}").format( path=cl['path'], reason=e)) raise Handled() created.append(template) LOG.info(_LI("Created cluster template {info} " "from {path}").format(info=u.name_and_id(template), path=cl['path'])) except Handled: error = do_reversals(created, updated) except Exception as e: LOG.warning(_LW("Unhandled exception while processing " "cluster templates, {reason}").format(reason=e)) error = do_reversals(created, updated) return error
def add_node_group_templates(ctx, node_groups): error = False ng_info = {"ids": {}, "created": [], "updated": []} def do_reversals(ng_info): reverse_node_group_template_updates(ctx, ng_info["updated"]) reverse_node_group_template_creates(ctx, ng_info["created"]) return {}, True try: for ng in node_groups: template = ng['template'] current = u.find_node_group_template_by_name(ctx, template['name']) if current: # Track what we see in the current template that is different # from our update values. Save it for possible rollback. # Note, this is not perfect because it does not recurse through # nested structures to get an exact diff, but it ensures that # we track only fields that are valid in the JSON schema updated_fields = u.value_diff(current.to_dict(), template) # Always attempt to update. Since the template value is a # combination of JSON and config values, there is no useful # timestamp we can use to skip an update. # If sqlalchemy determines no change in fields, it will not # mark it as updated. try: template = conductor.API.node_group_template_update( ctx, current['id'], template, ignore_default=True) except Exception as e: LOG.warning(_LW("Update of node group template {info} " "failed, {reason}").format( info=u.name_and_id(current), reason=e)) raise Handled() if template['updated_at'] != current['updated_at']: ng_info["updated"].append((template, updated_fields)) LOG.info(_LI("Updated node group template {info} " "from {path}").format( info=u.name_and_id(template), path=ng["path"])) else: LOG.debug("No change to node group template {info} " "from {path}".format( info=u.name_and_id(current), path=ng['path'])) else: template['is_default'] = True try: template = conductor.API.node_group_template_create( ctx, template) except Exception as e: LOG.warning(_LW("Creation of node group template " "from {path} failed, {reason}").format( path=ng['path'], reason=e)) raise Handled() ng_info["created"].append(template) LOG.info(_LI("Created node group template {info} " "from {path}").format(info=u.name_and_id(template), path=ng["path"])) # For the purposes of substituion we need a dict of id by name ng_info["ids"][template['name']] = template['id'] except Handled: ng_info, error = do_reversals(ng_info) except Exception as e: LOG.warning(_LW("Unhandled exception while processing " "node group templates, {reason}").format(reason=e)) ng_info, error = do_reversals(ng_info) return ng_info, error
def add_cluster_templates(ctx, clusters, ng_dict): '''Add cluster templates to the database. The value of any node_group_template_id fields in cluster templates which reference a node group template in ng_dict by name will be changed to the id of the node group template. If there is an error in creating or updating a template, any templates that have already been created will be delete and any updates will be reversed. :param clusters: a list of dictionaries. Each dictionary has a "template" entry holding the cluster template and a "path" entry holding the path of the file from which the template was read. :param ng_dict: a dictionary of node group template ids keyed by node group template names ''' error = False created = [] updated = [] def do_reversals(created, updated): reverse_cluster_template_updates(ctx, updated) reverse_cluster_template_creates(ctx, created) return True try: for cl in clusters: template = cl['template'] # Fix up node_group_template_id fields u.substitute_ng_ids(template, ng_dict) # Name + tenant_id is unique, so search by name current = u.find_cluster_template_by_name(ctx, template['name']) if current: # Track what we see in the current template that is different # from our update values. Save it for possible rollback. # Note, this is not perfect because it does not recurse through # nested structures to get an exact diff, but it ensures that # we track only fields that are valid in the JSON schema updated_fields = u.value_diff(current.to_dict(), template) # Always attempt to update. Since the template value is a # combination of JSON and config values, there is no useful # timestamp we can use to skip an update. # If sqlalchemy determines no change in fields, it will not # mark it as updated. # TODO(tmckay): why when I change the count in an # entry in node_groups does it not count as an update? # Probably a bug try: template = conductor.API.cluster_template_update( ctx, current['id'], template, ignore_prot_on_def=True) except Exception as e: LOG.warning( _LW("Update of cluster template {info} " "failed, {reason}").format( info=u.name_and_id(current), reason=e)) raise Handled() if template['updated_at'] != current['updated_at']: updated.append((template, updated_fields)) LOG.info( _LI("Updated cluster template {info} " "from {path}").format(info=u.name_and_id(template), path=cl['path'])) else: LOG.debug("No change to cluster template {info} " "from {path}".format(info=u.name_and_id(current), path=cl["path"])) else: template["is_default"] = True try: template = conductor.API.cluster_template_create( ctx, template) except Exception as e: LOG.warning( _LW("Creation of cluster template " "from {path} failed, {reason}").format( path=cl['path'], reason=e)) raise Handled() created.append(template) LOG.info( _LI("Created cluster template {info} " "from {path}").format(info=u.name_and_id(template), path=cl['path'])) except Handled: error = do_reversals(created, updated) except Exception as e: LOG.warning( _LW("Unhandled exception while processing " "cluster templates, {reason}").format(reason=e)) error = do_reversals(created, updated) return error
def add_node_group_templates(ctx, node_groups): error = False ng_info = {"ids": {}, "created": [], "updated": []} def do_reversals(ng_info): reverse_node_group_template_updates(ctx, ng_info["updated"]) reverse_node_group_template_creates(ctx, ng_info["created"]) return {}, True try: for ng in node_groups: template = ng['template'] current = u.find_node_group_template_by_name(ctx, template['name']) if current: # Track what we see in the current template that is different # from our update values. Save it for possible rollback. # Note, this is not perfect because it does not recurse through # nested structures to get an exact diff, but it ensures that # we track only fields that are valid in the JSON schema updated_fields = u.value_diff(current.to_dict(), template) # Always attempt to update. Since the template value is a # combination of JSON and config values, there is no useful # timestamp we can use to skip an update. # If sqlalchemy determines no change in fields, it will not # mark it as updated. try: template = conductor.API.node_group_template_update( ctx, current['id'], template, ignore_prot_on_def=True) except Exception as e: LOG.warning( _LW("Update of node group template {info} " "failed, {reason}").format( info=u.name_and_id(current), reason=e)) raise Handled() if template['updated_at'] != current['updated_at']: ng_info["updated"].append((template, updated_fields)) LOG.info( _LI("Updated node group template {info} " "from {path}").format(info=u.name_and_id(template), path=ng["path"])) else: LOG.debug("No change to node group template {info} " "from {path}".format(info=u.name_and_id(current), path=ng['path'])) else: template['is_default'] = True try: template = conductor.API.node_group_template_create( ctx, template) except Exception as e: LOG.warning( _LW("Creation of node group template " "from {path} failed, {reason}").format( path=ng['path'], reason=e)) raise Handled() ng_info["created"].append(template) LOG.info( _LI("Created node group template {info} " "from {path}").format(info=u.name_and_id(template), path=ng["path"])) # For the purposes of substitution we need a dict of id by name ng_info["ids"][template['name']] = template['id'] except Handled: ng_info, error = do_reversals(ng_info) except Exception as e: LOG.warning( _LW("Unhandled exception while processing " "node group templates, {reason}").format(reason=e)) ng_info, error = do_reversals(ng_info) return ng_info, error