ramdisk-func-test/ramdisk_func_test/node.py

200 lines
6.5 KiB
Python

#
# Copyright 2016 Cray Inc., All Rights Reserved
#
# 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.
import os
import logging
import paramiko
from time import time
from time import sleep
from contextlib import contextmanager
from lxml import etree
from oslo_config import cfg
from ramdisk_func_test import base
from ramdisk_func_test import conf
from ramdisk_func_test import exception
from ramdisk_func_test import utils
CONF = conf.CONF
CONF.register_opts([
cfg.IntOpt('node_boot_timeout',
help='Time to wait slave node to boot (seconds)',
default=360),
cfg.StrOpt('libvirt_machine_type',
default='',
help='Libvirt machine type (apply if it is not set in '
'template)'),
])
CONF.import_opt('ramdisk_func_test_workdir', 'ramdisk_func_test.utils')
LOG = logging.getLogger(__name__)
class Node(base.LibvirtBase):
def __init__(self, jinja_env, template, network, key):
super(Node, self).__init__(jinja_env)
self.name = self._generate_name('node')
self.workdir = os.path.join(CONF.ramdisk_func_test_workdir, self.name)
self.network = network
self.mac = utils.get_random_mac()
self.ip = None
self.ssh_login = "root"
self.ssh_key = key
self.console_log = os.path.join(self.workdir, "console.log")
xml = self.jinja_env.get_template(template).render(
mac_addr=self.mac,
network_name=network,
node_name=self.name,
console_log=self.console_log)
if CONF.libvirt_machine_type:
xml_tree = etree.fromstring(xml)
type_element = xml_tree.find(r'.//os/type')
if 'machine' not in type_element.keys():
type_element.set('machine', CONF.libvirt_machine_type)
xml = etree.tostring(xml_tree)
self.domain = self._define_domain(xml)
def _define_domain(self, xml):
self.libvirt.defineXML(xml)
dom = self.libvirt.lookupByName(self.name)
return dom
def put_file(self, src, dst):
LOG.info("Putting {0} file to {1} at {2} node".format(
src, dst, self.name
))
with self._connect_ssh() as ssh:
sftp = ssh.open_sftp()
sftp.put(src, dst)
def get_file(self, src, dst):
LOG.info("Getting {0} file from {1} node".format(
src, self.name
))
with self._connect_ssh() as ssh:
sftp = ssh.open_sftp()
sftp.get(src, dst)
def read_file(self, partition, file, part_type='ext4'):
out, ret_code = self.run_cmd(
'mount -t {part_type} {partition} /mnt '
'&& cat /mnt/{file} '
'; umount /mnt'.format(**locals()))
return out
def write_file(self, partition, file, contents, part_type='ext4'):
out, ret_code = self.run_cmd(
'mount -t {part_type} {partition} /mnt '
'&& echo \'{contents}\' > /mnt/{file} '
'; umount /mnt'.format(**locals()))
return out
def run_cmd(self, cmd, check_ret_code=False, get_bareon_log=False):
LOG.info("Running '{0}' command on {1} node".format(
cmd, self.name
))
with self._connect_ssh() as ssh:
(stdin, stdout, stderr) = ssh.exec_command(cmd)
out = stdout.read()
err = stderr.read()
ret_code = stdout.channel.recv_exit_status()
if err:
LOG.info("{0} cmd {1} stderr below {0}".format("#"*40, cmd))
LOG.error(err)
LOG.info("{0} end cmd {1} stderr {0}".format("#"*40, cmd))
if get_bareon_log:
LOG.info("{0} bareon log below {0}".format("#"*40))
out, rc = self.run_cmd('cat /var/log/bareon.log')
LOG.info(out)
LOG.info("{0} end bareon log {0}".format("#"*40))
if check_ret_code and ret_code:
raise exception.NonZeroCmdRetCode(cmd=cmd, ret_code=ret_code)
return out, ret_code
def wait_for_boot(self):
LOG.info("Waiting {0} node to boot".format(
self.name))
timeout = CONF.node_boot_timeout
end = time() + timeout
while time() < end:
try:
self.run_cmd('ls') # dummy cmd to check connection
return
except(paramiko.ssh_exception.NoValidConnectionsError,
paramiko.ssh_exception.SSHException):
pass
sleep(1)
raise exception.NodeSSHTimeout(timeout=timeout,
node_name=self.name)
def wait_for_callback(self):
callback_path = os.path.join(CONF.ramdisk_func_test_workdir,
self.name, 'callback')
timeout = CONF.node_boot_timeout
end = time() + timeout
while time() < end:
if os.path.exists(callback_path):
LOG.info("Callback from node '{0}' received.".format(
self.name))
return
sleep(1)
raise exception.NodeCallbackTimeout(timeout=timeout,
node_name=self.name)
@contextmanager
def _connect_ssh(self):
try:
ssh = paramiko.SSHClient()
# -oStrictHostKeyChecking=no
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(self.ip,
username=self.ssh_login,
key_filename=self.ssh_key,
look_for_keys=0)
yield ssh
finally:
ssh.close()
def reboot_to_hdd(self):
xml = self.domain.XMLDesc()
xml_tree = etree.fromstring(xml)
xml_tree.find(r'.//os/boot').set('dev', 'hd')
updated_xml = etree.tostring(xml_tree)
self.domain = self._define_domain(updated_xml)
self.stop()
self.start()
LOG.info("Boot device for node '{0}' has changed to hdd, node is "
"rebooting.".format(self.name))