Convert controller post script to python

The old script is based on shell and not convenient to use in some cases. Some
functions in utils script might also need to be adjusted.

Change-Id: Ie1c9417363b07074f879e62b8856a455efb281c9
This commit is contained in:
John Hua 2016-10-17 11:58:37 +08:00
parent 10874d8672
commit d7a7c1cbbc
6 changed files with 192 additions and 140 deletions

View File

@ -2,7 +2,6 @@
import ConfigParser
from distutils.version import LooseVersion
import logging
import netifaces
import os
import re
@ -12,7 +11,6 @@ import utils
from utils import HIMN_IP
LOG_FILE = os.path.join(utils.LOG_ROOT, 'compute_post_deployment.log')
INT_BRIDGE = 'br-int'
XS_PLUGIN_ISO = 'xenapi-plugins-mitaka.iso'
DIST_PACKAGES_DIR = '/usr/lib/python2.7/dist-packages/'
@ -20,11 +18,8 @@ CONNTRACK_ISO = 'conntrack-tools.iso'
CONNTRACK_CONF_SAMPLE =\
'/usr/share/doc/conntrack-tools-1.4.2/doc/stats/conntrackd.conf'
if not os.path.exists(utils.LOG_ROOT):
os.mkdir(utils.LOG_ROOT)
logging.basicConfig(filename=LOG_FILE,
level=logging.DEBUG)
utils.setup_logging('compute_post_deployment.log')
LOG = utils.LOG
def get_endpoints(astute):
@ -35,8 +30,8 @@ def get_endpoints(astute):
endpoints[k]['IP'][0]
) for k in endpoints])
logging.info('storage network: {storage}'.format(**endpoints))
logging.info('mgmt network: {mgmt}'.format(**endpoints))
LOG.info('storage network: {storage}'.format(**endpoints))
LOG.info('mgmt network: {mgmt}'.format(**endpoints))
return endpoints
@ -77,7 +72,7 @@ def create_novacompute_conf(himn, username, password, public_ip, services_ssl):
cf.write(configfile)
except Exception:
utils.reportError('Cannot set configurations to %s' % filename)
logging.info('%s created' % filename)
LOG.info('%s created' % filename)
def route_to_compute(endpoints, himn_xs, himn_local, username):
@ -122,7 +117,7 @@ def route_to_compute(endpoints, himn_xs, himn_local, username):
cmd = cmd.format(net=net, mask=mask, himn_local=himn_local)
utils.ssh(himn_xs, username, cmd)
else:
logging.info('%s network ip is missing' % endpoint_name)
LOG.info('%s network ip is missing' % endpoint_name)
utils.ssh(himn_xs, username, 'chmod +x /etc/udev/scripts/reroute.sh')
utils.ssh(himn_xs, username,
('echo \'SUBSYSTEM=="net" ACTION=="add" '
@ -203,7 +198,7 @@ def modify_neutron_rootwrap_conf(himn, username, password):
cf.write(configfile)
except Exception:
utils.reportError("Fail to modify file %s", filename)
logging.info('Modify file %s successfully', filename)
LOG.info('Modify file %s successfully', filename)
def modify_neutron_ovs_agent_conf(int_br, br_mappings):
@ -221,7 +216,7 @@ def modify_neutron_ovs_agent_conf(int_br, br_mappings):
cf.write(configfile)
except Exception:
utils.reportError("Fail to modify %s", filename)
logging.info('Modify %s successfully', filename)
LOG.info('Modify %s successfully', filename)
def get_private_network_ethX():
@ -345,7 +340,7 @@ def check_and_setup_ceilometer(himn, username, password):
cf.set('xenapi', 'connection_password', password)
with open(filename, 'w') as configfile:
cf.write(configfile)
logging.info('Modify file %s successfully', filename)
LOG.info('Modify file %s successfully', filename)
except Exception:
utils.reportError("Fail to modify file %s", filename)
return
@ -359,7 +354,7 @@ def enable_conntrack_service(himn, username):
'param-key=platform_version'))
if LooseVersion(xcp_ver) < LooseVersion('2.1.0'):
# Only support conntrack-tools since XS7.0(XCP2.1.0) and above
logging.info('No need to enable conntrack-tools with XCP %s' % xcp_ver)
LOG.info('No need to enable conntrack-tools with XCP %s' % xcp_ver)
return
conn_installed = utils.ssh(himn, username,
@ -429,5 +424,5 @@ if __name__ == '__main__':
if is_ceilometer_enabled:
check_and_setup_ceilometer(HIMN_IP, username, password)
else:
logging.info('Skip ceilomter setup as this service is '
'disabled.')
LOG.info('Skip ceilomter setup as this service is '
'disabled.')

View File

@ -1,21 +1,16 @@
#!/usr/bin/env python
import json
import logging
import os
import stat
import utils
from utils import HIMN_IP
XS_RSA = '/root/.ssh/xs_rsa'
LOG_FILE = os.path.join(utils.LOG_ROOT, 'compute_pre_deployment.log')
VERSION_HOTFIXES = '@VERSION_HOTFIXES@'
if not os.path.exists(utils.LOG_ROOT):
os.mkdir(utils.LOG_ROOT)
logging.basicConfig(filename=LOG_FILE,
level=logging.DEBUG)
utils.setup_logging('compute_pre_test.log')
LOG = utils.LOG
def ssh_copy_id(host, username, password):

View File

@ -0,0 +1,146 @@
#!/usr/bin/env python
import ConfigParser
from glanceclient import Client
from keystoneauth1 import loading
from keystoneauth1 import session
import os
from time import sleep
import utils
import yaml
utils.setup_logging('controller_post_deployment.log')
LOG = utils.LOG
def get_keystone_creds():
return {
'username': os.environ.get('OS_USERNAME'),
'password': os.environ.get('OS_PASSWORD'),
'auth_url': os.environ.get('OS_AUTH_URL'),
'tenant_name': os.environ.get('OS_TENANT_NAME'),
}
def get_keystone_session():
loader = loading.get_plugin_loader('password')
creds = get_keystone_creds()
auth = loader.load_from_options(**creds)
return session.Session(auth=auth)
def list_images(sess):
LOG.info('Listing images:')
glance = Client('2', session=sess)
images = glance.images.list()
for image in images:
LOG.info(('+ {name} container_format:{container_format} '
'disk_format:{disk_format} visibility:{visibility} '
'file:{file}').format(**image))
def del_images(sess, image_name):
glance = Client('2', session=sess)
images = glance.images.list()
for image in images:
if image.name == image_name:
glance.images.delete(image.id)
LOG.info('Image %s has been deleted' % image_name)
def add_image(sess, image_name, vm_mode, image_file):
glance = Client('2', session=sess)
image = glance.images.create(name=image_name, container_format="ovf",
disk_format="vhd", visibility="public",
vm_mode=vm_mode)
with open(image_file, 'rb') as f:
glance.images.upload(image.id, f)
LOG.info('Image %s (mode: %s, file: %s) has been added' %
(image_name, vm_mode, image_file))
def wait_ocf_resource_started(timeout, interval):
"""Wait until all ocf resources are started"""
LOG.info("Waiting for all ocf resources to start")
remain_time = timeout
while remain_time > 0:
resources = utils.execute('pcs', 'resource', 'show')
if resources:
exists_not_started = any([("Started" not in line)
for line in resources.split('\n')
if "ocf::fuel" in line])
# All started
if not exists_not_started:
return
sleep(interval)
remain_time = timeout - interval
utils.reportError("Timeout for waiting resources to start")
def mod_novnc():
astute = utils.get_astute()
if astute:
public_ip = utils.astute_get(
astute, ('network_metadata', 'vips', 'public', 'ipaddr'))
filename = '/etc/nova/nova-compute.conf'
cf = ConfigParser.ConfigParser()
try:
cf.read(filename)
cf.set('DEFAULT', 'novncproxy_base_url',
'http//%s:6080/vnc_auto.html' % public_ip)
cf.set('DEFAULT', 'novncproxy_host', '0.0.0.0')
with open(filename, 'w') as configfile:
cf.write(configfile)
LOG.info('%s created' % filename)
utils.execute('service', 'nova-novncproxy', 'restart')
utils.execute('service', 'nova-consoleauth', 'restart')
except Exception:
utils.reportError('Cannot set configurations to %s' % filename)
def mod_ceilometer():
rc, out, err = utils.detailed_execute(
'pcs', 'resource', 'show', 'p_ceilometer-agent-central',
allowed_return_codes=[0, 1])
"""Wait until all ocf resources are started, otherwise there is risk for race
condition: If run "pcs resource restart" while some resources are still in
restarting or initiating stage, it may result into failures for both.
"""
if rc == 0:
wait_ocf_resource_started(300, 10)
LOG.info("Patching ceilometer pipeline.yaml to exclude \
network.servers.*")
# Exclude network.services.* to avoid error 404
pipeline = '/etc/ceilometer/pipeline.yaml'
if not os.path.exists(pipeline):
utils.reportError('%s not found' % pipeline)
with open(pipeline) as f:
ceilometer = yaml.safe_load(f)
sources = utils.astute_get(ceilometer, ('sources',))
if len(sources) != 1:
utils.reportError('ceilometer has none or more than one sources')
source = sources[0]
meters = utils.astute_get(source, ('meters',))
new_meter = '!network.services.*'
if new_meter not in meters:
meters.append(new_meter)
with open(pipeline, "w") as f:
ceilometer = yaml.safe_dump(ceilometer, f)
restart_info = utils.execute(
'pcs', 'resource', 'restart', 'p_ceilometer-agent-central')
LOG.info(restart_info)
if __name__ == '__main__':
sess = get_keystone_session()
list_images(sess)
del_images(sess, "TestVM")
add_image(sess, "TestVM", "xen", "cirros-0.3.4-x86_64-disk.vhd.tgz")
list_images(sess)
mod_ceilometer()
mod_novnc()

View File

@ -1,99 +0,0 @@
#!/bin/bash -eu
LOG_ROOT="/var/log/@PLUGIN_NAME@/"
mkdir -p $LOG_ROOT
LOG_FILE=$LOG_ROOT"controller_post_deployment.log"
function replace_test_image {
local image_name
image_name="$1"
local vm_mode
vm_mode="$2"
local image_file
image_file="$3"
image_id=$(glance image-list | awk -F "|" '/'$image_name'/ {print $2}')
if [[ -n "$image_id" ]]; then
echo "Delete image $image_name" >> $LOG_FILE
glance image-delete $image_id 2>&1 &>> $LOG_FILE
fi
echo "Create image $image_name" >> $LOG_FILE
glance image-create \
--name "$image_name" \
--container-format ovf \
--disk-format vhd \
--property vm_mode="$vm_mode" \
--visibility public \
--file "$image_file" \
2>&1 &>> $LOG_FILE
}
function mod_novnc {
local public_ip
public_ip=$(python - <<EOF
import sys
import yaml
astute=yaml.load(open('/etc/astute.yaml'))
print astute['network_metadata']['vips']['public']['ipaddr']
EOF
)
cat > /etc/nova/nova-compute.conf <<EOF
[DEFAULT]
novncproxy_host=0.0.0.0
novncproxy_base_url=http://$public_ip:6080/vnc_auto.html
EOF
service nova-novncproxy restart
service nova-consoleauth restart
}
function wait_ocf_resource_started {
# wait upto $TIMEOUT seconds until all ocf resources are started
TIMEOUT=300
INTERVAL=10
remain_time=$TIMEOUT
while [ ${remain_time} -gt 0 ]; do
if pcs resource show | grep ocf::fuel | grep -v Started >> $LOG_FILE; then
echo "$(date): wait for resources to start." >> $LOG_FILE
sleep $INTERVAL
remain_time=$((remain_time - $INTERVAL))
else
return 0
fi
done
echo "Error: $(date): timeout for waiting resources to start." >> $LOG_FILE
echo "Error: $(date): timeout for waiting resources to start." >&2
exit 1
}
function mod_ceilometer {
# modify ceilometer configuration per need.
if pcs resource show p_ceilometer-agent-central >/dev/null 2>&1; then
# wait until all ocf resources are started, otherwise there is risk for race
# condition: If run "pcs resource restart" while some resources are still in
# restarting or initiating stage, it may result into failures for both.
wait_ocf_resource_started
# exclude network.services.* to avoid NotFound: 404 service not found error.
sed -i '/- "!storage.api.request"/a\ - "!network.services.*"' \
/etc/ceilometer/pipeline.yaml>>$LOG_FILE 2>&1
pcs resource restart p_ceilometer-agent-central >>$LOG_FILE 2>&1
fi
}
source /root/openrc admin
echo "Before image replacement" >> $LOG_FILE
glance image-list 2>&1 >> $LOG_FILE
replace_test_image "TestVM" "xen" cirros-0.3.4-x86_64-disk.vhd.tgz
echo "After image replacement" >> $LOG_FILE
glance image-list 2>&1 >> $LOG_FILE
mod_novnc
mod_ceilometer

View File

@ -13,6 +13,10 @@ LOG_ROOT = '/var/log/@PLUGIN_NAME@'
HIMN_IP = '169.254.0.1'
LOG = logging.getLogger('@PLUGIN_NAME@')
LOG.setLevel(logging.DEBUG)
class ExecutionError(Exception):
pass
@ -22,7 +26,7 @@ class FatalException(Exception):
def reportError(err):
logging.error(err)
LOG.error(err)
raise FatalException(err)
@ -37,7 +41,7 @@ def detailed_execute(*cmd, **kwargs):
env.update(_env)
else:
env = None
logging.info(env_prefix + ' '.join(cmd))
LOG.info(env_prefix + ' '.join(cmd))
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, # nosec
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, env=env)
@ -51,14 +55,14 @@ def detailed_execute(*cmd, **kwargs):
if out:
# Truncate "\n" if it is the last char
out = out.strip()
logging.debug(out)
LOG.debug(out)
if err:
logging.info(err)
LOG.info(err)
if proc.returncode is not None and proc.returncode != 0:
if proc.returncode in kwargs.get('allowed_return_codes', [0]):
logging.info('Swallowed acceptable return code of %d',
proc.returncode)
LOG.info('Swallowed acceptable return code of %d',
proc.returncode)
else:
raise ExecutionError(err)
@ -106,6 +110,17 @@ def scp(host, username, target_path, filename):
'%s@%s:%s' % (username, host, target_path))
def setup_logging(filename):
LOG_FILE = os.path.join(LOG_ROOT, filename)
if not os.path.exists(LOG_ROOT):
os.mkdir(LOG_ROOT)
logging.basicConfig(
filename=LOG_FILE, level=logging.WARNING,
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
def get_astute(astute_path=ASTUTE_PATH):
"""Return the root object read from astute.yaml"""
if not os.path.exists(astute_path):
@ -133,9 +148,9 @@ def get_options(astute, astute_section=ASTUTE_SECTION):
reportError('%s not found' % astute_section)
options = astute[astute_section]
logging.info('username: {username}'.format(**options))
logging.info('password: {password}'.format(**options))
logging.info('install_xapi: {install_xapi}'.format(**options))
LOG.info('username: {username}'.format(**options))
LOG.info('password: {password}'.format(**options))
LOG.info('install_xapi: {install_xapi}'.format(**options))
return options['username'], options['password'], \
options['install_xapi']
@ -163,7 +178,7 @@ def find_eth_xenstore():
himn_mac = execute(
'xenstore-read',
'/local/domain/%s/vm-data/himn_mac' % domid)
logging.info('himn_mac: %s' % himn_mac)
LOG.info('himn_mac: %s' % himn_mac)
eths = [eth for eth in netifaces.interfaces()
if eth_to_mac(eth) == himn_mac]
@ -205,7 +220,7 @@ def init_eth():
try:
eth = find_eth_xenstore()
except Exception:
logging.debug('Failed to find MAC through xenstore', exc_info=True)
LOG.debug('Failed to find MAC through xenstore', exc_info=True)
if eth is None:
eth = detect_eth_dhclient()
@ -213,7 +228,7 @@ def init_eth():
if eth is None:
reportError('Failed to detect HIMN ethernet device')
logging.info('himn_eth: %s' % eth)
LOG.info('himn_eth: %s' % eth)
execute('dhclient', eth)
fname = '/etc/network/interfaces.d/ifcfg-' + eth
@ -222,7 +237,7 @@ def init_eth():
'post-up route del default dev {eth}').format(eth=eth)
with open(fname, 'w') as f:
f.write(s)
logging.info('%s created' % fname)
LOG.info('%s created' % fname)
execute('ifdown', eth)
execute('ifup', eth)
@ -231,12 +246,12 @@ def init_eth():
himn_xs = '.'.join(himn_local.split('.')[:-1] + ['1'])
if HIMN_IP != himn_xs:
# Not on the HIMN - we failed here.
logging.info('himn_local: DHCP returned incorrect IP %s' %
ip[0]['addr'])
LOG.info('himn_local: DHCP returned incorrect IP %s' %
ip[0]['addr'])
ip = None
if not ip:
reportError('HIMN failed to get IP address from Hypervisor')
logging.info('himn_local: %s' % ip[0]['addr'])
LOG.info('himn_local: %s' % ip[0]['addr'])
return eth, ip[0]['addr']

View File

@ -31,5 +31,5 @@
requires: ['post_deployment_start']
type: shell
parameters:
cmd: ./controller_post_deployment.sh
cmd: source /root/openrc && ./controller_post_deployment.py
timeout: 600