kickstart+http安装centos(没有pxe)

我需要做一个一键安装centos系统的程序,并且pxe无法使用,因为dhcp服务器不支持pxe指令集,所以只是使用kickstart,但又想智能自动一点,所以使用了网络安装方式,有一个好处就是,现在我不使用cdrom了,使用u盘,但是u盘的在linux下的device标签会有时候变,会在sda和sdb中变化,所以最后还是决定使用网络安装系统,由u盘引导,最后就有了这样一篇文章。

简述

先说说linux的系统启动

1. 載入 BIOS 的硬體資訊與進行自我測試,並依據設定取得第一個可開機的裝置;
2. 讀取並執行第一個開機裝置內 MBR 的 boot Loader (亦即是 grub, spfdisk 等程式);
3. 依據 boot loader 的設定載入 Kernel ,Kernel 會開始偵測硬體與載入驅動程式;
4. 在硬體驅動成功後,Kernel 會主動呼叫 init 程式,而 init 會取得 run-level 資訊;
5. init 執行 /etc/rc.d/rc.sysinit 檔案來準備軟體執行的作業環境 (如網路、時區等);
6. init 執行 run-level 的各個服務之啟動 (script 方式);
7. init 執行 /etc/rc.d/rc.local 檔案;
8. init 執行終端機模擬程式 mingetty 來啟動 login 程式,最後就等待使用者登入啦;

引用参考:http://linux.vbird.org/linux_basic/0510osloader.php#startup_intro

然后说说Anaconda

Anaconda是RedHat、CentOS、Fedora等Linux的安装管理程序。它可以提供文本、图形等安装管理方式,并支持Kickstart等脚本提供自动安装的功能。此外,其还支持许多启动参数,熟悉这些参数可为安装带来很多方便。该程序的功能是把位于光盘或其他源上的数据包,根据设置安装到主机上。为实现该定制安装,它提供一个定制界面,可以实现交互式界面供用户选择配置(如选择语言,键盘,时区等信息)。Anaconda的大部分模块用Python编写,有少许的载入模块用C编写。

Anaconda支持的管理模式: 
(1)Kickstart提供的自动化安装; 
(2)对一个RedHat实施upgrade; 
(3)Rescuse模式对不能启动的系统进行故障排除。 
要进入安装步骤,需要先有一个引导程序引导启动一个特殊的Linux安装环境系统;引导有多种方式: 
(1)基于网络方式的小型引导镜像,需要提供小型的引导镜像; 
(2)U盘引导,通过可引导存储介质中的小型引导镜像启动安装过程;  
(3)基于PXE的网络安装方式,要提供PXE的完整安装环境; 
(4)其他bootloder引导(如GRUB)。 
可用的安装方式:本地CDROM、硬盘驱动器、网络方式(NFS、FTP、HTTP)。 

对于Kickstart,它是一个利用Anconda工具实现服务器自动化安装的方法。通过生成的kickstart配置文件ks.cfg,服务器安装可以实现从裸机到全功能服务的的非交互式(无人值守式)安装配置;ks.cfg是一个简单的文本文件,文件包含Anconda在安装系统及安装后配置服务时所需要获取的一些必要配置信息(如键盘设置,语言设置,分区设置等)。Anconda直接从该文件中读取必要的配置,只要该文件信息配置正确无误且满足所有系统需求,就不再需要同用户进行交互获取信息,从而实现安装的自动化。但是配置中如果忽略任何必需的项目,安装程序会提示用户输入相关的项目的选择,就象用户在典型的安装过程中所遇到的一样。一旦用户进行了选择,安装会以非交互的方式(unattended)继续。使用kickstart可以实现流线化自动化的安装、快速大量的裸机部署、强制建立的一致性(软件包,分区,配置,监控,安全性)、以及减少人为的部署失误。

(1)第一阶段:加载安装树的isolinux目录下的内核映像vmlinuz和初始RAM磁盘initrd.img,建立安装环境。initrd.img中的/init程序调用/sbin/loader程序,loader加载kickstart文件,最后运行/usr/bin/anaconda主程序,进入第二阶段。

(2)第二阶段:anaconda程序加载各python和bash模块,执行各个安装步骤

引用参考:http://blog.csdn.net/zhoudaxia/article/details/7172020

再说说一下pxe+dhcp+kickstart+ftp(http,nfs)

他是由dhcp中pxe的指令集分配pxe指令到客户机,并且客户机本身网卡支持pxe网络启动,这样的话就可以在分配ip的时候告知客户机kickstart配置和系统iso资源的位置,这样就能完成了无人值守的一键安装

《kickstart+http安装centos(没有pxe)》

最后说说我的dhcp+kickstart+http

《kickstart+http安装centos(没有pxe)》

第一步是启动盘引导

第二步是获取dhcp分配的ip

第三步是访问web服务器拿到ks.cfg文件

第四步是通过第三步的ks.cfg来访问web服务器的系统盘iso资源进行安装

第一次格式化磁盘的时候kickstart并不能识别,所以有warning,按照指示点击确认即可

配置

主要是这2步。

1.创建一个启动iso镜像,并且写入到光盘或者u盘里面,这个叫光盘引导驱动盘或者u盘引导驱动盘

1.下载原始光盘(centos官网下载32位64位mini32),我这边选用32位mini

下载完成后挂载到/mnt/下

mount -o loop -t iso9660 /root/CentOS-6.5-i386-minimal.iso  /mnt
    
mkdir /tmp/isomake_dir
cp -arp /mnt/* /tmp/isomake_dir/

2.修改iso启动配置文件isolinux.cfg(vi /tmp/isomake_dir/isolinux/isolinux.cfg)

default vesamenu.c32
#prompt 1
timeout 600

display boot.msg

menu background splash.jpg
menu title Welcome to CentOS 6.5!
menu color border 0 #ffffffff #00000000
menu color sel 7 #ffffffff #ff000000
menu color title 0 #ffffffff #00000000
menu color tabmsg 0 #ffffffff #00000000
menu color unsel 0 #ffffffff #00000000
menu color hotsel 0 #ff000000 #ffffffff
menu color hotkey 7 #ffffffff #ff000000
menu color scrollbar 0 #ffffffff #00000000

label linux
  menu label ^Install or upgrade an existing system
  menu default
  kernel vmlinuz
  append initrd=initrd.img
label vesa
  menu label Install system with ^basic video driver
  kernel vmlinuz
  append initrd=initrd.img xdriver=vesa nomodeset
label rescue
  menu label ^Rescue installed system
  kernel vmlinuz
  append initrd=initrd.img rescue
label local
  menu label Boot from ^local drive
  localboot 0xffff
label memtest86
  menu label ^Memory test
  kernel memtest
  append -
label http
  menu http
  kernel vmlinuz
  append initrd=initrd.img append initrd=initrd.img ks=http://192.168.6.127/centos/isolinux/ks.cfg

主要是增加了

label http  #启动界面的名字,写http是为了方便识别
  menu http
  kernel vmlinuz
  append initrd=initrd.img append initrd=initrd.img ks=http://192.168.6.127/centos/isolinux/ks.cfg

ks.cfg是后面创建,这里先写好,这里的意义是在启动盘启动引导的时候指定使用的kickstart文件,(isolinux.cfg是centos6系统盘iso的启动引导菜单,就是安装弹出的第一个选择的菜单,增加这个是指定了哪个启动项对于的启动动作)然后交给这个kickstart文件进行引导安装

《kickstart+http安装centos(没有pxe)》

3.制作启动iso镜像

yum -y install mkisofs


cd /tmp/isomake_dir
mkisofs -R -J -T -v -no-emul-boot -boot-load-size 4 -boot-info-table -V "test centos install iso" -b isolinux/isolinux.bin -c isolinux/boot.cat -o /tmp/test.iso


I: -input-charset not specified, using utf-8 (detected in locale settings)
genisoimage 1.1.9 (Linux)
Scanning .
Scanning ./isolinux
Excluded by match: ./isolinux/boot.cat
Scanning ./Packages
Scanning ./repodata
Scanning ./images
Excluded: ./TRANS.TBL
Using RPM_G000.;1 for  /RPM-GPG-KEY-CentOS-Debug-6 (RPM-GPG-KEY-CentOS-6)
Using RPM_G001.;1 for  /RPM-GPG-KEY-CentOS-6 (RPM-GPG-KEY-CentOS-Security-6)
Using RPM_G002.;1 for  /RPM-GPG-KEY-CentOS-Security-6 (RPM-GPG-KEY-CentOS-Testing-6)
Using DEVIC000.RPM;1 for  ./Packages/device-mapper-multipath-libs-0.4.9-72.el6.i686.rpm (device-mapper-event-libs-1.02.79-8.el6.i686.rpm)
Using DEVIC001.RPM;1 for  ./Packages/device-mapper-event-libs-1.02.79-8.el6.i686.rpm (device-mapper-multipath-0.4.9-72.el6.i686.rpm)
Using DEVIC002.RPM;1 for  ./Packages/device-mapper-multipath-0.4.9-72.el6.i686.rpm (device-mapper-1.02.79-8.el6.i686.rpm)
Using LIBSE000.RPM;1 for  ./Packages/libselinux-2.0.94-5.3.el6_4.1.i686.rpm (libselinux-utils-2.0.94-5.3.el6_4.1.i686.rpm)
Using FIPSC000.RPM;1 for  ./Packages/fipscheck-1.2.0-7.el6.i686.rpm (fipscheck-lib-1.2.0-7.el6.i686.rpm)
Using OPENS000.RPM;1 for  ./Packages/openssh-server-5.3p1-94.el6.i686.rpm (openssh-5.3p1-94.el6.i686.rpm)
Using SELIN000.RPM;1 for  ./Packages/selinux-policy-3.7.19-231.el6.noarch.rpm (selinux-policy-targeted-3.7.19-231.el6.noarch.rpm)
Using CRACK000.RPM;1 for  ./Packages/cracklib-2.8.16-4.el6.i686.rpm (cracklib-dicts-2.8.16-4.el6.i686.rpm)
Using IPTAB000.RPM;1 for  ./Packages/iptables-1.4.7-11.el6.i686.rpm (iptables-ipv6-1.4.7-11.el6.i686.rpm)
Using DEVIC003.RPM;1 for  ./Packages/device-mapper-1.02.79-8.el6.i686.rpm (device-mapper-libs-1.02.79-8.el6.i686.rpm)
Using E2FSP000.RPM;1 for  ./Packages/e2fsprogs-1.41.12-18.el6.i686.rpm (e2fsprogs-libs-1.41.12-18.el6.i686.rpm)
Using CYRUS000.RPM;1 for  ./Packages/cyrus-sasl-lib-2.1.23-13.el6_3.1.i686.rpm (cyrus-sasl-2.1.23-13.el6_3.1.i686.rpm)
Using DEVIC004.RPM;1 for  ./Packages/device-mapper-libs-1.02.79-8.el6.i686.rpm (device-mapper-persistent-data-0.2.8-2.el6.i686.rpm)
Using KEYUT000.RPM;1 for  ./Packages/keyutils-libs-1.4-4.el6.i686.rpm (keyutils-1.4-4.el6.i686.rpm)
Using OPENS001.RPM;1 for  ./Packages/openssh-5.3p1-94.el6.i686.rpm (openssh-clients-5.3p1-94.el6.i686.rpm)
Using PLYMO000.RPM;1 for  ./Packages/plymouth-0.8.3-27.el6.centos.i686.rpm (plymouth-scripts-0.8.3-27.el6.centos.i686.rpm)
Using PLYMO001.RPM;1 for  ./Packages/plymouth-scripts-0.8.3-27.el6.centos.i686.rpm (plymouth-core-libs-0.8.3-27.el6.centos.i686.rpm)
Using CRYPT000.RPM;1 for  ./Packages/cryptsetup-luks-1.2.0-7.el6.i686.rpm (cryptsetup-luks-libs-1.2.0-7.el6.i686.rpm)
Using NSS_S000.RPM;1 for  ./Packages/nss-softokn-3.14.3-9.el6.i686.rpm (nss-softokn-freebl-3.14.3-9.el6.i686.rpm)
Using P11_K000.RPM;1 for  ./Packages/p11-kit-0.18.5-2.el6.i686.rpm (p11-kit-trust-0.18.5-2.el6.i686.rpm)
Using NFS_U000.RPM;1 for  ./Packages/nfs-utils-1.2.3-39.el6.i686.rpm (nfs-utils-lib-1.1.5-6.el6.i686.rpm)
Using DEVIC005.RPM;1 for  ./Packages/device-mapper-persistent-data-0.2.8-2.el6.i686.rpm (device-mapper-event-1.02.79-8.el6.i686.rpm)
Using NCURS000.RPM;1 for  ./Packages/ncurses-libs-5.7-3.20090208.el6.i686.rpm (ncurses-base-5.7-3.20090208.el6.i686.rpm)
Using NCURS001.RPM;1 for  ./Packages/ncurses-base-5.7-3.20090208.el6.i686.rpm (ncurses-5.7-3.20090208.el6.i686.rpm)
Using COREU000.RPM;1 for  ./Packages/coreutils-libs-8.4-31.el6.i686.rpm (coreutils-8.4-31.el6.i686.rpm)
Writing:   Initial PadblockStart Block 0
Done with: Initial PadblockBlock(s)16
Writing:   Primary Volume Descriptor   Start Block 16
Done with: Primary Volume Descriptor   Block(s)1
Writing:   Eltorito Volume Descriptor  Start Block 17
Size of boot image is 4 sectors -No emulation
Done with: Eltorito Volume Descriptor  Block(s)1
Writing:   Joliet Volume DescriptorStart Block 18
Done with: Joliet Volume DescriptorBlock(s)1
Writing:   End Volume Descriptor   Start Block 19
Done with: End Volume Descriptor   Block(s)1
Writing:   Version block   Start Block 20
Done with: Version block   Block(s)1
Writing:   Path table  Start Block 21
Done with: Path table  Block(s)4
Writing:   Joliet path table   Start Block 25
Done with: Joliet path table   Block(s)4
Writing:   Directory tree  Start Block 29
Done with: Directory tree  Block(s)27
Writing:   Joliet directory tree   Start Block 56
Done with: Joliet directory tree   Block(s)17
Writing:   Directory tree cleanup  Start Block 73
Done with: Directory tree cleanup  Block(s)0
Writing:   Extension recordStart Block 73
Done with: Extension recordBlock(s)1
Writing:   The File(s) Start Block 74
  3.02% done, estimate finish Sun Jan 18 05:17:14 2015
  6.04% done, estimate finish Sun Jan 18 05:17:14 2015
  9.06% done, estimate finish Sun Jan 18 05:17:14 2015
12.08% done, estimate finish Sun Jan 18 05:17:14 2015
15.09% done, estimate finish Sun Jan 18 05:17:14 2015
18.11% done, estimate finish Sun Jan 18 05:17:14 2015
21.13% done, estimate finish Sun Jan 18 05:17:14 2015
24.15% done, estimate finish Sun Jan 18 05:17:14 2015
27.16% done, estimate finish Sun Jan 18 05:17:14 2015
30.18% done, estimate finish Sun Jan 18 05:17:14 2015
33.20% done, estimate finish Sun Jan 18 05:17:14 2015
36.22% done, estimate finish Sun Jan 18 05:17:14 2015
39.24% done, estimate finish Sun Jan 18 05:17:14 2015
42.25% done, estimate finish Sun Jan 18 05:17:14 2015
45.28% done, estimate finish Sun Jan 18 05:17:14 2015
48.29% done, estimate finish Sun Jan 18 05:17:14 2015
51.31% done, estimate finish Sun Jan 18 05:17:14 2015
54.33% done, estimate finish Sun Jan 18 05:17:14 2015
57.34% done, estimate finish Sun Jan 18 05:17:14 2015
60.36% done, estimate finish Sun Jan 18 05:17:14 2015
63.38% done, estimate finish Sun Jan 18 05:17:14 2015
66.39% done, estimate finish Sun Jan 18 05:17:14 2015
69.42% done, estimate finish Sun Jan 18 05:17:14 2015
72.43% done, estimate finish Sun Jan 18 05:17:14 2015
75.45% done, estimate finish Sun Jan 18 05:17:14 2015
78.47% done, estimate finish Sun Jan 18 05:17:14 2015
81.49% done, estimate finish Sun Jan 18 05:17:14 2015
84.50% done, estimate finish Sun Jan 18 05:17:14 2015
87.52% done, estimate finish Sun Jan 18 05:17:14 2015
90.54% done, estimate finish Sun Jan 18 05:17:14 2015
93.56% done, estimate finish Sun Jan 18 05:17:14 2015
96.57% done, estimate finish Sun Jan 18 05:17:14 2015
99.60% done, estimate finish Sun Jan 18 05:17:14 2015
Total translation table size: 68712
Total rockridge attributes bytes: 30785
Total directory bytes: 49152
Path table size(bytes): 72
Done with: The File(s) Block(s)165455
Writing:   Ending Padblock Start Block 165529
Done with: Ending Padblock Block(s)150
Max brk space used 61000
165679 extents written (323 MB)

这里做的比较简便,将安装包也一起打包到iso了,其实我们使用了网络安装,安装包不需要也可以的,只要有linux引导三部曲(vmlinux,initrd,root分区信息)就可以了,不过现在的新系统都比较丰满了,包装了很多东西进去,也懒得删减了,毕竟本身也就几百M。

2.安装web 服务器 apache(可以是ftp,nfs)

yum -y install httpd

默认安装后配置文件在/etc/httpd/conf/httpd.conf,默认使用apache 用户运行,80端口监听,网页存放目录/var/www/html/

启动apache(root用户运行)

service httpd start

创建一个ks.cfg的kickstart配置(demo模板在/root/anaconda-ks.cfg),并放到web服务器apache 的网页目录,例如默认的目录就是/var/www/html/

install
url --url http://192.168.6.127/centos/
lang en_US.UTF-8
keyboard us
network --onboot yes --device eth0 --bootproto dhcp --noipv6 --hostname=XXXX
rootpw XXXX
firewall --disabled
authconfig --enableshadow --passalgo=sha512
selinux --disabled
timezone --utc Asia/Shanghai
bootloader --location=mbr --driveorder=sda --append="crashkernel=auto rhgb quiet"

clearpart --all --drives=sda

part / --fstype=ext4 --size=7000
part /var/log --fstype=ext4 --size=1024
part swap --size=1024
part /workerfree --fstype=ext4 --size=30 --grow

#repo --name="CentOS"  --baseurl=cdrom:sr0 --cost=100

%packages --nobase
@core

%post --interpreter=/bin/bash
(
cd /mnt
/usr/bin/curl -O http://192.168.6.127/XXXX.tar.gz
/bin/tar -zxpf XXXX.tar.gz
cd XXXX
/bin/sh install.sh
)2>&1 >> /root/post-install.log
%end
reboot

注意修改权限755,起码可以让apache服务器访问这个文件

mkdir /var/www/html/centos
chmod 755 /var/www/html/centos/isolinux/ks.cfg

《kickstart+http安装centos(没有pxe)》

这个位置就是ks=http://192.168.6.127/centos/isolinux/ks.cfg访问的位置,遥相呼应的

将安装光盘镜像iso的挂载的目录复制到web服务器apache的网页目录,达到可以网页访问的效果

mkdir /var/www/html/centos
cp -arp /tmp/isomake_dir/* /var/www/html/centos

这里做的目的是网络安装,启动盘读到kickstart文件后转到访问网络安装镜像的位置进行安装

至此完成配置,即可试验。

试验

开机启动选择http

然后就一键安装了

科普时间

  • mkisofs的参数意义

-r -rational-rock 使用Rock Ridge Extensions,并开放全部文件的读取权限。

-J -joliet -J或-joliet 使用Joliet格式的目录与文件名称。

-b:启动image

-c boot_catalog #指定启动的boot catalog

Specifies  the  path  and  filename  of the boot catalog, which is required for an El Torito bootable CD. The pathname must be relative to the source path specified to genisoimage.  This file will be
inserted into the output tree and not created in the source filesystem, so be sure the specified filename does not conflict with an existing file,  or  it  will  be  excluded.  Usually  a  name  like
boot.catalog is chosen.
If -sort has not been specified, the boot catalog sorted with low priority (+1) to the beginning of the medium.  If you don’t like this, you need to specify a sort weight of 0 for the boot catalog.

-no-emul-boot Boot image is ‘no emulation’ image即非模拟模式启动

-boot-load-size 4 #指定启动的分区,设置为4是为了兼容某些bios

Specifies the number of "virtual" (512-byte) sectors to load in no-emulation mode.  The default is to load the entire boot file.  Some BIOSes may have problems if this is not a multiple of 4.

-boot-info-table #指定启动信息

Specifies that a 56-byte table with information of the CD-ROM layout will be patched in at offset 8 in the boot file.  If this option is given, the boot file is modified in the source filesystem,  so
make a copy of this file if it cannot be easily regenerated!  See the EL TORITO BOOT INFO TABLE section for a description of this table.

有兴趣可以自行man mkisofs

  • kickstart文件配置

这里我说说我的配置文件

install #安装模式
url --url http://192.168.6.127/centos/  #使用url模式,也支持nfs,harddisk,cdrom,这里就是说安装方式
lang en_US.UTF-8        #我这里不需要考虑中文
keyboard us #键盘制式
network --onboot yes --device eth0 --bootproto dhcp --noipv6 --hostname=XXXX        #配置网络相关信息
rootpw XXXXX    #配置root密码
firewall --disabled #默认关闭iptables
authconfig --enableshadow --passalgo=sha512 #配置验证方式,使用sha512加密密码
selinux --disabled  #默认关闭selinux
timezone --utc Asia/Shanghai        #时区
bootloader --location=mbr --driveorder=sda --append="crashkernel=auto rhgb quiet"   #引导内容,安装引导程序grub到mbr,磁盘识别的顺序是sda优先,使用auto rhgb quiet的内核启动参数,该参数rhgb表示redhat graphics boot,就是会看到图片来代替启动过程中显示的文本信息,这些信息在启动后用dmesg也可以看到,quiet表示在启动过程中只有重要信息显示,类似硬件自检的消息不回显示

clearpart --all --drives=sda        #清理磁盘所有内容,清理的磁盘是sda盘,关于sda和hda,sdb的区别hda一般是指IDE接口的硬盘,hda一般指第一块硬盘,类似的有hdb,hdc等,sda一般是指SATA接口的硬盘,sda一般指第一块硬盘,类似的有sdb,sdc等

part / --fstype=ext4 --size=7000        #分区设置
part /var/log --fstype=ext4 --size=1024
part swap --size=1024   #swap配置
part /workerfree --fstype=ext4 --size=30 --grow #--grow代表增长到可以用的最大

#repo --name="CentOS"  --baseurl=cdrom:sr0 --cost=100

%packages --nobase  #nobase是为了防止弹出的警告说没有basegroup的组件
@core   #mini的iso只有core的group包可以安装

%post --interpreter=/bin/bash   #配置安装后的动作,这里指定bash作为环境变量和脚本执行工具
(
cd /mnt
/usr/bin/curl -O http://192.168.6.127/XXXX.tar.gz
/bin/tar -zxpf XXXX.tar.gz
cd XXXX
/bin/sh install.sh
)2>&1 >> /root/post-install.log #自定义脚本命令或者命令合集,例如可以下载文件,配置网络等等,并且将内容的执行日志输出到指定的日志
%end    #结束%post的动作
reboot  #安装完成后自动重启
~ 

详细可以参考官方:

https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Installation_Guide/s1-kickstart2-options.html

这里也有几个可以看的翻译文章,但是怎么说呢,翻译总是滞后,并且不一定翻译对,最重要还是要理解原文作者的意思,这样才能搞懂,以不变应万变,当然实操试验才是王道。

参考:http://www.cnblogs.com/xiaOt119/p/3143462.html

参考:http://bbs.linuxtone.org/thread-7539-1-1.html

troubleshooting

Unable to read package metadata. This may be due to missing repodata directory. Please ensure that your install tree has been correctly generated. 

Failare:
repodata/743fec56b2af0ce8d6ec82c47a4efafc2a4d18cddfa9683f29611cb18d1a33de-primary.sqlite.bz2 from anaconda-CentOS-201311271240.i386: [Ermo 256] No more mirrors to try.

这里是因为iso经过解压后将里面的bz或者gz压缩文件解压了,导致安装程序的安装过程处理失败,解决办法就是找一个完整的安装iso,不要解压,用iso的挂载方式重新读取repodata下的文件,然后覆盖你的系统安装资源即可

引用参考:http://stackoverflow.com/questions/21637068/unable-to-read-package-metadata-this-may-be-due-to-missing-repodata-directory

原文链接:http://www.godblessyuan.com/2015/01/24/kickstart_http_install_centos_no_pxe/

    原文作者:线上猛如虎_线下怂如鼠
    原文地址: https://www.jianshu.com/p/53733a9ab360
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞