-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
This patch implements 'cobbler system deploy'. Basically, if you have
func connecting your cobbler server to your virtual hosts, you can now
have cobbler simply deploy the virtual hosts. You can also manage the
power state of your virtual hosts over func. You can deploy a system to
a virt_host or a virt_group. A virt_group is a collection of available
virt_hosts. Basic usage is as follows
cobbler system edit --name=virtserver01 --virt-group=default
cobbler system edit --name=virtserver02 --virt-group=default
cobbler system deploy --name=vm01 --virt-group=default
cobbler system deploy --name=vm02 --virt-host=virtserver02
The first two commands add virtservers 01 and 02 to virtual group
default. Then the second two commands deploy vms to those hosts. The
first one will get randomly placed on 01 or 02, while the second will
get placed on virtserver02.
Currently cobbler picks which virt_host from a virt_group a vm lands on
at random (random.choice(systems)). I would like to make this choice
more intelligent based on the load each virtual host. Unfortunately,
the load a virtual host is under can change from place to place.
Currently the load factor I would like to use involves over committing
the physical CPUs by 2 times, and limiting based upon maximum memory in
the system. So it would be (VRAM+VCPU)/(PRAM+2*PCPU). Which should
come out to a number less than one and we would grab the minimum. Any
suggestions on a better way to do it? Its a pretty small/simple piece
of code, but I bet it will have a large effect on the usability of the
system.
As for testing, I did initial smoke testing and it doesn't seem to break
anything else. It should work except I can't get my version of koan to
contact cobbler, but it is calling koan with all the proper arguments on
the other side, so I call that 'working'. I'd love comments on it. Thanks.
- --
Scott Henson
Red Hat CIS Operator
WVU Alum BSAE/BSME
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Fedora -
http://enigmail.mozdev.org
iEYEARECAAYFAknL2cQACgkQZ+CEIfzQ+Eq/DQCgkV4i97VsVQj8nC1+a4XJC6Ns
nzwAoKB25abxzXBweE+RCIQICZOAuvg5
=HvJU
-----END PGP SIGNATURE-----
From 6d0364c48116de03bbed3364723c41b626963827 Mon Sep 17 00:00:00 2001
From: Scott Henson <shenson(a)redhat.com>
Date: Mon, 9 Mar 2009 18:28:45 -0400
Subject: [PATCH] Implement cobbler system deploy
- We now have a func utils module
- As a bonus we get power over func for virt hosts
---
cobbler/action_deploy.py | 81 +++++++++++++++++++++++++++++++++++++++++
cobbler/action_power.py | 15 ++++++++
cobbler/api.py | 9 ++++-
cobbler/collection.py | 2 +
cobbler/commands.py | 25 +++++++++++++
cobbler/func_utils.py | 29 +++++++++++++++
cobbler/item_system.py | 19 ++++++++++
cobbler/modules/cli_system.py | 10 ++++-
cobbler/remote.py | 9 ++++-
cobbler/utils.py | 30 +++++++++++++++
10 files changed, 225 insertions(+), 4 deletions(-)
create mode 100644 cobbler/action_deploy.py
create mode 100644 cobbler/func_utils.py
diff --git a/cobbler/action_deploy.py b/cobbler/action_deploy.py
new file mode 100644
index 0000000..875a202
--- /dev/null
+++ b/cobbler/action_deploy.py
@@ -0,0 +1,81 @@
+"""
+Replicate from a cobbler master.
+
+Copyright 2009, Red Hat, Inc
+Scott Henson <shenson(a)redhat.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA
+"""
+
+import func_utils
+from utils import _
+from cexceptions import *
+import random
+
+class Deployer:
+ def __init__(self,config):
+ """
+ Constructor
+ """
+ self.config = config
+ self.settings = config.settings()
+ self.api = config.api
+ self.virt_host = None
+
+ # -------------------------------------------------------
+
+ def find_host(self, virt_group):
+ """
+ Find a system in the virtual group specified
+ """
+
+ systems = self.api.find_system(virt_group=virt_group, return_list = True)
+ if len(systems) == 0:
+ raise CX("No systems were found in virtual group %s"%virt_group)
+ return random.choice(systems)
+
+ # -------------------------------------------------------
+
+ def deploy(self, system, virt_host = None, virt_group=None):
+ """
+ Deploy the current system to the virtual host or virtual group
+ """
+ if virt_host is None and virt_group is not None:
+ virt_host = self.find_host(virt_group)
+
+ if virt_host is None and system.virt_group == '':
+ virt_host = self.find_host(system.virt_group)
+
+ if system.virt_host != '':
+ virt_host = system.virt_host
+
+ if virt_host is None:
+ raise CX("No host specified for deployment.")
+
+ if not func_utils.HAZFUNC:
+ raise CX("Func is not available.")
+
+ try:
+ client = func_utils.func.Client(virt_host)
+ rc = client.virt.install(self.settings.server, system.hostname,
True)[virt_host]
+ if rc != 0:
+ raise CX("Func Error: %s"%rc[2])
+ system.virt_host = virt_host
+ self.api.add_system(system)
+ return rc
+
+ except func_utils.Func_Client_Exception, ex:
+ raise CX("A Func Exception has occured: %s"%ex)
diff --git a/cobbler/action_power.py b/cobbler/action_power.py
index 789750a..f43c215 100644
--- a/cobbler/action_power.py
+++ b/cobbler/action_power.py
@@ -34,6 +34,7 @@ import traceback
import time
import utils
+import func_utils
from cexceptions import *
import templar
@@ -64,6 +65,20 @@ class PowerTool:
interested in maximum security should take that route.
"""
+ if self.system.virt_host != '' and func_utils.HAZFUNC:
+ try:
+ client = func_utils.func.Client(self.system.virt_host)
+ if desired_state == 'on':
+ rc = client.virt.create(self.system.hostname)[self.system.virt_host]
+ else:
+ rc =
client.virt.destroy(self.system.hostname)[self.system.virt_host]
+ if rc != 0:
+ print rc[2]
+ else:
+ return rc
+ except func_utils.Func_Client_Exception:
+ pass
+
template = self.get_command_template()
template_file = open(template, "r")
diff --git a/cobbler/api.py b/cobbler/api.py
index fee90f2..5617833 100644
--- a/cobbler/api.py
+++ b/cobbler/api.py
@@ -27,6 +27,7 @@ import config
import utils
import action_sync
import action_check
+import action_deploy
import action_import
import action_reposync
import action_status
@@ -767,7 +768,13 @@ class BootAPI:
self.power_off(system, user, password)
time.sleep(5)
return self.power_on(system, user, password)
-
+
+ def deploy(self, system, virt_host=None, virt_group=None):
+ """
+ Deploys a system to the virtual host or virtual group
+ """
+ return
action_deploy.Deployer(self._config).deploy(system,virt_host=virt_host,virt_group=virt_group)
+
def get_os_details(self):
return (self.dist, self.os_version)
diff --git a/cobbler/collection.py b/cobbler/collection.py
index 55c65f6..63c3fb4 100644
--- a/cobbler/collection.py
+++ b/cobbler/collection.py
@@ -113,6 +113,8 @@ class Collection(serializable.Serializable):
'virt-type' : 'virt_type',
'virt-bridge' : 'virt_bridge',
'virt-cpus' : 'virt_cpus',
+ 'virt-host' : 'virt_host',
+ 'virt-group' : 'virt_group',
'dhcp-tag' : 'dhcp_tag',
'netboot-enabled' : 'netboot_enabled'
}
diff --git a/cobbler/commands.py b/cobbler/commands.py
index 4bb6ec3..791c58c 100644
--- a/cobbler/commands.py
+++ b/cobbler/commands.py
@@ -299,6 +299,21 @@ class CobblerFunction:
self.api.reboot(obj,self.options.power_user,self.options.power_pass)
return None
+ if "deploy" in self.args:
+ obj = collect_fn().find(self.options.name)
+ if obj is None:
+ raise CX(_("object not found"))
+ if self.options.virt_host == '':
+ virt_host = None
+ else:
+ virt_host = self.options.virt_host
+
+ if self.options.virt_group == '':
+ virt_group = None
+ else:
+ virt_group = self.options.virt_group
+ self.api.deploy(obj,virt_host=virt_host,virt_group=virt_group)
+
if "remove" in self.args:
recursive = False
# only applies to distros/profiles and is not supported elsewhere
@@ -332,6 +347,16 @@ class CobblerFunction:
raise CX(_("object not found"))
return obj
+ if "deploy" in self.args:
+ if not self.options.name:
+ raise CX(_("name is required"))
+ obj = collect_fn().find(self.options.name)
+ if obj is None:
+ raise CX(_("object not found"))
+ if obj.virt_host == '' or not self.options.virt_host or not
self.options.virt_group:
+ raise CX(_("No virtual host to deploy to"))
+ return obj
+
try:
# catch some invalid executions of the CLI
getattr(self, "options")
diff --git a/cobbler/func_utils.py b/cobbler/func_utils.py
new file mode 100644
index 0000000..79e1e10
--- /dev/null
+++ b/cobbler/func_utils.py
@@ -0,0 +1,29 @@
+"""
+Misc func functions for cobbler
+
+Copyright 2006-2008, Red Hat, Inc
+Scott Henson <shenson(a)redhat.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA
+"""
+
+try:
+ import func.overlord.client as func
+ from func.CommonErrors import Func_Client_Exception
+ HAZFUNC=True
+except ImportError:
+ HAZFUNC=False
+
diff --git a/cobbler/item_system.py b/cobbler/item_system.py
index cf8e5d1..5d6f85e 100644
--- a/cobbler/item_system.py
+++ b/cobbler/item_system.py
@@ -62,6 +62,8 @@ class System(item.Item):
self.virt_type = "<<inherit>>" #
""
self.virt_path = "<<inherit>>" #
""
self.virt_bridge = "<<inherit>>" #
""
+ self.virt_host = "" # ""
+ self.virt_group = "" # ""
self.comment = ""
self.ctime = 0
self.mtime = 0
@@ -195,6 +197,8 @@ class System(item.Item):
self.virt_type =
self.load_item(seed_data,'virt_type','<<inherit>>')
self.virt_bridge =
self.load_item(seed_data,'virt_bridge','<<inherit>>')
self.virt_cpus =
self.load_item(seed_data,'virt_cpus','<<inherit>>')
+ self.virt_host = self.load_item(seed_data,'virt_host','')
+ self.virt_group = self.load_item(seed_data,'virt_group','')
self.ctime = self.load_item(seed_data,'ctime',0)
self.mtime = self.load_item(seed_data,'mtime',0)
@@ -576,6 +580,12 @@ class System(item.Item):
def set_virt_cpus(self,num):
return utils.set_virt_cpus(self,num)
+ def set_virt_host(self,host):
+ return utils.set_virt_host(self,host)
+
+ def set_virt_group(self,group):
+ return utils.set_virt_group(self,group)
+
def set_virt_file_size(self,num):
return utils.set_virt_file_size(self,num)
@@ -706,6 +716,8 @@ class System(item.Item):
'image' : self.image,
'server' : self.server,
'virt_cpus' : self.virt_cpus,
+ 'virt_host' : self.virt_host,
+ 'virt_group' : self.virt_group,
'virt_bridge' : self.virt_bridge,
'virt_file_size' : self.virt_file_size,
'virt_path' : self.virt_path,
@@ -754,6 +766,8 @@ class System(item.Item):
buf = buf + _("virt auto boot : %s\n") % self.virt_auto_boot
buf = buf + _("virt cpus : %s\n") % self.virt_cpus
+ buf = buf + _("virt host : %s\n") % self.virt_host
+ buf = buf + _("virt group : %s\n") % self.virt_group
buf = buf + _("virt file size : %s\n") % self.virt_file_size
buf = buf + _("virt path : %s\n") % self.virt_path
buf = buf + _("virt ram : %s\n") % self.virt_ram
@@ -829,6 +843,11 @@ class System(item.Item):
'virt_ram' : self.set_virt_ram,
'virt_type' : self.set_virt_type,
'virt_cpus' : self.set_virt_cpus,
+ 'virt-host' : self.set_virt_host,
+ 'virt_host' : self.set_virt_host,
+ 'virt-group' : self.set_virt_group,
+ 'virt_group' : self.set_virt_group,
+ 'virt-file-size' : self.set_virt_file_size,
'virt_file_size' : self.set_virt_file_size,
'server' : self.set_server,
'owners' : self.set_owners,
diff --git a/cobbler/modules/cli_system.py b/cobbler/modules/cli_system.py
index 9516b77..148e418 100644
--- a/cobbler/modules/cli_system.py
+++ b/cobbler/modules/cli_system.py
@@ -35,13 +35,13 @@ from cexceptions import *
class SystemFunction(commands.CobblerFunction):
def help_me(self):
- return commands.HELP_FORMAT % ("cobbler
system","<add|copy|edit|find|list|power[off|on]|reboot|rename|remove|report|getks>
[ARGS]")
+ return commands.HELP_FORMAT % ("cobbler
system","<add|copy|deploy|edit|find|list|power[off|on]|reboot|rename|remove|report|getks>
[ARGS]")
def command_name(self):
return "system"
def subcommands(self):
- return
["add","copy","dumpvars","edit","find","getks","poweroff","poweron","list","reboot","remove","rename","report"]
+ return
["add","copy","deploy","dumpvars","edit","find","getks","poweroff","poweron","list","reboot","remove","rename","report"]
def add_options(self, p, args):
@@ -121,6 +121,8 @@ class SystemFunction(commands.CobblerFunction):
p.add_option("--virt-auto-boot", dest="virt_auto_boot",
help="auto boot this VM with host?")
p.add_option("--virt-bridge", dest="virt_bridge",
help="ex: 'virbr0'")
p.add_option("--virt-cpus", dest="virt_cpus",
help="integer (default: 1)")
+ p.add_option("--virt-host", dest="virt_host",
help="virtual host")
+ p.add_option("--virt-group", dest="virt_group",
help="virtual group")
p.add_option("--virt-file-size", dest="virt_file_size",
help="size in GB")
p.add_option("--virt-path", dest="virt_path",
help="path, partition, or volume")
p.add_option("--virt-ram", dest="virt_ram",
help="size in MB")
@@ -187,6 +189,10 @@ class SystemFunction(commands.CobblerFunction):
obj.set_virt_type(self.options.virt_type)
if self.options.virt_cpus is not None:
obj.set_virt_cpus(self.options.virt_cpus)
+ if self.options.virt_host is not None:
+ obj.set_virt_host(self.options.virt_host)
+ if self.options.virt_group is not None and not self.matches_args(self.args,
["deploy"]):
+ obj.set_virt_group(self.options.virt_group)
if self.options.virt_path is not None:
obj.set_virt_path(self.options.virt_path)
diff --git a/cobbler/remote.py b/cobbler/remote.py
index c9d668d..5646586 100644
--- a/cobbler/remote.py
+++ b/cobbler/remote.py
@@ -1570,7 +1570,14 @@ class CobblerXMLRPCInterface:
raise CX(_("invalid power mode '%s', expected
on/off/reboot" % power))
return rc
-
+ def deploy(self, object_id, virt_host=None, virt_group=None, token=None):
+ """
+ Deploy a system
+ """
+ obj = self.__get_object(object_id)
+ self.check_access(token, "deploy", obj)
+ rc = self.api.deploy(obj, virt_host=virt_host, virt_group=virt_group)
+ return rc
diff --git a/cobbler/utils.py b/cobbler/utils.py
index 1c3d56b..edd18d7 100644
--- a/cobbler/utils.py
+++ b/cobbler/utils.py
@@ -1381,6 +1381,36 @@ def set_virt_cpus(self,num):
self.virt_cpus = num
return True
+def set_virt_host(self,host):
+ """
+ For setting the virtual host that the virt system resides on.
+ """
+ if host is None:
+ self.virt_host = ''
+ return True
+
+ if host == "<<inherit>>":
+ self.virt_host = "<<inherit>>"
+ return True
+
+ self.virt_host = host
+ return True
+
+def set_virt_group(self,group):
+ """
+ For setting the virtual group that the virt system resides in.
+ """
+ if group is None:
+ self.virt_group = ''
+ return True
+
+ if group == "<<inherit>>":
+ self.virt_group = "<<inherit>>"
+ return True
+
+ self.virt_group = group
+ return True
+
def get_kickstart_templates(api):
files = {}
for x in api.profiles():
--
1.6.0.6