# Copyright (c) 2011 - 2017, Intel Corporation.
#
# 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.
"""``dev_chef_server.py``
`Chef Server host device related functionality`
"""
import os
import time
from . import clissh
from . import entry_template
from . import loggers
[docs]class GenericChefServerHost(entry_template.GenericEntry):
"""Generic Chef Server host pattern class.
"""
class_logger = loggers.ClassLogger()
ipaddr = None
ssh_user = None
ssh_pass = None
ssh_port = 22
[docs] def __init__(self, config, opts):
"""Initialize GenericChefServerHost class.
"""
super(GenericChefServerHost, self).__init__(config, opts)
self.name = config.get('name', "noname")
self.id = config['id']
self.type = config['instance_type']
self.ipaddr = str(config.get('ip_host', self.__class__.ipaddr))
self.ssh_port = int(config.get('ip_port', self.__class__.ssh_port))
self.ssh_user = str(config.get('username', self.__class__.ssh_user))
self.ssh_pass = str(config.get('password', self.__class__.ssh_pass))
self.chef_repo_path = config['installed_path']
self.roles_list = []
if self.ipaddr and self.ssh_user and self.ssh_pass:
self.ssh = clissh.CLISSH(self.ipaddr, self.ssh_port, self.ssh_user, self.ssh_pass)
self.class_logger.info("Init Chef Server: {}".format(self.ipaddr))
self.config = config
self.opts = opts
self.status = False
[docs] def exec_cmd(self, command, from_repo_root=True, check_root=True, timeout=None):
"""Exec shell command with root privileges and print warning message in case StdErr isn't empty.
Args:
command(str): Command to be executed
from_repo_root(bool): Directory chef-repo resides in
check_root(bool): Notify user has admin privileges
timeout(int): Max command execution time on chef server
Returns:
tuple (stdout, stderr, return code)
Examples::
env.chef[1].exec_cmd('ls -la')
"""
if check_root:
if self.ssh_user != "root":
command = "sudo " + command
if from_repo_root:
command = "cd '{0}'; {1}".format(self.chef_repo_path, command)
cmd_status = self.ssh.exec_command(command, timeout=timeout)
# Delay, to stabilize calls
time.sleep(1)
return cmd_status
[docs] def start(self, wait_on=True):
"""Mandatory method for environment specific classes.
"""
self.ssh.login(timeout=25)
[docs] def stop(self, with_cleanup=True):
"""Mandatory method for environment specific classes.
"""
self.ssh.close()
[docs] def cleanup(self):
"""Remove created configuration.
"""
pass
[docs] def create(self):
"""Start Chef server or get running one.
Notes:
This is mandatory method for all environment classes.
Also self.opts.get_only attribute affects logic of this method.
get_only is set in py.test command line options (read py.test --help for more information).
"""
self.start()
self.status = True
[docs] def destroy(self):
"""Stop or release Chef server.
Notes:
This is mandatory method for all environment classes.
Also self.opts.leave_on and get_only attributes affect logic of this method.
leave_on and get_only are set in py.test command line options (read py.test --help for more information).
"""
if not self.status:
self.class_logger.info("Skip id:{}({}) destroying because it already "
"has Off status.".format(self.id, self.name))
return
self.stop()
self.sanitize()
[docs] def sanitize(self):
"""Perform any necessary operations to leave environment in normal state.
"""
pass
[docs] def check(self):
"""Mandatory method for environment specific classes.
"""
pass
[docs] def set_role(self, src):
"""Put role file to Chef server chef-repo/roles dir and add it to Chef database.
"""
dst = os.path.join(self.chef_repo_path, 'roles', os.path.split(src)[-1])
self.class_logger.debug("Transfer generated role file to chef server.")
self.ssh.put_file(src, dst, proto="sftp")
self.exec_cmd("knife role from file '{}'".format(dst))
self.roles_list.append(dst)
[docs] def set_run_list(self, role_file, fqdn_hostname):
"""Set chosen JSON role file as target node run list.
"""
_cmd = 'knife node run_list set {} "role[{}]"'.format(fqdn_hostname,
os.path.splitext(role_file)[0])
self.exec_cmd(_cmd)
[docs] def bootstrap_node(self, switch_config, timeout=90):
"""Install chef client on target device.
"""
_cmd = "cd '{}'; knife bootstrap {} -V --bootstrap-template {} --environment {}".format(
self.chef_repo_path, switch_config['ip_host'],
self.config['distro'], self.config['environment'])
_alter = [('assword:', switch_config['sshtun_pass'], False, True)]
self.class_logger.info("Install chef client on device '{}'.".format(switch_config['name']))
self.ssh.open_shell()
self.ssh.shell_command(_cmd, alternatives=_alter, timeout=timeout)
self.ssh.close_shell()
[docs] def remove_role(self):
"""Cleanup generated role files on chef server.
"""
self.class_logger.info("Perform cleanup on chef server.")
for x in self.roles_list:
self.exec_cmd("knife role delete '{}' -y".format(
os.path.splitext(os.path.split(x)[-1])[0]))
# Remove role files generated during test run
self.exec_cmd("rm -f -- '{}'".format(x))
[docs] def delete_node(self, fqdn_hostname):
"""Delete node from chef database.
"""
self.exec_cmd('knife node delete -y {}'.format(fqdn_hostname))
self.exec_cmd('knife client delete -y {}'.format(fqdn_hostname))
ENTRY_TYPE = "chef-settings"
INSTANCES = {"chef": GenericChefServerHost}
NAME = "chef"