Cobbler: 是一个 Linux服务器快速网络安装的工具,由python 开发

本人开源代码: I夏/CMDB

cobbler 提供了命令行和api进行管理

cobbler check 核对当前设置是否有问题
cobbler list 列出所有的cobbler元素
cobbler report 列出元素的详细信息
cobbler sync 同步配置到数据目录,更改配置最好都要执行下
cobbler reposync 同步yum仓库
cobbler distro 查看导入的发行版系统信息
cobbler system 查看添加的系统信息
cobbler profile 查看配置信息

环境:centos7.2 mini x64

ip: 192.168.5.50

需要确保epel源已经安装成功

  1. 安装
yum -y install httpd dhcp tftp python-ctypes cobbler  xinetd cobbler-web


2.启动服务


systemctl start httpd
systemctl enable httpd
systemctl start cobblerd
systemctl enable cobblerd

3.检查cobbler 配置是否正常

[root@cobbler ~]# cobbler check
The following are potential configuration items that you may want to fix:

1 : The 'server' field in /etc/cobbler/settings must be set to something other than localhost, or kickstarting features will not work.  This should be a resolvable hostname or IP for the boot server as reachable by all machines that will use it.
2 : For PXE to be functional, the 'next_server' field in /etc/cobbler/settings must be set to something other than 127.0.0.1, and should match the IP of the boot server on the PXE network.
3 : change 'disable' to 'no' in /etc/xinetd.d/tftp
4 : Some network boot-loaders are missing from /var/lib/cobbler/loaders, you may run 'cobbler get-loaders' to download them, or, if you only want to handle x86/x86_64 netbooting, you may ensure that you have installed a *recent* version of the syslinux package installed and can ignore this message entirely.  Files in this directory, should you want to support all architectures, should include pxelinux.0, menu.c32, elilo.efi, and yaboot. The 'cobbler get-loaders' command is the easiest way to resolve these requirements.
5 : enable and start rsyncd.service with systemctl
6 : debmirror package is not installed, it will be required to manage debian deployments and repositories
7 : ksvalidator was not found, install pykickstart
8 : The default password used by the sample templates for newly installed machines (default_password_crypted in /etc/cobbler/settings) is still set to 'cobbler' and should be changed, try: "openssl passwd -1 -salt 'random-phrase-here' 'your-password-here'" to generate new one
9 : fencing tools were not found, and are required to use the (optional) power management features. install cman or fence-agents to use them

Restart cobblerd and then run 'cobbler sync' to apply changes.

我们需要解决这些警告

问题1,告诉我们 server 这个字段的值应该是一个地址不要写本地地址,因为我们是对外访问的,最后将这个字段修改为服务的ip

sed -i 's/^server: 127.0.0.1/server: 192.168.5.50/' /etc/cobbler/settings

问题2,next_server 该字段为pxe服务器的字段,不要设置为127.0.0.1,最好设置为自己服务器的ip

sed -i 's/^next_server: 127.0.0.1/next_server: 192.168.5.50/' /etc/cobbler/settings # pxe服务器 即 TFTP Server 的IP地址

问题3,cobbler服务需要tftp 服务启动,需要将 tftp 的disable 的值修改为no

[root@cobbler ~]# cat /etc/xinetd.d/tftp
# default: off
# description: The tftp server serves files using the trivial file transfer \
#	protocol.  The tftp protocol is often used to boot diskless \
#	workstations, download configuration files to network-aware printers, \
#	and to start the installation process for some operating systems.
service tftp
{
	socket_type		= dgram
	protocol		= udp
	wait			= yes
	user			= root
	server			= /usr/sbin/in.tftpd
	server_args		= -s /var/lib/tftpboot
	disable			= no    #默认是yes,需要修改为no
	per_source		= 11
	cps			= 100 2
	flags			= IPv4
}

问题4,boot-loaders 在本地或许不存在,需要通过 命令 cobbler get-loaders 下载

[root@cobbler ~]# cobbler get-loaders
task started: 2018-12-04_095358_get_loaders
task started (id=Download Bootloader Content, time=Tue Dec  4 09:53:58 2018)
downloading https://cobbler.github.io/loaders/README to /var/lib/cobbler/loaders/README
downloading https://cobbler.github.io/loaders/COPYING.elilo to /var/lib/cobbler/loaders/COPYING.elilo
downloading https://cobbler.github.io/loaders/COPYING.yaboot to /var/lib/cobbler/loaders/COPYING.yaboot
downloading https://cobbler.github.io/loaders/COPYING.syslinux to /var/lib/cobbler/loaders/COPYING.syslinux
downloading https://cobbler.github.io/loaders/elilo-3.8-ia64.efi to /var/lib/cobbler/loaders/elilo-ia64.efi
downloading https://cobbler.github.io/loaders/yaboot-1.3.17 to /var/lib/cobbler/loaders/yaboot
downloading https://cobbler.github.io/loaders/pxelinux.0-3.86 to /var/lib/cobbler/loaders/pxelinux.0
downloading https://cobbler.github.io/loaders/menu.c32-3.86 to /var/lib/cobbler/loaders/menu.c32
downloading https://cobbler.github.io/loaders/grub-0.97-x86.efi to /var/lib/cobbler/loaders/grub-x86.efi
downloading https://cobbler.github.io/loaders/grub-0.97-x86_64.efi to /var/lib/cobbler/loaders/grub-x86_64.efi
*** TASK COMPLETE ***

问题5,是 rsyncd 服务开机自动启动

[root@cobbler ~]# systemctl enable rsyncd
Created symlink from /etc/systemd/system/multi-user.target.wants/rsyncd.service to /usr/lib/systemd/system/rsyncd.service.

问题6,这个和debian 有关系,目测用不到,一些企业大部分用redhat 或者centos系统

问题7,ksvalidator没有找到,让安装 pykickstart

[root@cobbler ~]# yum install -y pykickstart

问题8,模板的默认密码是 cobbler,最好重新设置一些,要不要安装系统的默认密码就是cobbler,可以 通过 openssl passwd 进行设置

[root@cobbler ~]# [root@cobbler ~]# openssl passwd -1 -salt 'ilove' 'nineven'     # ilove 是随机盐   nineven 是密码
$1$ilove$A0hokoomR0W702oHUi1OT0

#这个字段 default_password_crypted 将生成的密码替换
[root@cobbler ~]# grep '^default_password_crypted' /etc/cobbler/settings
default_password_crypted: "$1$ilove$A0hokoomR0W702oHUi1OT0"

重新检查错误

[root@cobbler ~]# systemctl restart cobblerd
[root@cobbler ~]# cobbler check
The following are potential configuration items that you may want to fix:

1 : debmirror package is not installed, it will be required to manage debian deployments and repositories
2 : fencing tools were not found, and are required to use the (optional) power management features. install cman or fence-agents to use them

Restart cobblerd and then run 'cobbler sync' to apply changes

这两个错误可以忽略

4.配置cobbler 直接接管dhcp服务

[root@cobbler ~]# vim /etc/cobbler/settings 
[root@cobbler ~]# grep '^manage_dhcp' /etc/cobbler/settings
manage_dhcp: 1

配置dhcp模板

[root@cobbler ~]# vim /etc/cobbler/dhcp.template
ddns-update-style interim;

allow booting;
allow bootp;

deny unknown-clients; # 开启白名单默认,拒绝其他客户端,默认没有,需要单独添加

ignore client-updates;
set vendorclass = option vendor-class-identifier;

option pxe-system-type code 93 = unsigned integer 16;

subnet 192.168.5.0 netmask 255.255.255.0 {
     option routers             192.168.5.1;
     option domain-name-servers 192.168.5.1;
     option subnet-mask         255.255.255.0;
     range dynamic-bootp        192.168.5.10 192.168.5.50;
     
     #仅列出部分

5.配置只安装一次,要不然,会出现重复安装系统

[root@cobbler ~]# vim /etc/cobbler/settings 
[root@cobbler ~]# grep '^pxe_just_once' /etc/cobbler/settings
pxe_just_once: 1

    修改settings 配置文件之后,需要重启cobblerd 服务,使之生效

[root@cobbler ~]# systemctl restart cobblerd
[root@cobbler ~]# cobbler sync



6,导入系统:

可以发现现在,所有都是空的,

[root@cobbler ~]# cobbler list
distros:

profiles:

systems:

repos:

images:

mgmtclasses:

packages:

files:

通过 cobbler import 导入系统

a,先挂载系统,可以通过光驱挂载,也可以将镜像传到本地,然后进行挂载,我是通过上传镜像进行挂载的

[root@cobbler ~]# mount /root/CentOS-7-x86_64-Minimal-1511.iso /mnt/
mount: /dev/loop0 is write-protected, mounting read-only
[root@cobbler ~]# cobbler import --path=/mnt/ --name=CentOS-7-x86_64-Minimal-1511 --arch=x86_64
task started: 2018-12-04_102336_import
task started (id=Media import, time=Tue Dec  4 10:23:36 2018)
Found a candidate signature: breed=redhat, version=rhel6
Found a candidate signature: breed=redhat, version=rhel7
Found a matching signature: breed=redhat, version=rhel7
Adding distros from path /var/www/cobbler/ks_mirror/CentOS-7-x86_64-Minimal-1511-x86_64:
creating new distro: CentOS-7-Minimal-1511-x86_64
trying symlink: /var/www/cobbler/ks_mirror/CentOS-7-x86_64-Minimal-1511-x86_64 -> /var/www/cobbler/links/CentOS-7-Minimal-1511-x86_64
creating new profile: CentOS-7-Minimal-1511-x86_64
associating repos
checking for rsync repo(s)
checking for rhn repo(s)
checking for yum repo(s)
starting descent into /var/www/cobbler/ks_mirror/CentOS-7-x86_64-Minimal-1511-x86_64 for CentOS-7-Minimal-1511-x86_64
processing repo at : /var/www/cobbler/ks_mirror/CentOS-7-x86_64-Minimal-1511-x86_64
need to process repo/comps: /var/www/cobbler/ks_mirror/CentOS-7-x86_64-Minimal-1511-x86_64
looking for /var/www/cobbler/ks_mirror/CentOS-7-x86_64-Minimal-1511-x86_64/repodata/*comps*.xml
Keeping repodata as-is :/var/www/cobbler/ks_mirror/CentOS-7-x86_64-Minimal-1511-x86_64/repodata
*** TASK COMPLETE ***

通过cobbler list 查询信息

[root@cobbler ~]# cobbler list
distros:
   CentOS-7-Minimal-1511-x86_64

profiles:
   CentOS-7-Minimal-1511-x86_64

systems:

repos:

images:

mgmtclasses:

packages:

files:

Cobbler 服务已经搭建好了

Cobbler API

关于api的一些方法,可以查看 

/usr/lib/python2.7/site-packages/cobbler

该目录下的remote.py 文件

需要在cobbler 服务端的 /usr/bin/cobblerd  文件的 import 下一行增加

reload(sys)
sys.setdefaultencoding('utf8')

解决ks内容出现中文的bug

既然有API了,那么我们就可以通过web页面调用这个api ,通过页面添加安装系统任务

cobbler 教程 cobbler api_linux

cobbler 教程 cobbler api_cobbler 教程_02

cobbler 教程 cobbler api_python_03

cobbler 教程 cobbler api_linux_04

具体代码请看 https://gitee.com/nineven/CMDB

二,Cobbler API

需要先安装好cobbler服务

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# author : liuyu
# date : 2018/11/19 0019

import xmlrpc.client,time,os


'''
需要在cobbler 服务端的 /usr/bin/cobblerd  文件的 import 下一行增加

reload(sys)
sys.setdefaultencoding('utf8')

解决ks内容出现中文的bug
'''
class CobblerApi(object):
    def __init__(self,server,user,passwd):
        self.server = server
        self.user = user
        self.passwd = passwd
        self.token = ''
        self.__get_token()

    def __call__(self, *args, **kwargs):
        self.__sync_cobbler()
        print("1223333333333333333")
        self.remote_server.logout(self.token)

    def end(self):
        self.remote_server.logout(self.token)

    def is_kickstart_in_use(self,name):
        return self.remote_server.is_kickstart_in_use(name)

    def is_snippets_in_use(self,name,kickstart_templates):
        snsname = os.path.basename(name)
        snsname="$SNIPPET('{}')".format(snsname)
        for ks in kickstart_templates:
            # print(ks.get("kickstart"))
            context = self.read_or_write_kickstart_template(ks.get("kickstart"),"",True)
            if snsname in context:
                return True,kickstart_templates
        return False,kickstart_templates

    '''
    通过list 的格式返回每个类别的名称
    '''
    def __check_token(self):
        return self.remote_server.token_check(self.token)

    def __get_token(self):
        try:
            self.remote_server = xmlrpc.client.Server("http://{}/cobbler_api".format(self.server))
            self.token = self.remote_server.login(self.user, self.passwd)
        except Exception as e:
            print(e)
            return ('URL:%s no access' % self.server)

    def find_distros(self):
        return self.remote_server.find_distro()

    def find_profiles(self):
        return self.remote_server.find_profile()

    def find_system(self):
        return self.remote_server.find_system()

    '''
    通过list 的格式返回每个名称的详细信息
    '''
    def get_distros(self,name):
        return self.remote_server.get_distro(name)

    def get_profiles(self,name):
        return self.remote_server.get_profile(name)

    def get_system(self,name):
        return self.remote_server.get_system(name)

    '''
    删除
    '''

    # prof_id = remote_server.new_profile(token)  # 创建一个新的profile 并保存
    def remove_profile(self,name):
        self.remote_server.remove_profile(name,self.token)
        self.__sync_cobbler()

    def remove_distros(self,name):
        self.remote_server.remove_distro(name,self.token)
        self.__sync_cobbler()

    def remove_system(self,name):
        print(name)
        self.remote_server.remove_system(name,self.token)
        self.__sync_cobbler()

    '''
    修改
    '''
    def modify_profile(self,name,key,value):
        object_id = self.remote_server.get_profile_handle(name, self.token)
        # remote_server.modify_profile(prof_id,'name','vm_test1',token) # 修改prof_id指定的profile 名称
        # remote_server.modify_profile(prof_id,'distro','centos6.8-x86_64',token)  # 也是修改prof_id的信息
        # remote_server.modify_profile(object_id, 'kickstart', '/var/lib/cobbler/kickstarts/sample_end.ks', token)
        try:
            self.remote_server.modify_profile(object_id, key, value, self.token)
            self.remote_server.save_profile(object_id, self.token)  # 保存
            self.__sync_cobbler()

        except Exception as e:
            return e

    def modify_system(self,name,key,value):
        object_id = self.remote_server.get_system_handle(name, self.token)
        try:
            self.remote_server.modify_system(object_id, key, value, self.token)
            self.remote_server.save_system(object_id, self.token)  # 保存
            self.__sync_cobbler()

        except Exception as e:
            return e

    def modify_distros(self,name,key,value):
        object_id = self.remote_server.get_distros_handle(name, self.token)
        try:
            self.remote_server.modify_distros(object_id, key, value, self.token)
            self.remote_server.save_distros(object_id, self.token)  # 保存
            self.__sync_cobbler()

        except Exception as e:
            return e

    '''
    获取ks和sns 的文件列表
    '''
    def get_kickstart_templates(self):

        kickstartlist =[]
        for ks in self.remote_server.get_kickstart_templates():
            if ks.startswith('/'):
                if self.is_kickstart_in_use(ks):
                    status="in-use"
                else:
                    status = "not-in-use"
                kickstartlist.append({"kickstart":ks,"status":status})

        return kickstartlist


    def get_snippets(self):
        snippetslist =[]
        kickstart_templates=self.get_kickstart_templates()
        for sns in self.remote_server.get_snippets():
            if sns.startswith('/'):
                status,kickstart_templates=self.is_snippets_in_use(sns,kickstart_templates)
                if status:
                    status="in-use"
                else:
                    status = "not-in-use"
                snippetslist.append({"snippets":sns,"status":status})

        return snippetslist

    # 同步cobbler修改后的信息,这个做任何操作后,都要必须有
    def __sync_cobbler(self):
        return self.remote_server.sync(self.token) # 同步cobbler修改后的信息,这个做任何操作后,都要必须有

    '''
    填写ks模板
        # print(remote_server.read_or_write_kickstart_template('/var/lib/cobbler/kickstarts/1234.ks',False,'123\ndead\tdd\nda',token))
        替换KS字符串如果为-1,将删除此Ks文件,条件是此ks文件已不在引用
    '''
    def read_or_write_kickstart_template(self,filename,context,read=False):
        fullfilepath = os.path.join('/var/lib/cobbler/kickstarts/',filename)
        #not self.remote_server.is_kickstart_in_use(fullfilepath) and
        if not os.path.exists(fullfilepath):
            return self.remote_server.read_or_write_kickstart_template(fullfilepath,read,context,self.token)
        else:
            return "The kickstart is exists"

    '''
    填写sns模板
     # print(remote_server.read_or_write_snippet('/var/lib/cobbler/snippets/test1',False,'zhaoyong_test',token)) # 在snippgets下建立脚本文件
    '''
    def read_or_write_snippet(self,filename,context,read=False):
        fullfilepath = os.path.join('/var/lib/cobbler/snippets/',filename)
        if not os.path.exists(fullfilepath):
            return self.remote_server.read_or_write_snippet(fullfilepath,read,context,self.token)
        else:
            return "The snippet is exists"

    '''
    增加新的system
    '''
    def create_or_update_system(self,name,mac,ip,profile,subnet,gateway,interface,hostname,dns,netboot_enabled,
                                kickstart='/var/lib/cobbler/kickstarts/sample_end.ks',
                                kops='net.ifnames=0 biosdevname=0'):
        sid = self.remote_server.new_system(self.token)
        self.remote_server.modify_system(sid,'name',name,self.token)
        self.remote_server.modify_system(sid, 'hostname', hostname, self.token)
        self.remote_server.modify_system(sid, 'gateway', gateway, self.token)
        self.remote_server.modify_system(sid, 'profile', profile, self.token)
        self.remote_server.modify_system(sid, 'name_servers', dns, self.token)
        self.remote_server.modify_system(sid, 'modify_interface', {
            "macaddress-{}".format(interface): mac.upper(),
            "ipaddress-{}".format(interface): ip,
            "gateway-{}".format(interface): gateway,
            "subnet-{}".format(interface):subnet,
            "dnsname-{}".format(interface): ip,
            "static-{}".format(interface): True,
        }, self.token)
        self.remote_server.modify_system(sid,'kickstart',kickstart,self.token)
        self.remote_server.modify_system(sid,'kopts',kops,self.token)
        self.remote_server.modify_system(sid,'netboot_enabled',netboot_enabled,self.token)
        self.remote_server.save_system(sid,self.token)
        self.__sync_cobbler()

    '''
    获取system ks 定义文件
    '''
    def get_system_generate_kickstart(self,system):
        return self.remote_server.generate_kickstart('',system)

    '''
    获取profile ks 定义文件
    '''
    def get_profile_generate_kickstart(self,profile):
        return self.remote_server.generate_kickstart(profile)

    def get_status(self):
        # print(self.remote_server.get_status('text',self.token))
        # print(self.remote_server.get_status("normal",self.token))
        return self.remote_server.get_status('json',self.token)

    def get_system_status(self,name):
        for ip,v in self.remote_server.get_status('json',self.token).items():
            if v[2].split(":")[1] == name:
                start = totime(v[0])
                end = ""
                if v[1] != -1:
                    end = totime(v[1])
                system_name = v[2].split(":")[1]
                status = v[5]
                initcount = v[3]
                successcount = v[4]
                infos= {"starttime":start,"endtime":end,"ip":ip,
                        "systemname":system_name,"status":status,
                        "initcount":initcount,"successcount":successcount}
                return infos
        return False


class init_system_api(CobblerApi):

    @staticmethod
    def run():
        server = '10.1.41.100'
        user = 'cobbler'
        passwd = 'cobbler'
        return CobblerApi(server,user,passwd)


def totime(timeStamp):
    timeArray = time.localtime(timeStamp)
    otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
    return otherStyleTime

if __name__ == '__main__':
    time_str = time.time()
    server = '10.1.41.100'
    user = 'cobbler'
    passwd = 'cobbler'
    cobbler = CobblerApi(server,user,passwd)
    # print(cobbler.find_system())
    # print(cobbler.find_distros())
    # print(cobbler.find_profiles())
    # print(cobbler.get_system('kvm-centos7-test'))
    # cobbler.remove_system('centos-nineven')
    info=cobbler.get_system('kvm-123')
    if info == "~":
        print("不存在")
    else:
        print(info)
    # print(cobbler.remove_system('111'))
    #
    # infos = cobbler.get_system('centos-nineven')
    # print(infos.get("name"))
    # print(infos.get("profile"))
    # print(infos.get("kickstart"))
    # print(infos.get("kernel_options"))
    # for k,v in infos.get("interfaces").items():
    #     print(k)
    #     print(v.get("gateway"))
    #     print(v.get("hostname"))
    #     print(v.get("mac_address"))
    #     print(v.get("netmask"))
    #     print(v.get("ip_address"))
    exit()
    # print(cobbler.get_status())
    system_status = cobbler.get_status()
    for ip,v in system_status.items():
        start = totime(v[0])
        end=""
        if v[1] !=-1:
            end = totime(v[1])
        system_name = v[2]
        status = v[5]
        # v[4] 安装系统成功的次数  ,v[3] 安装次数
        print(ip,v)
        print('''
        ip:             %s
        start:          %s
        end:            %s
        system_name:    %s
        status:         %s
        '''%(ip,start,end,system_name,status))


    # print(cobbler.get_system_generate_kickstart('Centos-7.2-mini-x86_64'))
    # print(cobbler.get_profiles(cobbler.get_system('centos-nineven').get("profile")))
    # print(cobbler.modify_profile('Centos-7.2-mini-x86_64','kopts','net.ifnames=0 biosdevname=0'))
    # print(cobbler.create_or_update_system("centos-nineven","00:50:56:39:E6:FE","192.168.5.82","Centos-7.2-mini-x86_64",
    #                          "255.255.255.0","192.168.5.1","eth0","nineven-test","114.114.114.114"))
    # print(cobbler.remove_system("centos-nineven"))