mirror of
https://github.com/ZwareBear/awx.git
synced 2026-04-02 06:01:48 -05:00
264 lines
7.9 KiB
Python
264 lines
7.9 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
# this work for additional information regarding copyright ownership.
|
|
# The ASF licenses this file to You 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.
|
|
|
|
"""
|
|
Provides generic deployment steps for machines post boot.
|
|
"""
|
|
|
|
from __future__ import with_statement
|
|
|
|
import os
|
|
import binascii
|
|
|
|
from libcloud.utils.py3 import basestring, PY3
|
|
|
|
|
|
class Deployment(object):
|
|
"""
|
|
Base class for deployment tasks.
|
|
"""
|
|
|
|
def run(self, node, client):
|
|
"""
|
|
Runs this deployment task on node using the client provided.
|
|
|
|
:type node: :class:`Node`
|
|
:keyword node: Node to operate one
|
|
|
|
:type client: :class:`BaseSSHClient`
|
|
:keyword client: Connected SSH client to use.
|
|
|
|
:return: :class:`Node`
|
|
"""
|
|
raise NotImplementedError(
|
|
'run not implemented for this deployment')
|
|
|
|
def _get_string_value(self, argument_name, argument_value):
|
|
if not isinstance(argument_value, basestring) and \
|
|
not hasattr(argument_value, 'read'):
|
|
raise TypeError('%s argument must be a string or a file-like '
|
|
'object' % (argument_name))
|
|
|
|
if hasattr(argument_value, 'read'):
|
|
argument_value = argument_value.read()
|
|
|
|
return argument_value
|
|
|
|
|
|
class SSHKeyDeployment(Deployment):
|
|
"""
|
|
Installs a public SSH Key onto a server.
|
|
"""
|
|
|
|
def __init__(self, key):
|
|
"""
|
|
:type key: ``str`` or :class:`File` object
|
|
:keyword key: Contents of the public key write or a file object which
|
|
can be read.
|
|
"""
|
|
self.key = self._get_string_value(argument_name='key',
|
|
argument_value=key)
|
|
|
|
def run(self, node, client):
|
|
"""
|
|
Installs SSH key into ``.ssh/authorized_keys``
|
|
|
|
See also :class:`Deployment.run`
|
|
"""
|
|
client.put(".ssh/authorized_keys", contents=self.key, mode='a')
|
|
return node
|
|
|
|
|
|
class FileDeployment(Deployment):
|
|
"""
|
|
Installs a file on the server.
|
|
"""
|
|
|
|
def __init__(self, source, target):
|
|
"""
|
|
:type source: ``str``
|
|
:keyword source: Local path of file to be installed
|
|
|
|
:type target: ``str``
|
|
:keyword target: Path to install file on node
|
|
"""
|
|
self.source = source
|
|
self.target = target
|
|
|
|
def run(self, node, client):
|
|
"""
|
|
Upload the file, retaining permissions.
|
|
|
|
See also :class:`Deployment.run`
|
|
"""
|
|
perms = int(oct(os.stat(self.source).st_mode)[4:], 8)
|
|
|
|
with open(self.source, 'rb') as fp:
|
|
content = fp.read()
|
|
|
|
client.put(path=self.target, chmod=perms,
|
|
contents=content)
|
|
return node
|
|
|
|
|
|
class ScriptDeployment(Deployment):
|
|
"""
|
|
Runs an arbitrary shell script on the server.
|
|
|
|
This step works by first writing the content of the shell script (script
|
|
argument) in a \*.sh file on a remote server and then running that file.
|
|
|
|
If you are running a non-shell script, make sure to put the appropriate
|
|
shebang to the top of the script. You are also advised to do that even if
|
|
you are running a plan shell script.
|
|
"""
|
|
|
|
def __init__(self, script, args=None, name=None, delete=False):
|
|
"""
|
|
:type script: ``str``
|
|
:keyword script: Contents of the script to run.
|
|
|
|
:type args: ``list``
|
|
:keyword args: Optional command line arguments which get passed to the
|
|
deployment script file.
|
|
|
|
:type name: ``str``
|
|
:keyword name: Name of the script to upload it as, if not specified,
|
|
a random name will be chosen.
|
|
|
|
:type delete: ``bool``
|
|
:keyword delete: Whether to delete the script on completion.
|
|
"""
|
|
script = self._get_string_value(argument_name='script',
|
|
argument_value=script)
|
|
|
|
self.script = script
|
|
self.args = args or []
|
|
self.stdout = None
|
|
self.stderr = None
|
|
self.exit_status = None
|
|
self.delete = delete
|
|
self.name = name
|
|
|
|
if self.name is None:
|
|
# File is put under user's home directory
|
|
# (~/libcloud_deployment_<random_string>.sh)
|
|
random_string = binascii.hexlify(os.urandom(4))
|
|
random_string = random_string.decode('ascii')
|
|
self.name = 'libcloud_deployment_%s.sh' % (random_string)
|
|
|
|
def run(self, node, client):
|
|
"""
|
|
Uploads the shell script and then executes it.
|
|
|
|
See also :class:`Deployment.run`
|
|
"""
|
|
file_path = client.put(path=self.name, chmod=int('755', 8),
|
|
contents=self.script)
|
|
|
|
# Pre-pend cwd if user specified a relative path
|
|
if self.name[0] != '/':
|
|
base_path = os.path.dirname(file_path)
|
|
name = os.path.join(base_path, self.name)
|
|
else:
|
|
name = self.name
|
|
|
|
cmd = name
|
|
|
|
if self.args:
|
|
# Append arguments to the command
|
|
cmd = '%s %s' % (name, ' '.join(self.args))
|
|
else:
|
|
cmd = name
|
|
|
|
self.stdout, self.stderr, self.exit_status = client.run(cmd)
|
|
|
|
if self.delete:
|
|
client.delete(self.name)
|
|
|
|
return node
|
|
|
|
|
|
class ScriptFileDeployment(ScriptDeployment):
|
|
"""
|
|
Runs an arbitrary shell script from a local file on the server. Same as
|
|
ScriptDeployment, except that you can pass in a path to the file instead of
|
|
the script content.
|
|
"""
|
|
|
|
def __init__(self, script_file, args=None, name=None, delete=False):
|
|
"""
|
|
:type script_file: ``str``
|
|
:keyword script_file: Path to a file containing the script to run.
|
|
|
|
:type args: ``list``
|
|
:keyword args: Optional command line arguments which get passed to the
|
|
deployment script file.
|
|
|
|
|
|
:type name: ``str``
|
|
:keyword name: Name of the script to upload it as, if not specified,
|
|
a random name will be chosen.
|
|
|
|
:type delete: ``bool``
|
|
:keyword delete: Whether to delete the script on completion.
|
|
"""
|
|
with open(script_file, 'rb') as fp:
|
|
content = fp.read()
|
|
|
|
if PY3:
|
|
content = content.decode('utf-8')
|
|
|
|
super(ScriptFileDeployment, self).__init__(script=content,
|
|
args=args,
|
|
name=name,
|
|
delete=delete)
|
|
|
|
|
|
class MultiStepDeployment(Deployment):
|
|
"""
|
|
Runs a chain of Deployment steps.
|
|
"""
|
|
def __init__(self, add=None):
|
|
"""
|
|
:type add: ``list``
|
|
:keyword add: Deployment steps to add.
|
|
"""
|
|
self.steps = []
|
|
self.add(add)
|
|
|
|
def add(self, add):
|
|
"""
|
|
Add a deployment to this chain.
|
|
|
|
:type add: Single :class:`Deployment` or a ``list`` of
|
|
:class:`Deployment`
|
|
:keyword add: Adds this deployment to the others already in this
|
|
object.
|
|
"""
|
|
if add is not None:
|
|
add = add if isinstance(add, (list, tuple)) else [add]
|
|
self.steps.extend(add)
|
|
|
|
def run(self, node, client):
|
|
"""
|
|
Run each deployment that has been added.
|
|
|
|
See also :class:`Deployment.run`
|
|
"""
|
|
for s in self.steps:
|
|
node = s.run(node, client)
|
|
return node
|