e857447879
This change adds a CLI called sahara-templates to manage default templates. Partial-implements: blueprint default-templates Change-Id: I4f30bd6bc378d90fda41b512c04afe8023d2b4b2
191 lines
7.1 KiB
Python
191 lines
7.1 KiB
Python
# Copyright 2015 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import copy
|
|
|
|
import six
|
|
|
|
from sahara import conductor
|
|
|
|
|
|
def name_and_id(template):
|
|
return "{name} ({id})".format(name=template["name"],
|
|
id=template["id"])
|
|
|
|
|
|
def is_node_group(template):
|
|
# Node group templates and cluster templates have
|
|
# different required fields in validation and neither
|
|
# allows additional fields. So, the presence of
|
|
# node_processes or flavor_id should suffice to
|
|
# identify a node group template. Check for both
|
|
# to be nice, in case someone made a typo.
|
|
return 'node_processes' in template or 'flavor_id' in template
|
|
|
|
|
|
def substitute_ng_ids(cl, ng_dict):
|
|
'''Substitute node group template ids for node group template names
|
|
|
|
If the cluster template contains node group elements with
|
|
node_group_template_id fields that reference node group templates
|
|
by name, substitute the node group template id for the name.
|
|
The name reference is expected to be a string containing a format
|
|
specifier of the form "{name}", for example "{master}"
|
|
|
|
:param cl: a cluster template
|
|
:param ng_dict: a dictionary of node group template ids keyed by
|
|
node group template names
|
|
'''
|
|
for ng in cl["node_groups"]:
|
|
if "node_group_template_id" in ng:
|
|
val = ng["node_group_template_id"].format(**ng_dict)
|
|
ng["node_group_template_id"] = val
|
|
|
|
|
|
def check_basic_fields(template):
|
|
return "plugin_name" in template and (
|
|
"hadoop_version" in template and (
|
|
"name" in template))
|
|
|
|
|
|
def check_plugin_version(template, plugin_versions):
|
|
'''Check that the template matches the plugin versions list
|
|
|
|
Tests whether or not the plugin version indicated by the template
|
|
matches one of the versions specified in plugin_versions
|
|
|
|
:param template: A node group or cluster template
|
|
:param plugin_versions: A list of plugin version strings. These
|
|
values may be regular version strings or may be
|
|
the name of the plugin followed by a
|
|
"." followed by a version string.
|
|
:returns: True if the plugin version specified in the template
|
|
matches a version in plugin_versions or plugin_versions
|
|
is an empty list. Otherwise False
|
|
'''
|
|
def dotted_name(template):
|
|
return template['plugin_name'] + "." + template['hadoop_version']
|
|
|
|
version_matches = plugin_versions is None or (
|
|
template['hadoop_version'] in plugin_versions) or (
|
|
dotted_name(template) in plugin_versions)
|
|
|
|
return version_matches
|
|
|
|
|
|
def check_plugin_name_and_version(template, plugin_names, plugin_versions):
|
|
'''Check that the template is for one of the specified plugins
|
|
|
|
Tests whether or not the plugin name and version indicated by the template
|
|
matches one of the names and one of the versions specified in
|
|
plugin_names and plugin_versions
|
|
|
|
:param template: A node group or cluster template
|
|
:param plugin_names: A list of plugin names
|
|
:param plugin_versions: A list of plugin version strings. These
|
|
values may be regular version strings or may be
|
|
the name of the plugin followed by a
|
|
"." followed by a version string.
|
|
:returns: True if the plugin name specified in the template matches
|
|
a name in plugin_names or plugin_names is an empty list, and if
|
|
the plugin version specified in the template matches a version
|
|
in plugin_versions or plugin_versions is an empty list.
|
|
Otherwise False
|
|
'''
|
|
name_and_version_matches = (plugin_names is None or (
|
|
template['plugin_name'] in plugin_names)) and (
|
|
check_plugin_version(template, plugin_versions))
|
|
|
|
return name_and_version_matches
|
|
|
|
|
|
# TODO(tmckay): refactor the service validation code so
|
|
# that the node group template usage checks there can be reused
|
|
# without incurring unnecessary dependencies
|
|
def check_node_group_template_usage(node_group_template_id,
|
|
cluster_list, cluster_template_list=[]):
|
|
cluster_users = []
|
|
template_users = []
|
|
|
|
for cluster in cluster_list:
|
|
if (node_group_template_id in
|
|
[node_group.node_group_template_id
|
|
for node_group in cluster.node_groups]):
|
|
cluster_users += [cluster.name]
|
|
|
|
for cluster_template in cluster_template_list:
|
|
if (node_group_template_id in
|
|
[node_group.node_group_template_id
|
|
for node_group in cluster_template.node_groups]):
|
|
template_users += [cluster_template.name]
|
|
|
|
return cluster_users, template_users
|
|
|
|
|
|
# TODO(tmckay): refactor the service validation code so
|
|
# that the cluster template usage checks there can be reused
|
|
# without incurring unnecessary dependencies
|
|
def check_cluster_template_usage(cluster_template_id, cluster_list):
|
|
cluster_users = []
|
|
for cluster in cluster_list:
|
|
if cluster_template_id == cluster.cluster_template_id:
|
|
cluster_users.append(cluster.name)
|
|
|
|
return cluster_users
|
|
|
|
|
|
def find_node_group_template_by_name(ctx, name):
|
|
t = conductor.API.node_group_template_get_all(ctx,
|
|
name=name,
|
|
is_default=True)
|
|
if t:
|
|
return t[0]
|
|
return None
|
|
|
|
|
|
def find_cluster_template_by_name(ctx, name):
|
|
t = conductor.API.cluster_template_get_all(ctx,
|
|
name=name,
|
|
is_default=True)
|
|
if t:
|
|
return t[0]
|
|
return None
|
|
|
|
|
|
def value_diff(current, new_values):
|
|
'''Return the entries in current that would be overwritten by new_values
|
|
|
|
Returns the set of entries in current that would be overwritten
|
|
if current.update(new_values) was called.
|
|
|
|
:param current: A dictionary whose key values are a superset
|
|
of the key values in new_values
|
|
:param new_values: A dictionary
|
|
'''
|
|
# Current is an existing template from the db and
|
|
# template is a set of values that has been validated
|
|
# against the JSON schema for the template.
|
|
# Copy items from current if they are present in template.
|
|
|
|
# In the case of "node_groups" the conductor does magic
|
|
# to set up template relations and insures that appropriate
|
|
# fields are cleaned (like "updated_at" and "id") so we
|
|
# trust the conductor in that case.
|
|
|
|
diff_values = {}
|
|
for k, v in six.iteritems(new_values):
|
|
if k in current and current[k] != v:
|
|
diff_values[k] = copy.deepcopy(current[k])
|
|
return diff_values
|