228 lines
7.0 KiB
Python
228 lines
7.0 KiB
Python
# Copyright (c) 2015, MapR Technologies
|
|
#
|
|
# 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.
|
|
|
|
|
|
from oslo_serialization import jsonutils as json
|
|
|
|
from sahara import context
|
|
import sahara.exceptions as e
|
|
from sahara.i18n import _
|
|
import sahara.plugins.exceptions as ex
|
|
import sahara.plugins.provisioning as p
|
|
from sahara.utils import files as files
|
|
|
|
|
|
_INSTALL_PACKAGES_TIMEOUT = 3600
|
|
|
|
|
|
class Service(object):
|
|
def __init__(self):
|
|
self._name = None
|
|
self._ui_name = None
|
|
self._node_processes = []
|
|
self._version = None
|
|
self._dependencies = []
|
|
self._ui_info = []
|
|
self._cluster_defaults = []
|
|
self._node_defaults = []
|
|
self._validation_rules = []
|
|
|
|
@property
|
|
def name(self):
|
|
return self._name
|
|
|
|
@property
|
|
def ui_name(self):
|
|
return self._ui_name
|
|
|
|
@property
|
|
def version(self):
|
|
return self._version
|
|
|
|
@property
|
|
def node_processes(self):
|
|
return self._node_processes
|
|
|
|
@property
|
|
def dependencies(self):
|
|
return self._dependencies
|
|
|
|
@property
|
|
def ui_info(self):
|
|
return self._ui_info
|
|
|
|
@property
|
|
def cluster_defaults(self):
|
|
return self._cluster_defaults
|
|
|
|
@property
|
|
def node_defaults(self):
|
|
return self._node_defaults
|
|
|
|
@property
|
|
def validation_rules(self):
|
|
return self._validation_rules
|
|
|
|
def install(self, cluster_context, instances):
|
|
with context.ThreadGroup() as tg:
|
|
for instance in instances:
|
|
tg.spawn('install-packages-%s' % instance.id,
|
|
self._install_packages_on_instance, cluster_context,
|
|
instance)
|
|
|
|
def _install_packages_on_instance(self, cluster_context, instance):
|
|
processes = [p for p in self.node_processes if
|
|
p.ui_name in instance.node_group.node_processes]
|
|
if processes is not None and len(processes) > 0:
|
|
packages = self._get_packages(processes)
|
|
cmd = cluster_context.distro.create_install_cmd(packages)
|
|
with instance.remote() as r:
|
|
r.execute_command(cmd, run_as_root=True,
|
|
timeout=_INSTALL_PACKAGES_TIMEOUT,
|
|
raise_when_error=False)
|
|
|
|
def _get_packages(self, node_processes):
|
|
result = []
|
|
|
|
result += self.dependencies
|
|
result += [(np.package, self.version) for np in node_processes]
|
|
|
|
return result
|
|
|
|
def post_install(self, cluster_context, instances):
|
|
pass
|
|
|
|
def post_start(self, cluster_context, instances):
|
|
pass
|
|
|
|
def configure(self, cluster_context, instances=None):
|
|
pass
|
|
|
|
def update(self, cluster_context, instances=None):
|
|
pass
|
|
|
|
def get_file_path(self, file_name):
|
|
template = 'plugins/mapr/services/%(service)s/resources/%(file_name)s'
|
|
args = {'service': self.name, 'file_name': file_name}
|
|
return template % args
|
|
|
|
def get_configs(self):
|
|
result = []
|
|
|
|
for d_file in self.cluster_defaults:
|
|
data = self._load_config_file(self.get_file_path(d_file))
|
|
result += [self._create_config_obj(c, self.ui_name) for c in data]
|
|
|
|
for d_file in self.node_defaults:
|
|
data = self._load_config_file(self.get_file_path(d_file))
|
|
result += [self._create_config_obj(c, self.ui_name, scope='node')
|
|
for c in data]
|
|
|
|
return result
|
|
|
|
def get_configs_dict(self):
|
|
result = dict()
|
|
for conf_obj in self.get_configs():
|
|
result.update({conf_obj.name: conf_obj.default_value})
|
|
return {self.ui_name: result}
|
|
|
|
def _load_config_file(self, file_path=None):
|
|
return json.loads(files.get_file_text(file_path))
|
|
|
|
def get_config_files(self, cluster_context, configs, instance=None):
|
|
return []
|
|
|
|
def _create_config_obj(self, item, target='general', scope='cluster',
|
|
high_priority=False):
|
|
def _prepare_value(value):
|
|
if isinstance(value, str):
|
|
return value.strip().lower()
|
|
return value
|
|
|
|
conf_name = _prepare_value(item.get('name', None))
|
|
|
|
conf_value = _prepare_value(item.get('value', None))
|
|
|
|
if not conf_name:
|
|
raise ex.HadoopProvisionError(_("Config missing 'name'"))
|
|
|
|
if conf_value is None:
|
|
raise e.InvalidDataException(
|
|
_("Config '%s' missing 'value'") % conf_name)
|
|
|
|
if high_priority or item.get('priority', 2) == 1:
|
|
priority = 1
|
|
else:
|
|
priority = 2
|
|
|
|
return p.Config(
|
|
name=conf_name,
|
|
applicable_target=target,
|
|
scope=scope,
|
|
config_type=item.get('config_type', "string"),
|
|
config_values=item.get('config_values', None),
|
|
default_value=conf_value,
|
|
is_optional=item.get('is_optional', True),
|
|
description=item.get('description', None),
|
|
priority=priority)
|
|
|
|
def get_version_config(self, versions):
|
|
return p.Config(
|
|
name='%s Version' % self._ui_name,
|
|
applicable_target=self.ui_name,
|
|
scope='cluster',
|
|
config_type='dropdown',
|
|
config_values=[(v, v) for v in sorted(versions, reverse=True)],
|
|
is_optional=False,
|
|
description=_('Specify the version of the service'),
|
|
priority=1)
|
|
|
|
def __eq__(self, other):
|
|
if isinstance(other, self.__class__):
|
|
version_eq = self.version == other.version
|
|
ui_name_eq = self.ui_name == other.ui_name
|
|
return version_eq and ui_name_eq
|
|
return NotImplemented
|
|
|
|
def restart(self, instances):
|
|
for node_process in self.node_processes:
|
|
node_process.restart(instances)
|
|
|
|
def service_dir(self, cluster_context):
|
|
args = {'mapr_home': cluster_context.mapr_home, 'name': self.name}
|
|
return '%(mapr_home)s/%(name)s' % args
|
|
|
|
def home_dir(self, cluster_context):
|
|
args = {
|
|
'service_dir': self.service_dir(cluster_context),
|
|
'name': self.name,
|
|
'version': self.version,
|
|
}
|
|
return '%(service_dir)s/%(name)s-%(version)s' % args
|
|
|
|
def conf_dir(self, cluster_context):
|
|
return '%s/conf' % self.home_dir(cluster_context)
|
|
|
|
def post_configure_sh(self, cluster_context, instances):
|
|
pass
|
|
|
|
|
|
class Single(type):
|
|
_instances = {}
|
|
|
|
def __call__(cls, *args, **kwargs):
|
|
if cls not in cls._instances:
|
|
cls._instances[cls] = super(Single, cls).__call__(*args, **kwargs)
|
|
return cls._instances[cls]
|