xCAT baremetal driver for OpenStack

This commit is contained in:
linggao 2014-02-20 09:45:15 -05:00
parent 2598e30ee1
commit 542259583c
14 changed files with 2065 additions and 0 deletions

View File

@ -0,0 +1,778 @@
# IBM(c) 2013 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::openstack;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use xCAT::Utils;
use xCAT::TableUtils;
use xCAT::SvrUtils;
use xCAT::NetworkUtils;
use xCAT::Table;
use Data::Dumper;
use File::Path;
use File::Copy;
use Getopt::Long;
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("pass_through");
sub handled_commands {
return {
opsaddbmnode => "openstack", #external command
opsaddimage => "openstack", #external command
deploy_ops_bm_node => "openstack", #internal command called from the baremetal driver
cleanup_ops_bm_node => "openstack", #internal command called from the baremetal driver
}
}
sub process_request {
my $request = shift;
my $callback = shift;
my $doreq = shift;
my $command = $request->{command}->[0];
if ($command eq "opsaddbmnode") {
return opsaddbmnode($request, $callback, $doreq);
} elsif ($command eq "opsaddimage") {
return opsaddimage($request, $callback, $doreq);
} elsif ($command eq "deploy_ops_bm_node") {
return deploy_ops_bm_node($request, $callback, $doreq);
} elsif ($command eq "cleanup_ops_bm_node") {
return cleanup_ops_bm_node($request, $callback, $doreq);
} else {
$callback->({error=>["Unsupported command: $command."],errorcode=>[1]});
return 1;
}
}
#-------------------------------------------------------------------------------
=head3 opsaddbmnode
This function takes the xCAT nodes and register them
as the OpenStack baremetal nodes
=cut
#-------------------------------------------------------------------------------
sub opsaddbmnode {
my $request = shift;
my $callback = shift;
my $doreq = shift;
@ARGV = @{$request->{arg}};
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $help;
my $version;
my $host;
if(!GetOptions(
'h|help' => \$help,
'v|version' => \$version,
's=s' => \$host,
))
{
&opsaddbmnode_usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($help) {
&opsaddbmnode_usage($callback);
return 0;
}
# display the version statement if -v or --verison is specified
if ($version)
{
my $rsp={};
$rsp->{data}->[0]= xCAT::Utils->Version();
$callback->($rsp);
return 0;
}
if (!$request->{node}) {
$callback->({error=>["Please specify at least one node."],errorcode=>[1]});
return 1;
}
if (!$host) {
$callback->({error=>["Please specify the OpenStack compute host name with -s flag."],errorcode=>[1]});
return 1;
}
my $nodes = $request->{node};
#get bmc info for the nodes
my $ipmitab = xCAT::Table->new("ipmi", -create => 0);
my $tmp_ipmi;
if ($ipmitab) {
$tmp_ipmi = $ipmitab->getNodesAttribs($nodes, ['bmc','username', 'password'], prefetchcache=>1);
#print Dumper($tmp_ipmi);
} else {
$callback->({error=>["Cannot open the ipmi table."],errorcode=>[1]});
return 1;
}
#get mac for the nodes
my $mactab = xCAT::Table->new("mac", -create => 0);
my $tmp_mac;
if ($mactab) {
$tmp_mac = $mactab->getNodesAttribs($nodes, ['mac'], prefetchcache=>1);
#print Dumper($tmp_mac);
} else {
$callback->({error=>["Cannot open the mac table."],errorcode=>[1]});
return 1;
}
#get cpu, memory and disk info for the nodes
my $hwinvtab = xCAT::Table->new("hwinv", -create => 0);
my $tmp_hwinv;
if ($hwinvtab) {
$tmp_hwinv = $hwinvtab->getNodesAttribs($nodes, ['cpucount', 'memory', 'disksize'], prefetchcache=>1);
#print Dumper($tmp_hwinv);
} else {
$callback->({error=>["Cannot open the hwinv table."],errorcode=>[1]});
return 1;
}
foreach my $node (@$nodes) {
#collect the node infomation needed for each node, some info
#may not be defined in the xCAT db
my ($bmc, $bmc_user, $bmc_password, $mac, $cpu, $memory, $disk);
my $ref_ipmi = $tmp_ipmi->{$node}->[0];
if ($ref_ipmi) {
if (exists($ref_ipmi->{bmc})) {
$bmc = $ref_ipmi->{bmc};
}
if (exists($ref_ipmi->{username})) {
$bmc_user = $ref_ipmi->{username};
}
if (exists($ref_ipmi->{password})) {
$bmc_password = $ref_ipmi->{password};
}
}
$ref_mac = $tmp_mac->{$node}->[0];
if ($ref_mac) {
if (exists($ref_mac->{mac})) {
$mac = $ref_mac->{mac};
}
}
$ref_hwinv = $tmp_hwinv->{$node}->[0];
if ($ref_hwinv) {
if (exists($ref_hwinv->{cpucount})) {
$cpu = $ref_hwinv->{cpucount};
}
if (exists($ref_hwinv->{memory})) {
$memory = $ref_hwinv->{memory};
#TODO: what if the unit is not in MB? We need to convert it to MB
$memory =~ s/MB|mb//g;
}
if (exists($ref_hwinv->{disksize})) {
#The format of the the disk size is: sda:250GB,sdb:250GB or just 250GB
#We need to get the size of the first one
$disk = $ref_hwinv->{disksize};
my @a = split(',', $disk);
my @b = split(':', $a[0]);
if (@b > 1) {
$disk = $b[1];
} else {
$disk = $b[0];
}
print "a=@a, b=@b\n";
#TODO: what if the unit is not in GB? We need to convert it to MB
$disk =~ s/GB|gb//g;
}
}
#some info are mendatory
if (!$mac) {
$callback->({error=>["Mac address is not defined in the mac table for node $node."],errorcode=>[1]});
next;
}
if (!$cpu) {
#default cpu count is 1
$cpu = 1;
}
if (!$memory) {
#default memory size is 1024MB=1GB
$memory = 1024;
}
if (!$disk) {
#default disk size is 1GB
$disk = 1;
}
#print "$bmc, $bmc_user, $bmc_password, $mac, $cpu, $memory, $disk\n";
#call OpenStack command to add the node into the OpenStack as
#a baremetal node.
my $cmd_tmp = "nova baremetal-node-create";
if ($bmc) {
#make sure it is an ip address
if (($bmc !~ /\d+\.\d+\.\d+\.\d+/) && ($bmc !~ /:/)) {
$bmc = xCAT::NetworkUtils->getipaddr($bmc);
}
$cmd_tmp .= " --pm_address=$bmc";
}
if ($bmc_user) {
$cmd_tmp .= " --pm_user=$bmc_user";
}
if ($bmc_password) {
$cmd_tmp .= " --pm_password=$bmc_password";
}
$cmd_tmp .= " $host $cpu $memory $disk $mac";
my $cmd = qq~source \~/openrc;$cmd_tmp~;
#print "cmd=$cmd\n";
my $output =
xCAT::InstUtils->xcmd($callback, $doreq, "xdsh", [$host], $cmd, 0);
if ($::RUNCMD_RC != 0) {
my $rsp;
push @{$rsp->{data}}, "OpenStack creating baremetal node $node:";
push @{$rsp->{data}}, "$output";
xCAT::MsgUtils->message("E", $rsp, $callback);
}
}
}
#-------------------------------------------------------------------------------
=head3 opsaddimage
This function takes the xCAT nodes and register them
as the OpenStack baremetal nodes
=cut
#-------------------------------------------------------------------------------
sub opsaddimage {
my $request = shift;
my $callback = shift;
my $doreq = shift;
@ARGV = @{$request->{arg}};
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $help;
my $version;
#my $cloud;
my $ops_img_names;
my $controller;
if(!GetOptions(
'h|help' => \$help,
'v|version' => \$version,
'c=s' => \$controller,
'n=s' => \$ops_img_names,
))
{
&opsaddimage_usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($help) {
&opsaddimage_usage($callback);
return 0;
}
# display the version statement if -v or --verison is specified
if ($version)
{
my $rsp={};
$rsp->{data}->[0]= xCAT::Utils->Version();
$callback->($rsp);
return 0;
}
if (@ARGV ==0) {
$callback->({error=>["Please specify an image name or a list of image names."],errorcode=>[1]});
return 1;
}
#make sure the input cloud name is valid.
#if (!$cloud) {
# $callback->({error=>["Please specify the name of the cloud with -c flag."],errorcode=>[1]});
# return 1;
#} else {
# my $cloudstab = xCAT::Table->new('clouds', -create => 0);
# my @et = $cloudstab->getAllAttribs('name', 'controller');
# if(@et) {
# foreach my $tmp_et (@et) {
# if ($tmp_et->{name} eq $cloud) {
# if ($tmp_et->{controller}) {
# $controller = $tmp_et->{controller};
# last;
# } else {
# $callback->({error=>["Please specify the controller in the clouds table for the cloud: $cloud."],errorcode=>[1]});
# return 1;
# }
# }
# }
# }
if (!$controller) {
$callback->({error=>["Please specify the OpenStack controller node name with -c."],errorcode=>[1]});
return 1;
}
#}
#make sure that the images from the command are valid image names
@images = split(',', $ARGV[0]);
@new_names = ();
if ($ops_img_names) {
@new_names = split(',', $ops_img_names);
}
#print "images=@images, new image names=@new_names, controller=$controller\n";
my $image_hash = {};
my $osimgtab = xCAT::Table->new('osimage', -create => 0);
my @et = $osimgtab->getAllAttribs('imagename');
if(@et) {
foreach my $tmp_et (@et) {
$image_hash->{$tmp_et->{imagename}}{'xCAT'} = 1;
}
}
my @bad_images;
foreach my $image (@images) {
if (!exists($image_hash->{$image})) {
push @bad_images, $image;
}
}
if (@bad_images > 0) {
$callback->({error=>["The following images cannot be found in xCAT osimage table:\n " . join("\n ", @bad_images) . "\n"],errorcode=>[1]});
return 1;
}
my $index=0;
foreach my $image (@images) {
my $new_name = shift(@new_names);
if (!$new_name) {
$new_name = $image; #the default new name is xCAT image name
}
my $cmd_tmp = "glance image-create --name $new_name --public --disk-format qcow2 --container-format bare --property xcat_image_name=\'$image\' < /tmp/$image.qcow2";
my $cmd = qq~touch /tmp/$image.qcow2;source \~/openrc;$cmd_tmp;rm /tmp/$image.qcow2~;
#print "cmd=$cmd\ncontroller=$controller\n";
my $output =
xCAT::InstUtils->xcmd($callback, $doreq, "xdsh", [$controller], $cmd, 0);
if ($::RUNCMD_RC != 0) {
my $rsp;
push @{$rsp->{data}}, "OpenStack creating image $new_name:";
push @{$rsp->{data}}, "$output";
xCAT::MsgUtils->message("E", $rsp, $callback);
}
}
}
#-------------------------------------------------------------------------------
=head3 deploy_ops_bm_node
This is a internel command called by OpenStack xCAT-baremetal driver.
It prepares the node by adding the config_ops_bm_node postbootscript
to the postscript table for the node, then call nodeset and then boot
the node up.
=cut
#-------------------------------------------------------------------------------
sub deploy_ops_bm_node {
my $request = shift;
my $callback = shift;
my $doreq = shift;
@ARGV = @{$request->{arg}};
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $node = $request->{node}->[0];
my $help;
my $version;
my $img_name;
my $hostname;
my $fixed_ip;
my $netmask;
if(!GetOptions(
'h|help' => \$help,
'v|version' => \$version,
'image=s' => \$img_name,
'host=s' => \$hostname,
'ip=s' => \$fixed_ip,
'mask=s' => \$netmask,
))
{
&deploy_ops_bm_node_usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($help) {
&deploy_ops_bm_node_usage($callback);
return 0;
}
# display the version statement if -v or --verison is specified
if ($version)
{
my $rsp={};
$rsp->{data}->[0]= xCAT::Utils->Version();
$callback->($rsp);
return 0;
}
print "node=$node, image=$img_name, host=$hostname, ip=$fixed_ip, mask=$netmask\n";
#validate the image name
my $osimagetab = xCAT::Table->new('osimage', -create=>1);
my $ref = $osimagetab->getAttribs({imagename => $img_name}, 'imagename');
if (!$ref) {
$callback->({error=>["Invalid image name: $img_name."],errorcode=>[1]});
return 1;
}
#check if the fixed ip is within the xCAT management network.
#get the master ip address for the node then check if the master ip and
#the OpenStack fixed_ip are on the same subnet.
#my $same_nw = 0;
#my $master = xCAT::TableUtils->GetMasterNodeName($node);
#my $master_ip = xCAT::NetworkUtils->toIP($master);
#if (xCAT::NetworkUtils::isInSameSubnet($master_ip, $fixed_ip, $netmask, 0)) {
# $same_nw = 1;
#}
#add config_ops_bm_node to the node's postbootscript
my $script = "config_ops_bm_node $hostname $fixed_ip $netmask";
add_postscript($callback, $node, $script);
#run nodeset
my $cmd = qq~osimage=$img_name~;
my $output = xCAT::Utils->runxcmd(
{command => ["nodeset"],
node => [$node],
arg => [$cmd]},
$doreq, -1, 1);
if ($::RUNCMD_RC != 0) {
my $rsp;
push @{$rsp->{data}}, "nodeset:";
push @{$rsp->{data}}, "$output";
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
}
#set boot order, assuming it is ipmi nodes for now
# TODO: add support for system power hw.
my $cmd = qq~net~;
my $output = xCAT::Utils->runxcmd(
{command => ["rsetboot"],
node => [$node],
arg => [$cmd]},
$doreq, -1, 1);
if ($::RUNCMD_RC != 0) {
my $rsp;
push @{$rsp->{data}}, "rsetboot:";
push @{$rsp->{data}}, "$output";
xCAT::MsgUtils->message("E", $rsp, $callback);
}
#reboot the node
my $cmd = qq~boot~;
my $output = xCAT::Utils->runxcmd(
{command => ["rpower"],
node => [$node],
arg => [$cmd]},
$doreq, -1, 1);
if ($::RUNCMD_RC != 0) {
my $rsp;
push @{$rsp->{data}}, "rpower:";
push @{$rsp->{data}}, "$output";
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
}
}
#-------------------------------------------------------------------------------
=head3 cleanup_ops_bm_node
This is a internel command called by OpenStack xCAT-baremetal driver.
It undoes all the changes made by deploy_ops_bm_node command. It removes
the config_ops_bmn_ode postbootscript from the postscript table for the
node, removes the alias ip and then power off the node.
=cut
#-------------------------------------------------------------------------------
sub cleanup_ops_bm_node {
my $request = shift;
my $callback = shift;
my $doreq = shift;
@ARGV = @{$request->{arg}};
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $node = $request->{node}->[0];
my $help;
my $version;
my $fixed_ip;
if(!GetOptions(
'h|help' => \$help,
'v|version' => \$version,
'ip=s' => \$fixed_ip,
))
{
&cleanup_ops_bm_node_usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($help) {
&cleanup_ops_bm_node_usage($callback);
return 0;
}
# display the version statement if -v or --verison is specified
if ($version)
{
my $rsp={};
$rsp->{data}->[0]= xCAT::Utils->Version();
$callback->($rsp);
return 0;
}
#print "node=$node, ip=$fixed_ip\n";
#removes the config_ops_bm_node postbootscript from the postscripts table
remove_postscript($callback, $node, "config_ops_bm_node");
#run updatenode to remove the ip alias
my $cmd = qq~-P deconfig_ops_bm_node $fixed_ip~;
my $output = xCAT::Utils->runxcmd(
{command => ["updatenode"],
node => [$node],
arg => [$cmd]},
$doreq, -1, 1);
if ($::RUNCMD_RC != 0) {
my $rsp;
push @{$rsp->{data}}, "updatenode:";
push @{$rsp->{data}}, "$output";
xCAT::MsgUtils->message("E", $rsp, $callback);
}
#turn the node power off
$ssh_ok = 0;
my $cmd = qq~stat~;
my $output = xCAT::Utils->runxcmd(
{command => ["rpower"],
node => [$node],
arg => [$cmd]},
$doreq, -1, 1);
if ($::RUNCMD_RC != 0) {
my $rsp;
push @{$rsp->{data}}, "rpower:";
push @{$rsp->{data}}, "$output";
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
} else {
if ($output !~ /: off/) {
#power off the node
my $cmd = qq~off~;
my $output = xCAT::Utils->runxcmd(
{command => ["rpower"],
node => [$node],
arg => [$cmd]},
$doreq, -1, 1);
if ($::RUNCMD_RC != 0) {
my $rsp;
push @{$rsp->{data}}, "rpower:";
push @{$rsp->{data}}, "$output";
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
}
}
}
}
#-------------------------------------------------------
=head3 add_postscript
It adds the 'config_ops_bm_node' postbootscript to the
postscript table for the given node.
=cut
#-------------------------------------------------------
sub add_postscript {
my $callback=shift;
my $node=shift;
my $script=shift;
print "script=$script\n";
my $posttab=xCAT::Table->new("postscripts", -create =>1);
my %setup_hash;
my $ref = $posttab->getNodeAttribs($node,[qw(postscripts postbootscripts)]);
my $found=0;
if ($ref) {
if (exists($ref->{postscripts})) {
my @a = split(/,/, $ref->{postscripts});
if (grep(/^config_ops_bm_node/, @a)) {
$found = 1;
if (!grep(/^$script$/, @a)) {
#not exact match, must replace it with the new script
for (@a) {
s/^config_ops_bm_node.*$/$script/;
}
my $new_post = join(',', @a);
$setup_hash{$node}={postscripts=>"$new_post"};
}
}
}
if (exists($ref->{postbootscripts})) {
my $post=$ref->{postbootscripts};
my @old_a=split(',', $post);
if (grep(/^config_ops_bm_node/, @old_a)) {
if (!grep(/^$script$/, @old_a)) {
#not exact match, will replace it with new script
for (@old_a) {
s/^config_ops_bm_node.*$/$script/;
}
my $new_postboot = join(',', @old_a);
$setup_hash{$node}={postbootscripts=>"$new_postboot"};
}
} else {
if (! $found) {
$setup_hash{$node}={postbootscripts=>"$post,$script"};
}
}
} else {
if (! $found) {
$setup_hash{$node}={postbootscripts=>"$script"};
}
}
} else {
$setup_hash{$node}={postbootscripts=>"$script"};
}
if (keys(%setup_hash) > 0) {
$posttab->setNodesAttribs(\%setup_hash);
}
return 0;
}
#-------------------------------------------------------
=head3 remove_postscript
It removes the 'config_ops_bm_node' postbootscript from
the postscript table for the given node.
=cut
#-------------------------------------------------------
sub remove_postscript {
my $callback=shift;
my $node=shift;
my $script=shift;
my $posttab=xCAT::Table->new("postscripts", -create =>1);
my %setup_hash;
my $ref = $posttab->getNodeAttribs($node,[qw(postscripts postbootscripts)]);
my $found=0;
if ($ref) {
if (exists($ref->{postscripts})) {
my @old_a = split(/,/, $ref->{postscripts});
my @new_a = grep(!/^$script/, @old_a);
if (scalar(@new_a) != scalar(@old_a)) {
my $new_post = join(',', @new_a);
$setup_hash{$node}={postscripts=>"$new_post"};
}
}
if (exists($ref->{postbootscripts})) {
my @old_b = split(/,/, $ref->{postbootscripts});
my @new_b = grep(!/^$script/, @old_b);
if (scalar(@new_b) != scalar(@old_b)) {
my $new_post = join(',', @new_b);
$setup_hash{$node}={postbootscripts=>"$new_post"};
}
}
}
if (keys(%setup_hash) > 0) {
$posttab->setNodesAttribs(\%setup_hash);
}
return 0;
}
#-------------------------------------------------------------------------------
=head3 opsaddbmnode_usage
The usage text for opsaddbmnode command.
=cut
#-------------------------------------------------------------------------------
sub opsaddbmnode_usage {
my $cb=shift;
my $rsp={};
$rsp->{data}->[0]= "Usage: opsaddbmnode -h";
$rsp->{data}->[1]= " opsaddbmnode -v";
$rsp->{data}->[2]= " opsaddbmnode <noderange> -s <service_host>";
$cb->($rsp);
}
#-------------------------------------------------------------------------------
=head3 opsaddimage_usage
The usage text for opsaddimage command.
=cut
#-------------------------------------------------------------------------------
sub opsaddimage_usage {
my $cb=shift;
my $rsp={};
$rsp->{data}->[0]= "Usage: opsaddimage -h";
$rsp->{data}->[1]= " opsaddimage -v";
$rsp->{data}->[2]= " opsaddimage <image1,image2...> [-n <new_name1,new_name2...> -c <controller>";
$cb->($rsp);
}
#-------------------------------------------------------------------------------
=head3 deploy_ops_bm_node_usage
The usage text for deploy_ops_bm_node command.
=cut
#-------------------------------------------------------------------------------
sub deploy_ops_bm_node_usage {
my $cb=shift;
my $rsp={};
$rsp->{data}->[0]= "Usage: deploy_ops_bm_node -h";
$rsp->{data}->[1]= " deploy_ops_bm_node -v";
$rsp->{data}->[2]= " deploy_ops_bm_node <node> --image <image_name> --host <ops_hostname> --ip <ops_fixed_ip> --mask <netmask>";
$cb->($rsp);
}
#-------------------------------------------------------------------------------
=head3 cleanup_ops_bm_node_usage
The usage text cleanup_ops_bm_node command.
=cut
#-------------------------------------------------------------------------------
sub cleanup_ops_bm_node_usage {
my $cb=shift;
my $rsp={};
$rsp->{data}->[0]= "Usage: cleanup_ops_bm_node -h";
$rsp->{data}->[1]= " cleanup_ops_bm_node -v";
$rsp->{data}->[2]= " cleanup_ops_bm_node <node> [--ip <ops_fixed_ip>]";
$cb->($rsp);
}
1;

View File

@ -0,0 +1,17 @@
# Copyright (c) 2012 NTT DOCOMO, 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.
from xcat.openstack.baremetal import driver
BareMetalDriver = driver.xCATBareMetalDriver

View File

@ -0,0 +1,255 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# coding=utf-8
"""
A driver for Bare-metal platform.
"""
from oslo.config import cfg
from nova.compute import power_state
from nova import context as nova_context
from nova import exception
from nova.openstack.common import excutils
from nova.openstack.common.gettextutils import _
from nova.openstack.common import importutils
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova.virt.baremetal import baremetal_states
from nova.virt.baremetal import db
from nova.virt.baremetal import driver as bm_driver
from nova.virt.baremetal import utils as bm_utils
from nova.virt import driver
from nova.virt import firewall
from nova.virt.libvirt import imagecache
from xcat.openstack.baremetal import xcat_driver
from xcat.openstack.baremetal import exception as xcat_exception
from xcat.openstack.baremetal import power_states
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
CONF.import_opt('use_ipv6', 'nova.netconf')
class xCATBareMetalDriver(bm_driver.BareMetalDriver):
"""BareMetal hypervisor driver."""
def __init__(self, virtapi, read_only=False):
super(xCATBareMetalDriver, self).__init__(virtapi)
self.xcat = xcat_driver.xCAT()
def _get_xCAT_image_name(self, image_meta):
prop = image_meta.get('properties')
xcat_image_name = prop.get('xcat_image_name')
if xcat_image_name:
return xcat_image_name
else:
raise xcat_exception.xCATInvalidImageError(image=image_meta.get('name'))
def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info=None, block_device_info=None):
"""
Create a new instance/VM/domain on the virtualization platform.
Once this successfully completes, the instance should be
running (power_state.RUNNING).
If this fails, any partial instance should be completely
cleaned up, and the virtualization platform should be in the state
that it was before this call began.
:param context: security context
:param instance: Instance object as returned by DB layer.
This function should use the data there to guide
the creation of the new instance.
:param image_meta: image object returned by nova.image.glance that
defines the image from which to boot this instance
:param injected_files: User files to inject into instance.
:param admin_password: Administrator password to set in instance.
:param network_info:
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
:param block_device_info: Information about block devices to be
attached to the instance.
"""
import pdb
pdb.set_trace()
node_uuid = self._require_node(instance)
node = db.bm_node_associate_and_update(context, node_uuid,
{'instance_uuid': instance['uuid'],
'instance_name': instance['hostname'],
'task_state': baremetal_states.BUILDING})
try:
self._plug_vifs(instance, network_info, context=context)
self._attach_block_devices(instance, block_device_info)
self._start_firewall(instance, network_info)
macs = self.macs_for_instance(instance)
nodename = self.xcat.get_xcat_node_name(macs)
imagename = self._get_xCAT_image_name(image_meta)
hostname = instance.get('hostname')
#get the network information for the new node
interfaces = bm_utils.map_network_interfaces(network_info, CONF.use_ipv6)
if CONF.use_ipv6:
fixed_ip = interfaces[0].get('address_v6')
netmask = interfaces[0].get('netmask_v6')
gateway = interfaces[0].get('gateway_v6')
else:
fixed_ip = interfaces[0].get('address')
netmask = interfaces[0].get('netmask')
gateway = interfaces[0].get('gateway')
#convert netmask from IPAddress to unicode string
if netmask:
netmask = unicode(netmask)
#let xCAT install it
bm_driver._update_state(context, node, instance, baremetal_states.DEPLOYING)
self.xcat.deploy_node(nodename, imagename, hostname, fixed_ip, netmask, gateway)
bm_driver._update_state(context, node, instance, baremetal_states.ACTIVE)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error(_("Error occured while deploying instance %(instance)s "
"on baremetal node %(node)s: %(error)s") %
{'instance': instance['uuid'],
'node': node['uuid'],
'error':str(e)})
bm_driver._update_state(context, node, instance, baremetal_states.ERROR)
def reboot(self, context, instance, network_info, reboot_type,
block_device_info=None, bad_volumes_callback=None):
"""Reboot the specified instance.
After this is called successfully, the instance's state
goes back to power_state.RUNNING. The virtualization
platform should ensure that the reboot action has completed
successfully even in cases in which the underlying domain/vm
is paused or halted/stopped.
:param instance: Instance object as returned by DB layer.
:param network_info:
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
:param reboot_type: Either a HARD or SOFT reboot
:param block_device_info: Info pertaining to attached volumes
:param bad_volumes_callback: Function to handle any bad volumes
encountered
"""
try:
node = bm_driver._get_baremetal_node_by_instance_uuid(instance['uuid'])
macs = self.macs_for_instance(instance)
nodename = self.xcat.get_xcat_node_name(macs)
self.xcat.reboot_node(nodename)
bm_driver._update_state(context, node, instance, baremetal_states.RUNNING)
except xcat_exception.xCATCommandError as e:
with excutils.save_and_reraise_exception():
LOG.error(_("Error occured while rebooting instance %(instance)s "
"on baremetal node %(node)s: %(error)s") %
{'instance': instance['uuid'],
'node': node['uuid'],
'error':str(e)})
bm_driver._update_state(context, node, instance, baremetal_states.ERROR)
def destroy(self, context, instance, network_info, block_device_info=None,
destroy_disks=True):
"""Destroy (shutdown and delete) the specified instance.
If the instance is not found (for example if networking failed), this
function should still succeed. It's probably a good idea to log a
warning in that case.
:param context: security context
:param instance: Instance object as returned by DB layer.
:param network_info:
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
:param block_device_info: Information about block devices that should
be detached from the instance.
:param destroy_disks: Indicates if disks should be destroyed
"""
#import pdb
#pdb.set_trace()
try:
node = bm_driver._get_baremetal_node_by_instance_uuid(instance['uuid'])
except exception.InstanceNotFound:
LOG.warning(_("Destroy function called on a non-existing instance %s")
% instance['uuid'])
return
try:
macs = self.macs_for_instance(instance)
nodename = self.xcat.get_xcat_node_name(macs)
interfaces = bm_utils.map_network_interfaces(network_info, CONF.use_ipv6)
fixed_ip=None
if interfaces and interfaces[0]:
if CONF.use_ipv6:
fixed_ip = interfaces[0].get('address_v6')
else:
fixed_ip = interfaces[0].get('address')
if fixed_ip:
self.xcat.cleanup_node(nodename, fixed_ip)
else:
self.xcat.cleanup_node(nodename)
except Exception as e:
#just log it and move on
LOG.warning(_("Destroy called with xCAT error:" + str(e)))
try:
self._detach_block_devices(instance, block_device_info)
self._stop_firewall(instance, network_info)
self._unplug_vifs(instance, network_info)
bm_driver._update_state(context, node, None, baremetal_states.DELETED)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error(_("Error occurred while destroying instance %s: %s")
% (instance['uuid'], str(e)))
bm_driver._update_state(context, node, instance,
baremetal_states.ERROR)
def power_off(self, instance, node=None):
"""Power off the specified instance."""
macs = self.macs_for_instance(instance)
nodename = self.xcat.get_xcat_node_name(macs)
self.xcat.power_off_node(nodename)
def power_on(self, context, instance, network_info, block_device_info=None,
node=None):
"""Power on the specified instance."""
macs = self.macs_for_instance(instance)
nodename = self.xcat.get_xcat_node_name(macs)
self.xcat.power_on_node(nodename)
def get_console_output(self, instance):
pass
def get_info(self, instance):
"""Get the current status of an instance, by name (not ID!)
Returns a dict containing:
:state: the running state, one of the power_state codes
:max_mem: (int) the maximum memory in KBytes allowed
:mem: (int) the memory in KBytes used by the domain
:num_cpu: (int) the number of virtual CPUs for the domain
:cpu_time: (int) the CPU time used in nanoseconds
"""
node = bm_driver._get_baremetal_node_by_instance_uuid(instance['uuid'])
macs = self.macs_for_instance(instance)
nodename = self.xcat.get_xcat_node_name(macs)
ps = self.xcat.get_node_power_state(nodename)
if ps == power_states.ON:
pstate = power_state.RUNNING
elif ps == power_states.OFF:
pstate = power_state.SHUTDOWN
else:
pstate = power_state.NOSTATE
return {'state': pstate,
'max_mem': node['memory_mb'],
'mem': node['memory_mb'],
'num_cpu': node['cpus'],
'cpu_time': 0}

View File

@ -0,0 +1,41 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
"""xCAT baremtal exceptions.
"""
import functools
import sys
from oslo.config import cfg
import webob.exc
from nova.openstack.common import excutils
from nova.openstack.common.gettextutils import _
from nova.openstack.common import log as logging
from nova import safe_utils
from nova import exception as nova_exception
LOG = logging.getLogger(__name__)
class xCATException(Exception):
errmsg = _("xCAT general exception")
def __init__(self, errmsg=None, **kwargs):
if not errmsg:
errmsg = self.errmsg
errmsg = errmsg % kwargs
super(xCATException, self).__init__(errmsg)
class xCATCommandError(xCATException):
errmsg = _("Error returned when calling xCAT command %(cmd)s"
" for node %(node)s:%(error)s")
class xCATInvalidImageError(xCATException):
errmsg = _("The image %(image)s is not an xCAT image")
class xCATDeploymentFailure(xCATException):
errmsg = _("xCAT node deployment failed for node %(node)s:%(error)s")
class xCATRebootFailure(xCATException):
errmsg = _("xCAT node rebooting failed for node %(node)s:%(error)s")

View File

@ -0,0 +1,9 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
"""
Possible xCAT node power states.
"""
OFF = 'off'
ON = 'on'
ERROR = 'error'

View File

@ -0,0 +1,257 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# coding=utf-8
"""
Baremetal xCAT power manager.
"""
import os
import sys
import stat
from oslo.config import cfg
import datetime
from nova import context as nova_context
from nova.virt.baremetal import baremetal_states
from nova.openstack.common.gettextutils import _
from nova.openstack.common import log as logging
from nova.openstack.common import loopingcall
from nova.openstack.common import timeutils
from nova import paths
from nova import utils
from xcat.openstack.baremetal import exception
from xcat.openstack.baremetal import power_states
LOG = logging.getLogger(__name__)
# register configuration options
xcat_opts = [
cfg.IntOpt('deploy_timeout',
help='Timeout for node deployment. Default: 0 second (unlimited)',
default=0),
cfg.IntOpt('reboot_timeout',
help='Timeout for rebooting a node. Default: 0 second (unlimited)',
default=0),
cfg.IntOpt('deploy_checking_interval',
help='Checking interval for node deployment. Default: 10 seconds',
default=10),
cfg.IntOpt('reboot_checking_interval',
help='Checking interval for rebooting a node. Default: 5 seconds',
default=5),
]
xcat_group = cfg.OptGroup(name='xcat',
title='xCAT Options')
CONF = cfg.CONF
CONF.register_group(xcat_group)
CONF.register_opts(xcat_opts, xcat_group)
class xCAT(object):
"""A driver that calls xCAT funcions"""
def __init__(self):
#setup the path for xCAT commands
#xcatroot = os.getenv('XCATROOT', '/opt/xcat/')
#sys.path.append("%s/bin" % xcatroot)
#sys.path.append("%s/sbin" % xcatroot)
pass
def _exec_xcat_command(self, command):
"""Calls xCAT command."""
args = command.split(" ")
out, err = utils.execute(*args, run_as_root=True)
LOG.debug(_("xCAT command stdout: '%(out)s', stderr: '%(err)s'"),
{'out': out, 'err': err})
return out, err
def get_xcat_node_name(self, macs):
"""Get the xcat node name given mac addressed.
It uses the mac address to search for the node name in xCAT.
"""
for mac in macs:
out, err = self._exec_xcat_command("lsdef -w mac=%s" % mac)
if out:
return out.split(" ")[0]
errstr='No node found in xCAT with the following mac address: ' \
+ ','.join(macs)
LOG.warning(errstr)
raise exception.xCATCommandError(errstr)
def deploy_node(self, nodename, imagename, hostname, fixed_ip, netmask, gateway):
"""
Install the node.
It calls xCAT command deploy_ops_bmnode which prepares the node
by adding the config_ops_bm_node postbootscript to the postscript
table for the node, then call nodeset and then boot the node up.
"""
out, err = self._exec_xcat_command(
"deploy_ops_bm_node %(node)s --image %(image)s"
" --host %(host)s --ip %(ip)s --mask %(mask)s"
% {'node': nodename,
'image': imagename,
'host': hostname,
'ip': fixed_ip,
'mask': netmask,
})
if err:
errstr = _("Error returned when calling xCAT deploy_ops_bm_node"
" command for node %s:%s") % (nodename, err)
LOG.warning(errstr)
raise exception.xCATCommandError(errstr)
self._wait_for_node_deploy(nodename)
def cleanup_node(self, nodename, fixed_ip=None):
"""
Undo all the changes made to the node by deploy_node function.
It calls xCAT command cleanup_ops_bm_node which removes the
config_ops_bm_node postbootscript from the postscript table
for the node, removes the alias ip and then power the node off.
"""
cmd = "cleanup_ops_bm_node %s" % nodename
if fixed_ip:
cmd += " --ip %s" % fixed_ip
out, err = self._exec_xcat_command(cmd)
if err:
errstr = _("Error returned when calling xCAT cleanup_ops_bm_node"
" command for node %s:%s") % (nodename, err)
LOG.warning(errstr)
raise exception.xCATCommandError(errstr)
def power_on_node(self, nodename):
"""Power on the node."""
state = self.get_node_power_state(nodename)
if state == power_states.ON:
LOG.warning(_("Powring on node called, but the node %s "
"is already on") % nodename)
out, err = self._exec_xcat_command("rpower %s on" % nodename)
if err:
errstr = _("Error returned when calling xCAT rpower on"
" for node %s:%s") % (nodename, err)
LOG.warning(errstr)
raise exception.xCATCommandError(errstr)
else:
return power_states.ON
def power_off_node(self, nodename):
"""Power off the node."""
state = self.get_node_power_state(nodename)
if state == power_states.OFF:
LOG.warning(_("Powring off node called, but the node %s "
"is already off") % nodename)
out, err = self._exec_xcat_command("rpower %s off" % nodename)
if err:
errstr = _("Error returned when calling xCAT rpower off"
" for node %s:%s") % (nodename, err)
LOG.warning(errstr)
raise exception.xCATCommandError(errstr)
else:
return power_states.OFF
def reboot_node(self, nodename):
"""Reboot the node."""
out, err = self._exec_xcat_command("rpower %s boot" % nodename)
if err:
errstr = _("Error returned when calling xCAT rpower boot"
" for node %s:%s") % (nodename, err)
LOG.warning(errstr)
raise exception.xCATCommandError(errstr)
self._wait_for_node_reboot(nodename)
return power_states.ON
def get_node_power_state(self, nodename):
out, err = self._exec_xcat_command("rpower %s stat" % nodename)
if err:
errstr = _("Error returned when calling xCAT rpower stat"
" for node %s:%s") % (nodename, err)
LOG.warning(errstr)
raise exception.xCATCommandError(errstr)
else:
state = out.split(":")[1]
if state:
state = state.strip()
if state == 'on':
return power_states.ON
elif state == 'off':
return power_states.OFF
return power_states.ERROR
def _wait_for_node_deploy(self, nodename):
"""Wait for xCAT node deployment to complete."""
locals = {'errstr':''}
def _wait_for_deploy():
out,err = self._exec_xcat_command("nodels %s nodelist.status" % nodename)
if err:
locals['errstr'] = _("Error returned when quering node status"
" for node %s:%s") % (nodename, err)
LOG.warning(locals['errstr'])
raise loopingcall.LoopingCallDone()
if out:
node,status = out.split(": ")
if status == "booted":
LOG.info(_("Deployment for node %s completed.")
% nodename)
raise loopingcall.LoopingCallDone()
if (CONF.xcat.deploy_timeout and
timeutils.utcnow() > expiration):
locals['errstr'] = _("Timeout while waiting for"
" deployment of node %s.") % nodename
LOG.warning(locals['errstr'])
raise loopingcall.LoopingCallDone()
expiration = timeutils.utcnow() + datetime.timedelta(
seconds=CONF.xcat.deploy_timeout)
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_deploy)
# default check every 10 seconds
timer.start(interval=CONF.xcat.deploy_checking_interval).wait()
if locals['errstr']:
raise exception.xCATDeploymentFailure(locals['errstr'])
def _wait_for_node_reboot(self, nodename):
"""Wait for xCAT node boot to complete."""
locals = {'errstr':''}
def _wait_for_reboot():
out,err = self._exec_xcat_command("nodels %s nodelist.status" % nodename)
if err:
locals['errstr'] = _("Error returned when quering node status"
" for node %s:%s") % (nodename, err)
LOG.warning(locals['errstr'])
raise loopingcall.LoopingCallDone()
if out:
node,status = out.split(": ")
if status == "booted":
LOG.info(_("Rebooting node %s completed.")
% nodename)
raise loopingcall.LoopingCallDone()
if (CONF.xcat.reboot_timeout and
timeutils.utcnow() > expiration):
locals['errstr'] = _("Timeout while waiting for"
" rebooting node %s.") % nodename
LOG.warning(locals['errstr'])
raise loopingcall.LoopingCallDone()
expiration = timeutils.utcnow() + datetime.timedelta(
seconds=CONF.xcat.reboot_timeout)
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_reboot)
# default check every 5 seconds
timer.start(interval=CONF.xcat.reboot_checking_interval).wait()
if locals['errstr']:
raise exception.xCATRebootFailure(locals['errstr'])

View File

@ -0,0 +1,75 @@
=head1 NAME
B<opsaddbmnode> - It adds xCAT baremetal nodes to an OpenStack cloud.
=head1 SYNOPSIS
B<opsaddbmnode> I<noderange> B<-s> I<service_host>
B<opsaddbmnode> [B<-h>|B<--help>]
B<opsaddbmnode> [B<-v>|B<--version>]
=head1 DESCRIPTION
The B<opsaddbmnode> command registers xCAT nodes to an OpenStack cloud.
An OpenStack nova baremetal node registration command takes several node attributes:
=item BMC ip addresss, user id and password
=item Name of nova compute host which will control this baremetal node
=item Number of CPUs in the node
=item Memory in the node (MB)
=item Local hard disk in the node (GB)
=item MAC address to provision the node
The opsaddbmnode command pulls the above baremetal node information from xCAT tables and calls "nova baremetal-node-create" to register the baremetal node with the OpenStack cloud.
Please make sure the following xCAT tables are filled with correct information for the given nodes before calling this command.
=item ipmi (for BMC ip addresss, user id and password)
=item mac (for MAC address)
=item hwinv (for CPU, memory and disk info.)
=head1 Parameters
I<noderage> is a comma separated node or node group names.
=head1 OPTIONS
=over 10
=item B<-s> The node name of the OpenStack compute host that hosts the baremetal nodes.
=item B<-h|--help> Display usage message.
=item B<-v|--version> The Command Version.
=back
=head1 RETURN VALUE
0 The command completed successfully.
1 An error has occurred.
=head1 EXAMPLES
=over 3
=item 1.
To register node1, node2 and node3 to OpenStack, sv1 is the compute host.
opsassbmnode node1,node2,node3 -s sv1
=back
=head1 FILES
/opt/xcat/bin/opadddbmnode
=head1 SEE ALSO
L<opsaddimage(1)|opsaddimage.1>

View File

@ -0,0 +1,65 @@
=head1 NAME
B<opsaddimage> - It adds or removes nodes for the vlan.
=head1 SYNOPSIS
B<opsaddimage> I<image1,image2,...> B<-n> I<new_name1,new_name2,...> [B<-c> I<controller>]
B<opsaddimage> [B<-h>|B<--help>]
B<opsaddimage> [B<-v>|B<--version>]
=head1 DESCRIPTION
The B<opsaddimage> command adds a list of xCAT images into the OpenStack cloud.
Under the cover, it creates a fake imgae and registers the fake image into OpenStack with command B<glance image-create>. It sets the property in the image to indicate that this is an xCAT image and also stores the original xCAT image name in the property for further reference.
The xCAT image names can be listed using B<lsdef -t osimage> command.
=head1 Parameters
I<image1,image1...> a comma separated xCAT images names.
=head1 OPTIONS
=over 10
=item B<-n> a comma separated new image names in the OpenStack. If omitted, the default is the original xCAT image nanme.
=item B<-c> the node name of the OpenStack controller. This node must be an xCAT managed node.
=item B<-h|--help> Display usage message.
=item B<-v|--version> The Command Version.
=back
=head1 RETURN VALUE
0 The command completed successfully.
1 An error has occurred.
=head1 EXAMPLES
=over 3
=item 1.
To register xCAT image rhels6.3-x86_64-install-compute into OpenStack.
opsaddimage rhels6.3-x86_64-install-compute -c sv2
=back
=head1 FILES
/opt/xcat/bin/opsaddimage
=head1 SEE ALSO
L<opsaddbmnode(1)|opsaddbmnode.1>

View File

@ -0,0 +1,187 @@
#!/bin/sh
# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html
# xCAT post script for configuring the openstack baremetal node.
# The format is:
# config_ops_bm_node ops_hostname ops_ip ops_netmask
get_os_type()
{
#get os type
str_os_type=`uname | tr 'A-Z' 'a-z'`
str_temp=''
if [ "$str_os_type" = "linux" ];then
str_temp=`echo $OSVER | grep -E '(sles|suse)'`
if [ -f "/etc/debian_version" ];then
str_os_type="debian"
elif [ -f "/etc/SuSE-release" -o -n "$str_temp" ];then
str_os_type="sles"
else
str_os_type="redhat"
fi
else
str_os_type="aix"
fi
echo "$str_os_type"
}
setup_ip()
{
str_os_type=$1
str_if_name=$2
str_v4ip=$3
str_v4mask=$4
ret=`ifconfig $str_if_name |grep "inet addr" 2>&1`
if [ $? -eq 0 ]; then
old_ip=`echo $ret|cut -d':' -f2 |cut -d' ' -f1`
old_mask=`echo $ret|cut -d':' -f4`
#echo "old ip = $old_ip, old mask=$old_mask"
if [ "$old_ip" == "$str_v4ip" -a "$old_mask" == "$str_v4mask" ]; then
#if nic is up and the address is the same, then donothing
#echo "do nothing"
exit 0
else
#bring down the nic and reconstruct it.
#echo "bring down the old nic"
ifconfig $str_if_name del $old_ip
fi
fi
if [ "$str_os_type" = "sles" ];then
str_conf_file="/etc/sysconfig/network/ifcfg-${str_if_name}"
if [ -f $str_conf_file ]; then
rm $str_conf_file
fi
echo "DEVICE=${str_if_name}" > $str_conf_file
echo "BOOTPROTO=static" >> $str_conf_file
echo "IPADDR=${str_v4ip}" >> $str_conf_file
echo "NETMASK=${str_v4mask}" >> $str_conf_file
echo "NETWORK=''" >> $str_conf_file
echo "STARTMODE=onboot" >> $str_conf_file
echo "USERCONTROL=no" >> $str_conf_file
ifup $str_if_name
#debian ubuntu
elif [ "$str_os_type" = "debian" ];then
str_conf_file="/etc/network/interfaces.d/${str_if_name}"
if [ -f $str_conf_file ]; then
rm $str_conf_file
fi
echo "auto ${str_if_name}" > $str_conf_file
echo "iface ${str_if_name} inet static" >> $str_conf_file
echo " address ${str_v4ip}" >> $str_conf_file
echo " netmask ${str_v4mask}" >> $str_conf_file
ifconfig $str_if_name up
else
# Write the info to the ifcfg file for redhat
str_conf_file="/etc/sysconfig/network-scripts/ifcfg-${str_if_name}"
if [ -f $str_conf_file ]; then
rm $str_conf_file
fi
echo "DEVICE=${str_if_name}" > $str_conf_file
echo "BOOTPROTO=static" >> $str_conf_file
echo "NM_CONTROLLED=no" >> $str_conf_file
echo "IPADDR=${str_v4ip}" >> $str_conf_file
echo "NETMASK=${str_v4mask}" >> $str_conf_file
echo "ONBOOT=yes" >> $str_conf_file
ifup $str_if_name
fi
}
str_os_type=$(get_os_type)
echo "os_type=$str_os_type"
if [ "$str_os_type" = "aix" ]; then
logger -t xcat "config_ops_bm_node dose not support AIX."
echo "config_ops_bm_node dose not support AIX."
exit 0
fi
#change the hostname
if [[ -n "$1" ]]; then
hostname $1
fi
#Add the openstack ip to the node
if [[ -n $2 ]]; then
ops_ip=$2
if [[ -z $3 ]]; then
logger -t xcat "config_ops_bm_node: Please specify the netmask."
echo "config_ops_bm_node: Please specify the netmask."
exit 1
else
ops_mask=$3
fi
#figure out the install nic
if [[ -n $MACADDRESS ]]; then
pos=0
#mac has the following format: 01:02:03:04:05:0E!node5|01:02:03:05:0F!node6-eth1
for x in `echo "$MACADDRESS" | tr "|" "\n"`
do
node=""
mac=""
pos=$((pos+1))
i=`expr index $x !`
if [[ $i -gt 0 ]]; then
node=`echo ${x##*!}`
mac_tmp=`echo ${x%%!*}`
else
mac_tmp=$x
fi
if [[ $pos -eq 1 ]]; then
mac1=$mac_tmp
fi
if [[ "$PRIMARYNIC" = "$mac_tmp" ]]; then
mac=$mac_tmp
break
fi
if [[ -z "$PRIMARYNIC" ]] || [[ "$PRIMARYNIC" = "mac" ]]; then
if [[ -z $node ]] || [[ "$node" = "$NODE" ]]; then
mac=$mac_tmp
break
fi
fi
done
if [[ -z $mac ]]; then
if [[ -z "$PRIMARYNIC" ]] || [[ "$PRIMARYNIC" = "mac" ]]; then
mac=$mac1 #if nothing mathes, take the first mac
else
nic=$PRIMARYNIC #or the primary nic itself is the nic
fi
fi
else
logger -t xcat "config_ops_bm_node: no mac addresses are defined in the mac table for the node $NODE"
echo "config_ops_bm_node: no mac addresses are defined in the mac table for the node $NODE"
index=$((index+1))
continue
fi
echo "mac=$mac"
#find the nic that has the mac
if [[ -z $nic ]]; then
#go to each nic to match the mac address
ret=`ifconfig |grep -i $mac 2>&1`;
if [ $? -eq 0 ]; then
nic=`echo $ret |head -n1|cut -d' ' -f 1`
else
logger -t xcat "config_ops_bm_node: The mac address for the network for $NODE is not defined."
echo "config_ops_bm_node: The mac address for the network for $NODE is not defined."
fi
fi
echo "nic=$nic"
#now setup the ip alias
setup_ip $str_os_type $nic:0 $ops_ip $ops_mask
fi

View File

@ -0,0 +1,65 @@
#!/bin/sh
# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html
# xCAT post script for deconfiguring the openstack baremetal node.
# The format is:
# deconfig_ops_bm_node ops_ip
get_os_type()
{
#get os type
str_os_type=`uname | tr 'A-Z' 'a-z'`
str_temp=''
if [ "$str_os_type" = "linux" ];then
str_temp=`echo $OSVER | grep -E '(sles|suse)'`
if [ -f "/etc/debian_version" ];then
str_os_type="debian"
elif [ -f "/etc/SuSE-release" -o -n "$str_temp" ];then
str_os_type="sles"
else
str_os_type="redhat"
fi
else
str_os_type="aix"
fi
echo "$str_os_type"
}
str_os_type=$(get_os_type)
echo "os_type=$str_os_type"
if [ $str_os_type == "aix" ]; then
logger -t xcat "deconfig_ops_bm_node dose not support AIX."
echo "deconfig_ops_bm_node dose not support AIX."
exit 0
fi
#change the hostname
hostname $NODE
#remove the openstack ip from the node
if [[ -n $1 ]]; then
ops_ip=$1
nic=$(ip addr | grep $ops_ip | awk '{print $NF}')
echo "nic=$nic, ops_ip=$ops_ip"
ifconfig $nic del $ops_ip
#delete the configuration file
if [ "$str_os_type" = "sles" ]; then
str_conf_file="/etc/sysconfig/network/ifcfg-$nic"
elif [ "$str_os_type" = "debian" ]; then #debian ubuntu
str_conf_file="/etc/network/interfaces.d/$nic"
else #redhat
str_conf_file="/etc/sysconfig/network-scripts/ifcfg-$nic"
fi
if [ -f $str_conf_file ]; then
rm $str_conf_file
fi
fi

View File

@ -0,0 +1,103 @@
Summary: Executables and data of the xCAT baremetal driver for OpenStack
Name: xCAT-OpenStack-baremetal
Version: %(cat Version)
Release: snap%(date +"%Y%m%d%H%M")
Epoch: 4
License: IBM
Group: Applications/System
Source: xCAT-OpenStack-baremetal-%{version}.tar.gz
Packager: IBM Corp.
Vendor: IBM Corp.
Distribution: %{?_distribution:%{_distribution}}%{!?_distribution:%{_vendor}}
Prefix: /opt/xcat
BuildRoot: /var/tmp/%{name}-%{version}-%{release}-root
%ifos linux
BuildArch: noarch
%endif
Provides: xCAT-OpenStack-baremetal = %{epoch}:%{version}
Requires: xCAT-client
%description
xCAT-OpenStack-baremetal provides the baremetal driver for OpenStack.
%prep
%setup -q -n xCAT-OpenStack-baremetal
%build
# Convert pods to man pages and html pages
./xpod2man
%install
# The install phase puts all of the files in the paths they should be in when the rpm is
# installed on a system. The RPM_BUILD_ROOT is a simulated root file system and usually
# has a value like: /var/tmp/xCAT-OpenStack-baremetal-2.0-snap200802270932-root
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/%{prefix}/bin
mkdir -p $RPM_BUILD_ROOT/%{prefix}/sbin
mkdir -p $RPM_BUILD_ROOT/%{prefix}/lib/perl/xCAT_plugin
mkdir -p $RPM_BUILD_ROOT/%{prefix}/lib/python/xcat/openstack/baremetal
mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/xcat/openstack/postscripts
mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/man/man1
mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/doc/man1
set +x
cp -R lib/* $RPM_BUILD_ROOT/%{prefix}/lib
cp share/xcat/openstack/postscripts/* $RPM_BUILD_ROOT/%{prefix}/share/xcat/openstack/postscripts
# These were built dynamically in the build phase
cp share/man/man1/* $RPM_BUILD_ROOT/%{prefix}/share/man/man1
chmod 444 $RPM_BUILD_ROOT/%{prefix}/share/man/man1/*
# These were built dynamically during the build phase
cp share/doc/man1/* $RPM_BUILD_ROOT/%{prefix}/share/doc/man1
chmod 644 $RPM_BUILD_ROOT/%{prefix}/share/doc/man1/*
# These links get made in the RPM_BUILD_ROOT/prefix area
ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/sbin/deploy_ops_bm_node
ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/sbin/cleanup_ops_bm_node
ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/bin/opsaddbmnode
ln -sf ../bin/xcatclientnnr $RPM_BUILD_ROOT/%{prefix}/bin/opsaddimage
set -x
%clean
# This step does not happen until *after* the %files packaging below
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
#%doc LICENSE.html
# Just package everything that has been copied into RPM_BUILD_ROOT
%{prefix}
%changelog
%post
#copy the postscripts under /installl/postscripts directory on MN only
if [ -f "/etc/xCATMN" ]; then
cp $RPM_INSTALL_PREFIX0/share/xcat/openstack/postscripts/* /install/postscripts
fi
exit 0
%preun
#remove postscripts under /installl/postscripts directory on MN only
if [ -f "/etc/xCATMN" ]; then
for fn in $RPM_INSTALL_PREFIX0/share/xcat/openstack/postscripts/*
do
bn=`basename $fn`
rm /install/postscripts/$bn
done
fi
exit 0

213
xCAT-OpenStack-baremetal/xpod2man Executable file
View File

@ -0,0 +1,213 @@
#!/usr/bin/perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
# First builds the xCAT summary man page from Synopsis of each man page.
# Then converts all of the pod man pages into html (including links to each other)
# We assume that this script is run in the xCAT-vlan-2.0 dir, so everything is
# done relative to that.
use strict;
#use lib '.';
use Pod::Man;
use Pod::Html;
my $poddir = 'pods';
my $mandir = 'share/man';
my $htmldir = 'share/doc';
my $cachedir = '/tmp';
my @pods = getPodList($poddir);
#foreach (@pods) { print "$_\n"; } exit;
# Build the cmd overview page.
writesummarypage("$poddir/man1/xcat.1.pod", @pods);
# Build the man page for each pod.
#mkdir($mandir) or die "Error: could not create $mandir.\n";
print "Converting PODs to man pages...\n";
foreach my $podfile (@pods) {
my $manfile = $podfile;
$manfile =~ s/^$poddir/$mandir/; # change the beginning of the path
$manfile =~ s/\.pod$//; # change the ending
my $mdir = $manfile;
$mdir =~ s|/[^/]*$||; # get rid of the basename part
if (system("mkdir -p $mdir")) { die "Error: could not create $mdir.\n"; }
my ($section) = $podfile =~ /\.(\d+)\.pod$/;
convertpod2man($podfile, $manfile, $section);
}
my @dummyPods = createDummyPods($poddir, \@pods);
# Build the html page for each pod.
#mkdir($htmldir) or die "Error: could not create $htmldir.\n";
print "Converting PODs to HTML pages...\n";
# have to clear the cache, because old entries can cause a problem
unlink("$cachedir/pod2htmd.tmp", "$cachedir/pod2htmi.tmp");
foreach my $podfile (@pods) {
my $htmlfile = $podfile;
$htmlfile =~ s/^$poddir/$htmldir/; # change the beginning of the path
$htmlfile =~ s/\.pod$/\.html/; # change the ending
my $hdir = $htmlfile;
$hdir =~ s|/[^/]*$||; # get rid of the basename part
if (system("mkdir -p $hdir")) { die "Error: could not create $hdir.\n"; }
#print "$podfile, $htmlfile, $poddir, $htmldir\n";
convertpod2html($podfile, $htmlfile, $poddir, $htmldir);
}
# Remove the dummy pods
unlink @dummyPods;
rmdir "$poddir/man7";
exit;
# To enable linking between the cmd man pages and the db man pages, need to:
# grep thru the cmd pods searching for references (L<>) to any section 5 man page
# if that pod does not exist, create an empty one that will satisfy pod2html
# keep track of all dummy pods created, so they can be removed later
sub createDummyPods {
my ($poddir, $pods) = @_;
my $cmd = "grep -r -E 'L<.+\\([57]\\)\\|.+\\.[57]>' " . $poddir;
#print "Running cmd: ", $cmd, "\n";
my @lines = `$cmd`;
if ($?) { print "Error running: $cmd\n"; print join('', @lines); }
#my @lines;
#system($cmd);
my @dummyPods;
foreach my $l (@lines) {
#print "$l\n";
my @matches = $l =~ /L<([^\(]+)\(([57])\)\|\1\.[57]>/g; # get all the matches in the line
# The above line should create the array with every other entry being the man page name
# and every other entry is the section # (5 or 7)
my $cmd;
while ($cmd=shift @matches) {
#foreach my $m (@matches) {
my $section = shift @matches;
my $filename = "$poddir/man$section/$cmd.$section.pod";
#print "$filename\n";
if (!(grep /^$filename$/, @$pods) && !(grep /^$filename$/, @dummyPods)) { push @dummyPods, $filename; }
}
}
# Create these empty files
print "Creating empty linked-to files: ", join(', ', @dummyPods), "\n";
mkdir "$poddir/man7";
foreach my $d (@dummyPods) {
if (!open(TMP, ">>$d")) { warn "Could not create dummy pod file $d ($!)\n"; }
else { close TMP; }
}
return @dummyPods;
}
# Recursively get the list of pod man page files.
sub getPodList {
my $poddir = shift;
my @files;
# 1st get toplevel dir listing
opendir(DIR, $poddir) or die "Error: could not read $poddir.\n";
my @topdir = grep !/^\./, readdir(DIR); # /
close(DIR);
# Now go thru each subdir (these are man1, man3, etc.)
foreach my $mandir (@topdir) {
opendir(DIR, "$poddir/$mandir") or die "Error: could not read $poddir/$mandir.\n";
my @dir = grep !/^\./, readdir(DIR); # /
close(DIR);
foreach my $file (@dir) {
push @files, "$poddir/$mandir/$file";
}
}
return sort @files;
}
# Create the xcat man page that gives a summary description of each xcat cmd.
sub writesummarypage {
my $file = shift; # relative path file name of the man page
# the rest of @_ contains the pod files that describe each cmd
open(FILE, ">$file") or die "Error: could not open $file for writing.\n";
print FILE <<'EOS1';
=head1 NAME
B<xcat> - extreme Cluster Administration Tool.
=head1 DESCRIPTION
Extreme Cluster Administration Toolkit (xCAT). xCAT is a scalable distributed computing management
and provisioning tool that provides a unified interface for hardware control, discovery, and
OS diskful/diskfree deployment.
=head1 XCAT DATABASE
All of the cluster configuration information is in the xCAT database. See L<xcatdb(5)|xcatdb.5> for
descriptions of every table in the database.
=head1 XCAT COMMANDS
What follows is a short description of each xCAT command. To get more information about a particular
command, see its man page. Note that the commands are listed in alphabetical order B<within each section>,
i.e. all the commands in section 1, then the commands in section 3, etc.
=over 12
EOS1
# extract the summary for each cmd from its man page
foreach my $manpage (@_) {
my ($sectionnum) = $manpage =~ /\.(\d+)\.pod$/;
# Suck in the whole file, then we will parse it.
open(MANPAGE, "$manpage") or die "Error: could not open $manpage for reading.\n";
my @contents = <MANPAGE>;
my $wholemanpage = join('', @contents);
close(MANPAGE);
# This regex matches: optional space, =head1, space, title, space, cmd, space, description, newline
my ($cmd, $description) = $wholemanpage =~ /^\s*=head1\s+\S+\s+(\S+)\s+(.+?)\n/si;
if (!defined($cmd)) { print "Warning: $manpage is not in a recognized structure. It will be ignored.\n"; next; }
if (!defined($description)) { print "Warning: $manpage does not have a description for $cmd. It will be ignored.\n"; next; }
$cmd =~ s/^.<(.+)>$/$1/; # if the cmd name has pod formatting around it, strip it off
$description =~ s/^-\s*//; # if the description has a leading hypen, strip it off
print FILE "\n=item L<$cmd($sectionnum)|$cmd.$sectionnum>\n\n".$description."\n";
}
# Artificially add the xcattest cmd, because the xCAT-test rpm will add this
print FILE "\n=item L<xcattest(1)|xcattest.1>\n\nRun automated xCAT test cases.\n";
print FILE <<"EOS3";
=back
EOS3
close FILE;
}
# Create the html page for one pod.
sub convertpod2html {
my ($podfile, $htmlfile, $poddir, $htmldir) = @_;
#TODO: use --css=<stylesheet> and --title=<pagetitle> to make the pages look better
pod2html($podfile,
"--outfile=$htmlfile",
"--podpath=man1",
"--podroot=$poddir",
"--htmldir=$htmldir",
"--recurse",
"--cachedir=$cachedir",
);
}
# Create the man page for one pod.
sub convertpod2man {
my ($podfile, $manfile, $section) = @_;
my $parser = Pod::Man->new(section => $section);
$parser->parse_from_file($podfile, $manfile);
}