mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Halite is long gone
This commit is contained in:
parent
20b274710f
commit
6bc0f8e3a5
3 changed files with 0 additions and 621 deletions
|
@ -1,237 +0,0 @@
|
|||
.. _tutorial-halite:
|
||||
|
||||
=================================
|
||||
Installing and Configuring Halite
|
||||
=================================
|
||||
|
||||
.. warning:: Halite is deprecated
|
||||
|
||||
The Halite project is retired. The code will remain available on GitHub.
|
||||
|
||||
In this tutorial, we'll walk through installing and setting up Halite. The
|
||||
current version of Halite is considered pre-alpha and is supported only in Salt
|
||||
``v2014.1.0`` or greater. Additional information is available on GitHub:
|
||||
https://github.com/saltstack/halite
|
||||
|
||||
Before beginning this tutorial, ensure that the salt-master is installed. To
|
||||
install the salt-master, please review the installation documentation:
|
||||
http://docs.saltstack.com/topics/installation/index.html
|
||||
|
||||
.. note::
|
||||
|
||||
Halite only works with Salt versions greater than 2014.1.0.
|
||||
|
||||
Installing Halite Via Package
|
||||
=============================
|
||||
|
||||
On CentOS, RHEL, or Fedora:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ yum install python-halite
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
By default python-halite only installs CherryPy. If you would like to use
|
||||
a different webserver please review the instructions below to install
|
||||
pip and your server of choice. The package does not modify the master
|
||||
configuration with ``/etc/salt/master``.
|
||||
|
||||
Installing Halite Using pip
|
||||
===========================
|
||||
|
||||
To begin the installation of Halite from PyPI, you'll need to install pip. The
|
||||
Salt package, as well as the bootstrap, do not install pip by default.
|
||||
|
||||
On CentOS, RHEL, or Fedora:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ yum install python-pip
|
||||
|
||||
|
||||
On Debian:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt-get install python-pip
|
||||
|
||||
|
||||
Once you have pip installed, use it to install halite:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install -U halite
|
||||
|
||||
|
||||
Depending on the webserver you want to run halite through, you'll need to
|
||||
install that piece as well. On RHEL based distros, use one of the following:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install cherrypy
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install paste
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ yum install python-devel
|
||||
$ yum install gcc
|
||||
$ pip install gevent
|
||||
$ pip install pyopenssl
|
||||
|
||||
On Debian based distributions:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install CherryPy
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install paste
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt-get install gcc
|
||||
$ apt-get install python-dev
|
||||
$ apt-get install libevent-dev
|
||||
$ pip install gevent
|
||||
$ pip install pyopenssl
|
||||
|
||||
|
||||
Configuring Halite Permissions
|
||||
==============================
|
||||
|
||||
Configuring Halite access permissions is easy. By default, you only need to
|
||||
ensure that the @runner group is configured. In the ``/etc/salt/master`` file,
|
||||
uncomment and modify the following lines:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
external_auth:
|
||||
pam:
|
||||
testuser:
|
||||
- .*
|
||||
- '@runner'
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
You cannot use the root user for pam login; it will fail to authenticate.
|
||||
|
||||
Halite uses the runner manage.present to get the status of minions, so runner
|
||||
permissions are required. For example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
external_auth:
|
||||
pam:
|
||||
mytestuser:
|
||||
- .*
|
||||
- '@runner'
|
||||
- '@wheel'
|
||||
|
||||
|
||||
Currently Halite allows, but does not require, any wheel modules.
|
||||
|
||||
|
||||
Configuring Halite Settings
|
||||
===========================
|
||||
|
||||
Once you've configured the permissions for Halite, you'll need to set up the
|
||||
Halite settings in the /etc/salt/master file. Halite supports CherryPy, Paste, and Gevent out of the box.
|
||||
|
||||
To configure cherrypy, add the following to the bottom of your /etc/salt/master file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
halite:
|
||||
level: 'debug'
|
||||
server: 'cherrypy'
|
||||
host: '0.0.0.0'
|
||||
port: '8080'
|
||||
cors: False
|
||||
tls: True
|
||||
certpath: '/etc/pki/tls/certs/localhost.crt'
|
||||
keypath: '/etc/pki/tls/certs/localhost.key'
|
||||
pempath: '/etc/pki/tls/certs/localhost.pem'
|
||||
|
||||
|
||||
If you wish to use paste:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
halite:
|
||||
level: 'debug'
|
||||
server: 'paste'
|
||||
host: '0.0.0.0'
|
||||
port: '8080'
|
||||
cors: False
|
||||
tls: True
|
||||
certpath: '/etc/pki/tls/certs/localhost.crt'
|
||||
keypath: '/etc/pki/tls/certs/localhost.key'
|
||||
pempath: '/etc/pki/tls/certs/localhost.pem'
|
||||
|
||||
|
||||
To use gevent:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
halite:
|
||||
level: 'debug'
|
||||
server: 'gevent'
|
||||
host: '0.0.0.0'
|
||||
port: '8080'
|
||||
cors: False
|
||||
tls: True
|
||||
certpath: '/etc/pki/tls/certs/localhost.crt'
|
||||
keypath: '/etc/pki/tls/certs/localhost.key'
|
||||
pempath: '/etc/pki/tls/certs/localhost.pem'
|
||||
|
||||
|
||||
The "cherrypy" and "gevent" servers require the certpath and keypath files
|
||||
to run tls/ssl. The .crt file holds the public cert and the .key file holds
|
||||
the private key. Whereas the "paste" server requires a single .pem file that
|
||||
contains both the cert and key. This can be created simply by concatenating
|
||||
the .crt and .key files.
|
||||
|
||||
If you want to use a self-signed cert, you can create one using the Salt.tls
|
||||
module:
|
||||
|
||||
.. note::
|
||||
|
||||
The following command needs to be run on your salt master.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call tls.create_self_signed_cert tls
|
||||
|
||||
Note that certs generated by the above command can be found under the ``/etc/pki/tls/certs/`` directory.
|
||||
When using self-signed certs, browsers will need approval before accepting the
|
||||
cert. If the web application page has been cached with a non-HTTPS version of
|
||||
the app, then the browser cache will have to be cleared before it will
|
||||
recognize and prompt to accept the self-signed certificate.
|
||||
|
||||
|
||||
Starting Halite
|
||||
===============
|
||||
|
||||
Once you've configured the halite section of your /etc/salt/master, you can
|
||||
restart the salt-master service, and your halite instance will be available.
|
||||
Depending on your configuration, the instance will be available either at
|
||||
https://localhost:8080/app, https://domain:8080/app, or
|
||||
https://123.456.789.012:8080/app .
|
||||
|
||||
.. note::
|
||||
|
||||
halite requires an HTML 5 compliant browser.
|
||||
|
||||
|
||||
All logs relating to halite are logged to the default /var/log/salt/master file.
|
|
@ -1,333 +0,0 @@
|
|||
"""
|
||||
This module provides the point of entry for client applications to interface to
|
||||
salt. The purpose is to have a simplified consistent interface for various
|
||||
client applications.
|
||||
|
||||
.. warning:: This API is not yet public or stable!
|
||||
|
||||
This API exists in its current form as an entry point for Halite only. This
|
||||
interface is likely to change without warning. Long-term plans are to make
|
||||
this public as a unified interface to Salt's *Client() APIs. Until that
|
||||
time please use Salt's *Client() interfaces individually:
|
||||
|
||||
http://docs.saltstack.com/ref/clients/index.html
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
|
||||
import salt.auth
|
||||
import salt.client
|
||||
import salt.config
|
||||
import salt.runner
|
||||
import salt.syspaths as syspaths
|
||||
import salt.utils.args
|
||||
import salt.utils.event
|
||||
import salt.wheel
|
||||
from salt.exceptions import EauthAuthenticationError
|
||||
|
||||
|
||||
def tokenify(cmd, token=None):
|
||||
"""
|
||||
If token is not None Then assign token to 'token' key of cmd dict
|
||||
and return cmd
|
||||
Otherwise return cmd
|
||||
"""
|
||||
if token is not None:
|
||||
cmd["token"] = token
|
||||
return cmd
|
||||
|
||||
|
||||
class APIClient:
|
||||
"""
|
||||
Provide a uniform method of accessing the various client interfaces in Salt
|
||||
in the form of low-data data structures. For example:
|
||||
"""
|
||||
|
||||
def __init__(self, opts=None, listen=True):
|
||||
if not opts:
|
||||
opts = salt.config.client_config(
|
||||
os.environ.get(
|
||||
"SALT_MASTER_CONFIG", os.path.join(syspaths.CONFIG_DIR, "master")
|
||||
)
|
||||
)
|
||||
self.opts = opts
|
||||
self.localClient = salt.client.get_local_client(self.opts["conf_file"])
|
||||
self.runnerClient = salt.runner.RunnerClient(self.opts)
|
||||
self.wheelClient = salt.wheel.Wheel(self.opts)
|
||||
self.resolver = salt.auth.Resolver(self.opts)
|
||||
self.event = salt.utils.event.get_event(
|
||||
"master",
|
||||
self.opts["sock_dir"],
|
||||
self.opts["transport"],
|
||||
opts=self.opts,
|
||||
listen=listen,
|
||||
)
|
||||
|
||||
def run(self, cmd):
|
||||
"""
|
||||
Execute the salt command given by cmd dict.
|
||||
|
||||
cmd is a dictionary of the following form:
|
||||
|
||||
{
|
||||
'mode': 'modestring',
|
||||
'fun' : 'modulefunctionstring',
|
||||
'kwarg': functionkeywordargdictionary,
|
||||
'tgt' : 'targetpatternstring',
|
||||
'tgt_type' : 'targetpatterntype',
|
||||
'ret' : 'returner namestring',
|
||||
'timeout': 'functiontimeout',
|
||||
'arg' : 'functionpositionalarg sequence',
|
||||
'token': 'salttokenstring',
|
||||
'username': 'usernamestring',
|
||||
'password': 'passwordstring',
|
||||
'eauth': 'eauthtypestring',
|
||||
}
|
||||
|
||||
Implied by the fun is which client is used to run the command, that is, either
|
||||
the master local minion client, the master runner client, or the master wheel client.
|
||||
|
||||
The cmd dict items are as follows:
|
||||
|
||||
mode: either 'sync' or 'asynchronous'. Defaults to 'asynchronous' if missing
|
||||
fun: required. If the function is to be run on the master using either
|
||||
a wheel or runner client then the fun: includes either
|
||||
'wheel.' or 'runner.' as a prefix and has three parts separated by '.'.
|
||||
Otherwise the fun: specifies a module to be run on a minion via the local
|
||||
minion client.
|
||||
Example:
|
||||
fun of 'wheel.config.values' run with master wheel client
|
||||
fun of 'runner.manage.status' run with master runner client
|
||||
fun of 'test.ping' run with local minion client
|
||||
fun of 'wheel.foobar' run with with local minion client not wheel
|
||||
kwarg: A dictionary of keyword function parameters to be passed to the eventual
|
||||
salt function specified by fun:
|
||||
tgt: Pattern string specifying the targeted minions when the implied client is local
|
||||
tgt_type: Optional target pattern type string when client is local minion.
|
||||
Defaults to 'glob' if missing
|
||||
ret: Optional name string of returner when local minion client.
|
||||
arg: Optional positional argument string when local minion client
|
||||
token: the salt token. Either token: is required or the set of username:,
|
||||
password: , and eauth:
|
||||
username: the salt username. Required if token is missing.
|
||||
password: the user's password. Required if token is missing.
|
||||
eauth: the authentication type such as 'pam' or 'ldap'. Required if token is missing
|
||||
|
||||
"""
|
||||
cmd = dict(cmd) # make copy
|
||||
client = "minion" # default to local minion client
|
||||
mode = cmd.get("mode", "async")
|
||||
|
||||
# check for wheel or runner prefix to fun name to use wheel or runner client
|
||||
funparts = cmd.get("fun", "").split(".")
|
||||
if len(funparts) > 2 and funparts[0] in ["wheel", "runner"]: # master
|
||||
client = funparts[0]
|
||||
cmd["fun"] = ".".join(funparts[1:]) # strip prefix
|
||||
|
||||
if not (
|
||||
"token" in cmd
|
||||
or ("eauth" in cmd and "password" in cmd and "username" in cmd)
|
||||
):
|
||||
raise EauthAuthenticationError("No authentication credentials given")
|
||||
|
||||
executor = getattr(self, "{}_{}".format(client, mode))
|
||||
result = executor(**cmd)
|
||||
return result
|
||||
|
||||
def minion_async(self, **kwargs):
|
||||
"""
|
||||
Wrap LocalClient for running :ref:`execution modules <all-salt.modules>`
|
||||
and immediately return the job ID. The results of the job can then be
|
||||
retrieved at a later time.
|
||||
|
||||
.. seealso:: :ref:`python-api`
|
||||
"""
|
||||
return self.localClient.run_job(**kwargs)
|
||||
|
||||
def minion_sync(self, **kwargs):
|
||||
"""
|
||||
Wrap LocalClient for running :ref:`execution modules <all-salt.modules>`
|
||||
|
||||
.. seealso:: :ref:`python-api`
|
||||
"""
|
||||
return self.localClient.cmd(**kwargs)
|
||||
|
||||
def runner_async(self, **kwargs):
|
||||
"""
|
||||
Wrap RunnerClient for executing :ref:`runner modules <all-salt.runners>`
|
||||
Expects that one of the kwargs is key 'fun' whose value is the namestring
|
||||
of the function to call
|
||||
"""
|
||||
return self.runnerClient.master_call(**kwargs)
|
||||
|
||||
runner_sync = runner_async # always runner asynchronous, so works in either mode
|
||||
|
||||
def wheel_sync(self, **kwargs):
|
||||
"""
|
||||
Wrap Wheel to enable executing :ref:`wheel modules <all-salt.wheel>`
|
||||
Expects that one of the kwargs is key 'fun' whose value is the namestring
|
||||
of the function to call
|
||||
"""
|
||||
return self.wheelClient.master_call(**kwargs)
|
||||
|
||||
wheel_async = wheel_sync # always wheel_sync, so it works either mode
|
||||
|
||||
def signature(self, cmd):
|
||||
"""
|
||||
Convenience function that returns dict of function signature(s) specified by cmd.
|
||||
|
||||
cmd is dict of the form:
|
||||
{
|
||||
'module' : 'modulestring',
|
||||
'tgt' : 'targetpatternstring',
|
||||
'tgt_type' : 'targetpatterntype',
|
||||
'token': 'salttokenstring',
|
||||
'username': 'usernamestring',
|
||||
'password': 'passwordstring',
|
||||
'eauth': 'eauthtypestring',
|
||||
}
|
||||
|
||||
The cmd dict items are as follows:
|
||||
|
||||
module: required. This is either a module or module function name for
|
||||
the specified client.
|
||||
tgt: Optional pattern string specifying the targeted minions when client
|
||||
is 'minion'
|
||||
tgt_type: Optional target pattern type string when client is 'minion'.
|
||||
Example: 'glob' defaults to 'glob' if missing
|
||||
token: the salt token. Either token: is required or the set of username:,
|
||||
password: , and eauth:
|
||||
username: the salt username. Required if token is missing.
|
||||
password: the user's password. Required if token is missing.
|
||||
eauth: the authentication type such as 'pam' or 'ldap'. Required if token is missing
|
||||
|
||||
Adds client per the command.
|
||||
"""
|
||||
cmd["client"] = "minion"
|
||||
if len(cmd["module"].split(".")) > 2 and cmd["module"].split(".")[0] in [
|
||||
"runner",
|
||||
"wheel",
|
||||
]:
|
||||
cmd["client"] = "master"
|
||||
return self._signature(cmd)
|
||||
|
||||
def _signature(self, cmd):
|
||||
"""
|
||||
Expects everything that signature does and also a client type string.
|
||||
client can either be master or minion.
|
||||
"""
|
||||
result = {}
|
||||
|
||||
client = cmd.get("client", "minion")
|
||||
if client == "minion":
|
||||
cmd["fun"] = "sys.argspec"
|
||||
cmd["kwarg"] = dict(module=cmd["module"])
|
||||
result = self.run(cmd)
|
||||
elif client == "master":
|
||||
parts = cmd["module"].split(".")
|
||||
client = parts[0]
|
||||
module = ".".join(parts[1:]) # strip prefix
|
||||
if client == "wheel":
|
||||
functions = self.wheelClient.functions
|
||||
elif client == "runner":
|
||||
functions = self.runnerClient.functions
|
||||
result = {"master": salt.utils.args.argspec_report(functions, module)}
|
||||
return result
|
||||
|
||||
def create_token(self, creds):
|
||||
"""
|
||||
Create token with creds.
|
||||
Token authorizes salt access if successful authentication
|
||||
with the credentials in creds.
|
||||
creds format is as follows:
|
||||
|
||||
{
|
||||
'username': 'namestring',
|
||||
'password': 'passwordstring',
|
||||
'eauth': 'eauthtypestring',
|
||||
}
|
||||
|
||||
examples of valid eauth type strings: 'pam' or 'ldap'
|
||||
|
||||
Returns dictionary of token information with the following format:
|
||||
|
||||
{
|
||||
'token': 'tokenstring',
|
||||
'start': starttimeinfractionalseconds,
|
||||
'expire': expiretimeinfractionalseconds,
|
||||
'name': 'usernamestring',
|
||||
'user': 'usernamestring',
|
||||
'username': 'usernamestring',
|
||||
'eauth': 'eauthtypestring',
|
||||
'perms: permslistofstrings,
|
||||
}
|
||||
The perms list provides those parts of salt for which the user is authorised
|
||||
to execute.
|
||||
example perms list:
|
||||
[
|
||||
"grains.*",
|
||||
"status.*",
|
||||
"sys.*",
|
||||
"test.*"
|
||||
]
|
||||
|
||||
"""
|
||||
try:
|
||||
tokenage = self.resolver.mk_token(creds)
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
raise EauthAuthenticationError(
|
||||
"Authentication failed with {}.".format(repr(ex))
|
||||
)
|
||||
|
||||
if "token" not in tokenage:
|
||||
raise EauthAuthenticationError(
|
||||
"Authentication failed with provided credentials."
|
||||
)
|
||||
|
||||
# Grab eauth config for the current backend for the current user
|
||||
tokenage_eauth = self.opts["external_auth"][tokenage["eauth"]]
|
||||
if tokenage["name"] in tokenage_eauth:
|
||||
tokenage["perms"] = tokenage_eauth[tokenage["name"]]
|
||||
else:
|
||||
tokenage["perms"] = tokenage_eauth["*"]
|
||||
|
||||
tokenage["user"] = tokenage["name"]
|
||||
tokenage["username"] = tokenage["name"]
|
||||
|
||||
return tokenage
|
||||
|
||||
def verify_token(self, token):
|
||||
"""
|
||||
If token is valid Then returns user name associated with token
|
||||
Else False.
|
||||
"""
|
||||
try:
|
||||
result = self.resolver.get_token(token)
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
raise EauthAuthenticationError(
|
||||
"Token validation failed with {}.".format(repr(ex))
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def get_event(self, wait=0.25, tag="", full=False):
|
||||
"""
|
||||
Get a single salt event.
|
||||
If no events are available, then block for up to ``wait`` seconds.
|
||||
Return the event if it matches the tag (or ``tag`` is empty)
|
||||
Otherwise return None
|
||||
|
||||
If wait is 0 then block forever or until next event becomes available.
|
||||
"""
|
||||
return self.event.get_event(wait=wait, tag=tag, full=full, auto_reconnect=True)
|
||||
|
||||
def fire_event(self, data, tag):
|
||||
"""
|
||||
fires event with data and tag
|
||||
This only works if api is running with same user permissions as master
|
||||
Need to convert this to a master call with appropriate authentication
|
||||
|
||||
"""
|
||||
return self.event.fire_event(data, salt.utils.event.tagify(tag, "wui"))
|
|
@ -78,14 +78,6 @@ except ImportError:
|
|||
# resource is not available on windows
|
||||
HAS_RESOURCE = False
|
||||
|
||||
try:
|
||||
import halite # pylint: disable=import-error
|
||||
|
||||
HAS_HALITE = True
|
||||
except ImportError:
|
||||
HAS_HALITE = False
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -760,10 +752,6 @@ class Master(SMaster):
|
|||
except Exception: # pylint: disable=broad-except
|
||||
log.error("Error creating ext_processes process: %s", proc)
|
||||
|
||||
if HAS_HALITE and "halite" in self.opts:
|
||||
log.info("Creating master halite process")
|
||||
self.process_manager.add_process(Halite, args=(self.opts["halite"],))
|
||||
|
||||
# TODO: remove, or at least push into the transport stuff (pre-fork probably makes sense there)
|
||||
if self.opts["con_cache"]:
|
||||
log.info("Creating master concache process")
|
||||
|
@ -832,45 +820,6 @@ class Master(SMaster):
|
|||
sys.exit(0)
|
||||
|
||||
|
||||
class Halite(salt.utils.process.SignalHandlingProcess):
|
||||
"""
|
||||
Manage the Halite server
|
||||
"""
|
||||
|
||||
def __init__(self, hopts, **kwargs):
|
||||
"""
|
||||
Create a halite instance
|
||||
|
||||
:param dict hopts: The halite options
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
self.hopts = hopts
|
||||
|
||||
# __setstate__ and __getstate__ are only used on Windows.
|
||||
# We do this so that __init__ will be invoked on Windows in the child
|
||||
# process so that a register_after_fork() equivalent will work on Windows.
|
||||
def __setstate__(self, state):
|
||||
self.__init__(
|
||||
state["hopts"],
|
||||
log_queue=state["log_queue"],
|
||||
log_queue_level=state["log_queue_level"],
|
||||
)
|
||||
|
||||
def __getstate__(self):
|
||||
return {
|
||||
"hopts": self.hopts,
|
||||
"log_queue": self.log_queue,
|
||||
"log_queue_level": self.log_queue_level,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Fire up halite!
|
||||
"""
|
||||
salt.utils.process.appendproctitle(self.__class__.__name__)
|
||||
halite.start(self.hopts)
|
||||
|
||||
|
||||
class ReqServer(salt.utils.process.SignalHandlingProcess):
|
||||
"""
|
||||
Starts up the master request server, minions send results to this
|
||||
|
|
Loading…
Add table
Reference in a new issue