/usr/lib/python2.7/dist-packages/chef/fabric.py is in python-chef 0.2.3-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | from __future__ import absolute_import
import functools
from chef.api import ChefAPI, autoconfigure
from chef.environment import Environment
from chef.exceptions import ChefError, ChefAPIVersionError
from chef.search import Search
try:
from fabric.api import env, task, roles, output
except ImportError, e:
env = {}
task = lambda *args, **kwargs: lambda fn: fn
roles = task
__all__ = ['chef_roledefs', 'chef_environment', 'chef_query', 'chef_tags']
# Default environment name
DEFAULT_ENVIRONMENT = '_default'
# Default hostname attributes
DEFAULT_HOSTNAME_ATTR = ['cloud.public_hostname', 'fqdn']
# Sentinel object to trigger defered lookup
_default_environment = object()
def _api(api):
api = api or ChefAPI.get_global() or autoconfigure()
if not api:
raise ChefError('Unable to load Chef API configuration')
return api
class Roledef(object):
"""Represents a Fabric roledef for a Chef role."""
def __init__(self, query, api, hostname_attr, environment=None):
self.query = query
self.api = api
self.hostname_attr = hostname_attr
if isinstance(self.hostname_attr, basestring):
self.hostname_attr = (self.hostname_attr,)
self.environment = environment
def __call__(self):
query = self.query
environment = self.environment
if environment is _default_environment:
environment = env.get('chef_environment', DEFAULT_ENVIRONMENT)
if environment:
query += ' AND chef_environment:%s' % environment
for row in Search('node', query, api=self.api):
if row:
if callable(self.hostname_attr):
val = self.hostname_attr(row.object)
if val:
yield val
else:
for attr in self.hostname_attr:
try:
val = row.object.attributes.get_dotted(attr)
if val: # Don't ever give out '' or None, since it will error anyway
yield val
break
except KeyError:
pass # Move on to the next
else:
raise ChefError('Cannot find a usable hostname attribute for node %s', row.object)
def chef_roledefs(api=None, hostname_attr=DEFAULT_HOSTNAME_ATTR, environment=_default_environment):
"""Build a Fabric roledef dictionary from a Chef server.
Example::
from fabric.api import env, run, roles
from chef.fabric import chef_roledefs
env.roledefs = chef_roledefs()
@roles('web_app')
def mytask():
run('uptime')
``hostname_attr`` can either be a string that is the attribute in the chef
node that holds the hostname or IP to connect to, an array of such keys to
check in order (the first which exists will be used), or a callable which
takes a :class:`~chef.Node` and returns the hostname or IP to connect to.
To refer to a nested attribute, separate the levels with ``'.'`` e.g. ``'ec2.public_hostname'``
``environment`` is the Chef :class:`~chef.Environment` name in which to
search for nodes. If set to ``None``, no environment filter is added. If
set to a string, it is used verbatim as a filter string. If not passed as
an argument at all, the value in the Fabric environment dict is used,
defaulting to ``'_default'``.
.. note::
``environment`` must be set to ``None`` if you are emulating Chef API
version 0.9 or lower.
.. versionadded:: 0.1
.. versionadded:: 0.2
Support for iterable and callable values for the``hostname_attr``
argument, and the ``environment`` argument.
"""
api = _api(api)
if api.version_parsed < Environment.api_version_parsed and environment is not None:
raise ChefAPIVersionError('Environment support requires Chef API 0.10 or greater')
roledefs = {}
for row in Search('role', api=api):
name = row['name']
roledefs[name] = Roledef('roles:%s' % name, api, hostname_attr, environment)
return roledefs
@task(alias=env.get('chef_environment_task_alias', 'env'))
def chef_environment(name, api=None):
"""A Fabric task to set the current Chef environment context.
This task works alongside :func:`~chef.fabric.chef_roledefs` to set the
Chef environment to be used in future role queries.
Example::
from chef.fabric import chef_environment, chef_roledefs
env.roledefs = chef_roledefs()
.. code-block:: bash
$ fab env:production deploy
The task can be configured slightly via Fabric ``env`` values.
``env.chef_environment_task_alias`` sets the task alias, defaulting to "env".
This value must be set **before** :mod:`chef.fabric` is imported.
``env.chef_environment_validate`` sets if :class:`~chef.Environment` names
should be validated before use. Defaults to True.
.. versionadded:: 0.2
"""
if env.get('chef_environment_validate', True):
api = _api(api)
chef_env = Environment(name, api=api)
if not chef_env.exists:
raise ChefError('Unknown Chef environment: %s' % name)
env['chef_environment'] = name
def chef_query(query, api=None, hostname_attr=DEFAULT_HOSTNAME_ATTR, environment=_default_environment):
api = _api(api)
if api.version_parsed < Environment.api_version_parsed and environment is not None:
raise ChefAPIVersionError('Environment support requires Chef API 0.10 or greater')
rolename = 'query_'+query
env.setdefault('roledefs', {})[rolename] = Roledef(query, api, hostname_attr, environment)
return lambda fn: roles(rolename)(fn)
def chef_tags(*tags, **kwargs):
# Allow passing a single iterable
if len(tags) == 1 and not isinstance(tags[0], basestring):
tags = tags[0]
query = ' AND '.join('tags:%s'%tag.strip() for tag in tags)
return chef_query(query, **kwargs)
|