blob: 34ea4b90338380b1d0316ab79471add589660078 [file] [log] [blame]
# -*- coding: utf-8 -*-
'''
Module for handling reclass metadata models.
'''
from __future__ import absolute_import
import logging
import os
import sys
import six
import yaml
import json
from reclass import get_storage, output
from reclass.core import Core
from reclass.config import find_and_read_configfile
LOG = logging.getLogger(__name__)
def __virtual__():
'''
Only load this module if reclass
is installed on this minion.
'''
return 'reclass'
def _get_nodes_dir():
defaults = find_and_read_configfile()
return os.path.join(defaults.get('inventory_base_uri'), 'nodes')
def _get_classes_dir():
defaults = find_and_read_configfile()
return os.path.join(defaults.get('inventory_base_uri'), 'classes')
def node_create(name, path=None, cluster="default", environment="prd", classes=None, parameters=None, **kwargs):
'''
Create a reclass node
:param name: new node FQDN
:param path: custom path in nodes for new node
:param classes: classes given to the new node
:param parameters: parameters given to the new node
:param environment: node's environment
:param cluster: node's cluster
CLI Examples:
.. code-block:: bash
salt '*' reclass.node_create server.domain.com classes=[system.neco1, system.neco2]
salt '*' reclass.node_create namespace/test enabled=False
'''
ret = {}
node = node_get(name=name)
if node and not "Error" in node:
LOG.debug("node {0} exists".format(name))
ret[name] = node
return ret
host_name = name.split('.')[0]
domain_name = '.'.join(name.split('.')[1:])
if classes == None:
meta_classes = []
else:
if isinstance(classes, six.string_types):
meta_classes = json.loads(classes)
else:
meta_classes = classes
if parameters == None:
meta_parameters = {}
else:
if isinstance(parameters, six.string_types):
meta_parameters = json.loads(parameters)
else:
meta_parameters = parameters
node_meta = {
'classes': meta_classes,
'parameters': {
'_param': meta_parameters,
'linux': {
'system': {
'name': host_name,
'domain': domain_name,
'cluster': cluster,
'environment': environment,
}
}
}
}
LOG.debug(node_meta)
if path == None:
file_path = os.path.join(_get_nodes_dir(), name + '.yml')
else:
file_path = os.path.join(_get_nodes_dir(), path, name + '.yml')
with open(file_path, 'w') as node_file:
node_file.write(yaml.safe_dump(node_meta, default_flow_style=False))
return node_get(name)
def node_delete(name, **kwargs):
'''
Delete a reclass node
:params node: Node name
CLI Examples:
.. code-block:: bash
salt '*' reclass.node_delete demo01.domain.com
salt '*' reclass.node_delete name=demo01.domain.com
'''
node = node_get(name=name)
if 'Error' in node:
return {'Error': 'Unable to retreive node'}
if node[name]['path'] == '':
file_path = os.path.join(_get_nodes_dir(), name + '.yml')
else:
file_path = os.path.join(_get_nodes_dir(), node[name]['path'], name + '.yml')
os.remove(file_path)
ret = 'Node {0} deleted'.format(name)
return ret
def node_get(name, path=None, **kwargs):
'''
Return a specific node
CLI Examples:
.. code-block:: bash
salt '*' reclass.node_get host01.domain.com
salt '*' reclass.node_get name=host02.domain.com
'''
ret = {}
nodes = node_list(**kwargs)
if not name in nodes:
return {'Error': 'Error in retrieving node'}
ret[name] = nodes[name]
return ret
def node_list(**connection_args):
'''
Return a list of available nodes
CLI Example:
.. code-block:: bash
salt '*' reclass.node_list
'''
ret = {}
for root, sub_folders, files in os.walk(_get_nodes_dir()):
for file in files:
file_path = os.path.join(root, file)
file_content = open(file_path, 'r')
file_data = yaml.load(file_content.read())
file_content.close()
if 'classes' in file_data:
classes = file_data.get('classes')
else:
classes = []
if 'parameters' in file_data:
if '_param' in file_data.get('parameters'):
parameters = file_data.get('parameters').get('_param')
else:
parameters = []
else:
parameters = []
name = file.replace('.yml', '')
host_name = name.split('.')[0]
domain_name = '.'.join(name.split('.')[1:])
path = root.replace(_get_nodes_dir()+'/', '')
ret[name] = {
'name': host_name,
'domain': domain_name,
'cluster': 'default',
'environment': 'prd',
'path': path,
'classes': classes,
'parameters': parameters
}
return ret
def node_update(name, classes=None, parameters=None, **connection_args):
'''
Update a node metadata information, classes and parameters.
CLI Examples:
.. code-block:: bash
salt '*' reclass.node_update name=nodename classes="[clas1, class2]"
'''
node = node_get(name=name)
if not node.has_key('Error'):
node = node[name.split("/")[1]]
else:
return {'Error': 'Error in retrieving node'}
def inventory(**connection_args):
'''
Get all nodes in inventory and their associated services/roles classification.
CLI Examples:
.. code-block:: bash
salt '*' reclass.inventory
'''
defaults = find_and_read_configfile()
storage = get_storage(defaults['storage_type'], _get_nodes_dir(), _get_classes_dir())
reclass = Core(storage, None)
nodes = reclass.inventory()["nodes"]
output = {}
for node in nodes:
service_classification = []
role_classification = []
for service in nodes[node]['parameters']:
if service not in ['_param', 'private_keys', 'public_keys', 'known_hosts']:
service_classification.append(service)
for role in nodes[node]['parameters'][service]:
if role not in ['_support', '_orchestrate', 'common']:
role_classification.append('%s.%s' % (service, role))
output[node] = {
'roles': role_classification,
'services': service_classification,
}
return output