• 3


    目录
    【任务 3】私有云运维开发[10 分]
    【题目 1】Ansible 服务部署:部署 MariaDB 集群[2 分]
    【题目 2】Ansible 服务部署:部署ELK 集群服务[2 分]
    【题目 3】Python 运维开发:基于OpenStack Restful API 实现镜像上传[1 分]
    【题目 4】Python 运维开发:基于 Openstack Python SDK 实现云主机创建[1 分]
    【题目 5】Python 运维开发:云主机类型管理的命令行工具开发[2 分]
    【题目 6】Python 运维开发:用户管理的命令行工具开发[2 分]

    【任务 3】私有云运维开发[10 分]
    【题目 1】Ansible 服务部署:部署 MariaDB 集群[2 分]
    使用OpenStack 私有云平台,创建 4 台系统为centos7.9 的云主机,其中一台作为 Ansible 的母机并命名为 ansible,另外三台云主机命名为 node1、node2、node3;使用这一台母机,编写 Ansible 脚本(在/root 目录下创建 example 目录作为 Ansible 工作目录,部署的入口文件命名为 cscc_install.yaml ) , 对其他三台云主机进行安装高可用数据库集群 (MariaDB_Galera_cluster,数据库密码设置为 123456)的操作(所需的安装包在 HTTP 服务中)。完成后提交 Ansible 节点的用户名、密码和 IP 地址到答题框。(考试系统会连接到你的Ansible 节点,去执行 Ansible 脚本,请准备好 Ansible 运行环境,以便考试系统访问)
    (1)环境准备
    节点 地址
    ansible 192.168.100.10
    node1 192.168.100.20
    node2 192.168.100.30
    node3 192.168.100.40
    (2)环境配置
    [root@localhost ~]# hostnamectl set-hostname ansible
    [root@localhost ~]# su
    [root@ansible ~]#

    [root@localhost ~]# hostnamectl set-hostname node1
    [root@localhost ~]# su
    [root@node1 ~]#

    [root@localhost ~]# hostnamectl set-hostname node2
    [root@localhost ~]# su
    [root@node2 ~]#

    [root@localhost ~]# hostnamectl set-hostname node3
    [root@localhost ~]# su
    [root@node3 ~]#

    将ip地址映射成主机名(其他3台节点配置一样,此处省略)

    [root@ansible ~]# vi /etc/hosts
    192.168.100.10 ansible
    192.168.100.20 node1
    192.168.100.30 node2
    192.168.100.40 node3
    127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6

    #关闭防火墙和selinux(其他3台节点配置一样,此处省略)
    [root@ansible ~]# systemctl stop firewalld && setenforce 0

    ansible节点部署ansible服务(通过CRT远程工具将提供的ansible软件包上传到ansible节点)

    [root@ansible ~]# tar -xvf ansible.tar.gz

    #删除centos自带的源并配置本地yum源安装ansible服务
    [root@ansible ~]# mv /etc/yum.repos.d/* /home/
    [root@ansible ~]# vi /etc/yum.repos.d/local.repo
    [ansible]
    name=ansible
    baseurl=file:///root/ansible
    gpgcheck=0
    enabled=1

    [root@ansible ~]# yum install ansible -y

    #检查是否安装完成
    [root@ansible ~]# ansible --version
    ansible 2.9.10
    config file = /etc/ansible/ansible.cfg
    configured module search path = [u’/root/.ansible/plugins/modules’, u’/usr/share/ansible/plugins/modules’]
    ansible python module location = /usr/lib/python2.7/site-packages/ansible
    executable location = /usr/bin/ansible
    python version = 2.7.5 (default, Oct 14 2020, 14:45:30) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
    (3)设置免密
    [root@ansible ~]# ssh-keygen
    Generating public/private rsa key pair.
    Enter file in which to save the key (/root/.ssh/id_rsa):
    Enter passphrase (empty for no passphrase):
    Enter same passphrase again:
    Your identification has been saved in /root/.ssh/id_rsa.
    Your public key has been saved in /root/.ssh/id_rsa.pub.
    The key fingerprint is:
    SHA256:svV0oqhTRNGB/tH37Xi49/OUg+AFu2XlxcKhsyXm+wI root@ansible
    The key’s randomart image is:
    ±–[RSA 2048]----+
    | .+… . |
    | o . o o |
    | o . .= +.o|
    | o . .o+=o…|
    | …S.o++= o |
    | .=.+Eo*.o o|
    | .o . .+… .|
    | … …oo=|
    | … .o+
    |
    ±—[SHA256]-----+

    [root@ansible ~]# ssh-copy-id root@node1
    /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: “/root/.ssh/id_rsa.pub”
    The authenticity of host ‘node1 (192.168.100.20)’ can’t be established.
    ECDSA key fingerprint is SHA256:sGY4R7darwNovS7pGeQNm9FzQjanFFmnwJHO5PRo0RE.
    ECDSA key fingerprint is MD5:fa:5d:85:af:69:0d:63:78:ac:4e:9a:5b:c1:a6:b2:60.
    Are you sure you want to continue connecting (yes/no)? yes
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed – if you are prompted now it is to install the new keys
    root@node1’s password:

    Number of key(s) added: 1

    Now try logging into the machine, with: “ssh ‘root@node1’”
    and check to make sure that only the key(s) you wanted were added.

    [root@ansible ~]# ssh-copy-id root@node2
    /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: “/root/.ssh/id_rsa.pub”
    The authenticity of host ‘node2 (192.168.100.30)’ can’t be established.
    ECDSA key fingerprint is SHA256:sGY4R7darwNovS7pGeQNm9FzQjanFFmnwJHO5PRo0RE.
    ECDSA key fingerprint is MD5:fa:5d:85:af:69:0d:63:78:ac:4e:9a:5b:c1:a6:b2:60.
    Are you sure you want to continue connecting (yes/no)? yes
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed – if you are prompted now it is to install the new keys
    root@node2’s password:

    Number of key(s) added: 1

    Now try logging into the machine, with: “ssh ‘root@node2’”
    and check to make sure that only the key(s) you wanted were added.

    [root@ansible ~]# ssh-copy-id root@node3
    /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: “/root/.ssh/id_rsa.pub”
    The authenticity of host ‘node3 (192.168.100.40)’ can’t be established.
    ECDSA key fingerprint is SHA256:sGY4R7darwNovS7pGeQNm9FzQjanFFmnwJHO5PRo0RE.
    ECDSA key fingerprint is MD5:fa:5d:85:af:69:0d:63:78:ac:4e:9a:5b:c1:a6:b2:60.
    Are you sure you want to continue connecting (yes/no)? yes
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed – if you are prompted now it is to install the new keys
    root@node3’s password:

    Number of key(s) added: 1

    Now try logging into the machine, with: “ssh ‘root@node3’”
    and check to make sure that only the key(s) you wanted were added.
    (4)修改ansible文件配置组
    [root@ansible ~]# vi /etc/ansible/hosts
    [node]
    node1
    node2
    node3

    [node1]
    node1

    [node2]
    node2

    [node3]
    node3
    #解压mariadb.tar软件包将mariadb-repo源拷贝到node1,node2,node3节点的/root目录下
    [root@ansible ~]# tar -xvf mariadb.tar
    mariadb/
    mariadb/mariadb-repo.tar.gz
    mariadb/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
    mariadb/schema.xml
    [root@ansible ~]# tar -xvf mariadb/mariadb-repo.tar.gz
    [root@ansible ~]# scp -r mariadb-repo/ root@node1:/root
    [root@ansible ~]# scp -r mariadb-repo/ root@node2:/root
    [root@ansible ~]# scp -r mariadb-repo/ root@node3:/root

    (5)创建ansible执行的工作目录编写执行playbook剧本的相关文件
    [root@ansible ~]# mkdir cscc_galera(应该是example目录)
    [root@ansible ~]# cd cscc_galera/

    #创建local.repo文件
    [root@ansible cscc_galera]# vi local.repo
    [centos]
    name=centos
    baseurl=file:///opt/centos #自行挂载centos7.9镜像到/opt目录
    gpgcheck=0
    enabled=1
    [mariadb]
    name=mariadb
    baseurl=file:///root/mariadb-repo
    gpgcheck=0
    enabled=1

    #创建server.cnf文件
    [root@ansible cscc_galera]# vi server.cnf
    [server]
    [mysqld]
    [galera]
    wsrep_on=ON
    wsrep_provider=/usr/lib64/galera/libgalera_smm.so
    wsrep_cluster_address=gcomm://node1,node2,node3
    binlog_format=row
    default_storage_engine=InnoDB
    innodb_autoinc_lock_mode=2
    bind-address=0.0.0.0
    wsrep_slave_threads=1
    innodb_flush_log_at_trx_commit=0
    [embedded]
    [mariadb]
    [mariadb-10.3]

    #创建playbook剧本文件
    [root@ansible cscc_galera]# vi install_galera.yaml(应该是cscc_install.yaml)

    • hosts: all
      remote_user: root
      tasks:
      • name: rm repo
        shell: rm -rf /etc/yum.repos.d/*
      • name: copy repo
        copy: src=local.repo dest=/etc/yum.repos.d/
      • name: install mariadb
        yum: name=mariadb-server
      • name: start mariadb
        service: name=mariadb state=started enabled=yes
      • name: init_mysql
        shell: mysqladmin -uroot password 123456
      • name: stop mariadb
        service: name=mariadb state=stopped
      • name: copy server.cnf
        copy: src=server.cnf dest=/etc/my.cnf.d/server.cnf
    • hosts: node1
      remote_user: root
      tasks:
      • name: start mariadb
        shell: /usr/sbin/mysqld --wsrep-new-cluster -u root &
    • hosts: node2,node3
      remote_user: root
      tasks:
      • name: start mariadb
        shell: systemctl start mariadb
        (6)执行入口文件
        [root@ansible cscc_galera]# ansible-playbook install_galera.yaml
        [WARNING]: Found both group and host with same name: node1
        [WARNING]: Found both group and host with same name: node3
        [WARNING]: Found both group and host with same name: node2

    PLAY [all] *******************************************************************************************************

    TASK [Gathering Facts] *******************************************************************************************
    ok: [node3]
    ok: [node2]
    ok: [node1]

    TASK [rm repo] ***************************************************************************************************
    [WARNING]: Consider using the file module with state=absent rather than running ‘rm’. If you need to use command
    because file is insufficient you can add ‘warn: false’ to this command task or set ‘command_warnings=False’ in
    ansible.cfg to get rid of this message.
    changed: [node2]
    changed: [node3]
    changed: [node1]

    TASK [copy repo] *************************************************************************************************
    changed: [node3]
    changed: [node2]
    changed: [node1]

    TASK [install mariadb] *******************************************************************************************
    changed: [node2]
    changed: [node3]
    changed: [node1]

    TASK [start mariadb] *********************************************************************************************
    changed: [node1]
    changed: [node2]
    changed: [node3]

    TASK [init_mysql] ************************************************************************************************
    changed: [node2]
    changed: [node1]
    changed: [node3]

    TASK [stop mariadb] **********************************************************************************************
    changed: [node1]
    changed: [node2]
    changed: [node3]

    TASK [copy server.cnf] **********************************************************************************************
    changed: [node1]
    changed: [node2]
    changed: [node3]

    PLAY [node1] *****************************************************************************************************

    TASK [Gathering Facts] *******************************************************************************************
    ok: [node1]

    TASK [start mariadb] *********************************************************************************************
    changed: [node1]

    PLAY [node2,node3] ***********************************************************************************************

    TASK [Gathering Facts] *******************************************************************************************
    ok: [node2]
    ok: [node3]

    TASK [start mariadb] *********************************************************************************************
    changed: [node2]
    changed: [node3]

    PLAY RECAP *******************************************************************************************************
    node1 : ok=9 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
    node2 : ok=9 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
    node3 : ok=9 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

    [root@ansible cscc_galera]#
    (7)验证,检查wsrep_cluster_size的值是否为3/wsrep_ready的状态是否为ON
    [root@ansible cscc_galera]# ssh node1
    Last login: Fri Aug 26 05:01:52 2022 from 192.168.100.1
    [root@node1 ~]# mysql -uroot -p123456
    Welcome to the MariaDB monitor. Commands end with ; or \g.
    Your MariaDB connection id is 10
    Server version: 10.3.23-MariaDB MariaDB Server

    Copyright © 2000, 2018, Oracle, MariaDB Corporation Ab and others.

    Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement.

    MariaDB [(none)]> show status like ‘wsrep_cluster_size’;
    ±-------------------±------+
    | Variable_name | Value |
    ±-------------------±------+
    | wsrep_cluster_size | 3 |
    ±-------------------±------+
    1 row in set (0.001 sec)

    MariaDB [(none)]> show status like ‘wsrep_ready’;
    ±--------------±------+
    | Variable_name | Value |
    ±--------------±------+
    | wsrep_ready | ON |
    ±--------------±------+
    1 row in set (0.001 sec)

    MariaDB [(none)]>
    1.执行yaml 文件正确计 0.5 分
    2.检查数据库集群部署正确计 1 分
    3.检查数据库集群数量为 3 正确计 0.5 分

    【题目 2】Ansible 服务部署:部署ELK 集群服务[2 分]
    使用赛项提供的OpenStack 私有云平台,创建三台 CentOS7.9 系统的云主机分别命名为elk-1、elk-2 和elk-3,Ansible 主机可以使用上一题的环境。要求Ansible 节点编写剧本,执行 Ansible 剧本可以在这三个节点部署 ELK 集群服务(在/root 目录下创建 install_elk 目录作为 ansible 工作目录,部署的入口文件命名为 install_elk.yaml)。具体要求为三个节点均安装Elasticserach 服务并配置为 Elasticserach 集群;kibana 安装在第一个节点;Logstash 安装在第二个节点。(需要用到的软件包在 HTTP 服务下)完成后提交 ansible 节点的用户名、密码和 IP 地址到答题框。(考试系统会连接到 ansible 节点,执行 ansible 脚本,准备好环境, 以便考试系统访问)
    1.执行yaml 文件正确计 0.5 分
    2.检查 ELK 服务部署正确计 1.5 分
    (1)配置主机映射:
    修改当前节点主机名为ansible,并修改ansible节点主机映射,命令如下:
    [root@localhost ~]# hostnamectl set-hostname ansible
    [root@localhost ~]# bash
    [root@ansible ~]# cat /etc/hosts
    172.128.11.162 ansible
    172.128.11.217 node1
    172.128.11.170 node2
    172.128.11.248 node3
    配置免密访问,虚拟机root用户密码为000000,命令如下:(考试的时候命名为elk-1、elk-2 和elk-3)
    [root@ansible ~]# ssh-keygen
    [root@ansible ~]# ssh-copy-id node1
    [root@ansible ~]# ssh-copy-id node2
    [root@ansible ~]# ssh-copy-id node3
    将ansible节点的域名解析文件复制给安装Elasticsearch集群服务的三个节点,命令如下:
    [root@ansible ~]# scp /etc/hosts node1:/etc/
    [root@ansible ~]# scp /etc/hosts node2:/etc/
    [root@ansible ~]# scp /etc/hosts node3:/etc/
    关闭ansible节点的防火墙和Selinux配置(如已关闭,则不需要操作),命令如下:
    [root@ansible ~]# setenforce 0
    (2)软件包下载及yum源配置
    将提供的Elasticsearch、Kibana以及Logstash软件包下载至ansible节点/root目录下,并将相应服务的软件包拷贝至不同节点。
    [root@ansible ~]# curl -O http://mirrors.douxuedu.com/competition/Ansible.tar.gz
    [root@ansible ~]# tar -zxvf Ansible.tar.gz
    将Elasticsearch软件包拷贝至三个节点,将Kibana软件包拷贝至node1节点,将Logstash软件包拷贝至node2节点,命令如下:
    [root@ansible ~]# scp elasticsearch-6.0.0.rpm node1:/root/
    [root@ansible ~]# scp elasticsearch-6.0.0.rpm node2:/root/
    [root@ansible ~]# scp elasticsearch-6.0.0.rpm node3:/root/
    [root@ansible ~]# scp kibana-6.0.0-x86_64.rpm node1:/root/
    [root@ansible ~]# scp kibana-6.0.0-x86_64.rpm node2:/root/
    [root@ansible ~]# scp kibana-6.0.0-x86_64.rpm node3:/root/
    [root@ansible ~]# scp logstash-6.0.0.rpm node1:/root/
    [root@ansible ~]# scp logstash-6.0.0.rpm node2:/root/
    [root@ansible ~]# scp logstash-6.0.0.rpm node3:/root/
    将软件包ansible.tar.gz上传至ansible节点配置本地yum源,并安装Ansible:
    [root@ansible ~]# tar -zxvf ansible.tar.gz -C /opt/
    [root@ansible ~]# mv /etc/yum.repos.d/* /media/
    [root@ansible ~]# vi /etc/yum.repos.d/local.repo
    [ansible]
    name=ansible
    baseurl=file:///opt/ansible
    gpgcheck=0
    enabled=1
    [root@ansible ~]# yum -y install ansible
    (3)配置Ansible主机映射
    创建示例目录,并配置Ansible主机映射。
    [root@ansible ~]# mkdir example
    [root@ansible ~]# cd example
    [root@ansible example]# vi /etc/ansible/hosts
    [node1]
    172.128.11.217
    [node2]
    172.128.11.170
    [node3]
    172.128.11.248
    使用CentOS-7-x86_64-DVD-2009.iso镜像文件作为安装库,将镜像挂载至/opt/centos,编写yum源文件,安装vsftpd服务,用于给远程主机安装Java。命令如下:
    [root@ansible example]# curl -O http://mirrors.douxuedu.com/competition/CentOS-7-x86_64-DVD-2009.iso
    [root@ansible example]# mkdir /opt/centos
    [root@ansible example]# mount CentOS-7-x86_64-DVD-2009.iso /opt/centos/
    [root@ansible example]# vi /etc/yum.repos.d/local.repo
    [ansible]
    name=ansible
    baseurl=file:///opt/ansible
    gpgcheck=0
    enabled=1
    [centos]
    name=centos
    baseurl=file:///opt/centos
    gpgcheck=0
    enabled=1
    [root@ansible example]# yum install -y vsftpd
    [root@ansible example]# vi /etc/vsftpd/vsftpd.conf
    anon_root=/opt
    [root@ansible example]# systemctl restart vsftpd
    [root@ansible example]# vi ftp.repo
    [centos]
    name=centos
    baseurl=ftp://172.128.11.162/centos/
    gpgcheck=0
    enabled=1
    (4)安装Elasticsearch获取配置文件
    安装Elasticsearch服务并编写node1节点配置文件,命令如下:
    [root@ansible example]# rpm -ivh /root/elasticsearch-6.0.0.rpm
    [root@ansible example]# cp -rf /etc/elasticsearch/elasticsearch.yml elk1.yml
    [root@ansible example]# cat elk1.yml | grep -Ev “^KaTeX parse error: Expected group after '^' at position 2: |^̲#" cluster.name…|^#”
    cluster.name: ELK
    node.name: node2
    node.master: false
    node.data: true
    path.data: /var/lib/elasticsearch
    path.logs: /var/log/elasticsearch
    network.host: 172.128.11.170
    http.port: 9200
    discovery.zen.ping.unicast.hosts: [“node1”,“node2”,“node3”]
    编写node3节点配置文件,命令如下:
    [root@ansible example]# cp elk1.yml elk3.yml
    [root@ansible example]# cat elk3.yml | grep -Ev “$|#”
    cluster.name: ELK
    node.name: node3
    node.master: false
    node.data: true
    path.data: /var/lib/elasticsearch
    path.logs: /var/log/elasticsearch
    network.host: 172.128.11.248
    http.port: 9200
    discovery.zen.ping.unicast.hosts: [“node1”,“node2”,“node3”]
    (5)安装Kibana获取配置文件
    安装Kibana服务并编写配置文件,命令如下:
    [root@ansible example]# rpm -ivh /root/kibana-6.0.0-x86_64.rpm
    [root@ansible example]# cp -rf /etc/kibana/kibana.yml .
    [root@ansible example]# cat kibana.yml |grep -v ^#
    server.port: 5601
    server.host: “172.128.11.217”
    elasticsearch.url: “http://172.128.11.217:9200”
    (6)安装Logstash获取配置文件
    安装Logstash服务并获取配置文件,命令如下:
    [root@ansible example]# rpm -ivh /root/logstash-6.0.0.rpm
    [root@ansible example]# cp -rf /etc/logstash/logstash.yml .
    [root@ansible example]# vi logstash.yml
    http.host: “172.128.11.170”
    新建日志输出文件,内容如下:
    [root@ansible example]# vi syslog.conf
    input {
    file {
    path => “/var/log/messages” #指定文件的路径
    type => “systemlog” #定义日志类型,可自定义
    start_position => “beginning” #指定何时开始收集
    stat_interval => “3”
    }
    }
    output {
    if [type] == “systemlog” {
    elasticsearch {
    hosts => [“172.128.11.217:9200”] #这里的地址为node1主机地址
    index => “system-log-%{+YYYY.MM.dd}”
    }
    }
    }
    (7)编写剧本文件
    安编写Playbook剧本文件,命令如下:
    [root@ansible example]# vi cscc_install.yaml

    • hosts: all
      remote_user: root
      tasks:
      • name: rm repo
        shell: rm -rf /etc/yum.repos.d/*
      • name: copy repo
        copy: src=ftp.repo dest=/etc/yum.repos.d/
      • name: install java
        shell: yum -y install java-1.8.0-*
      • name: install elk
        shell: rpm -ivh elasticsearch-6.0.0.rpm
    • hosts: node1
      remote_user: root
      tasks:
      • name: copy config
        copy: src=elk1.yml dest=/etc/elasticsearch/elasticsearch.yml
      • name: daemon-reload
        shell: systemctl daemon-reload
      • name: start elk
        shell: systemctl start elasticsearch && systemctl enable elasticsearch
      • name: install kibana
        shell: rpm -ivh kibana-6.0.0-x86_64.rpm
      • name: copy config
        template: src=kibana.yml dest=/etc/kibana/kibana.yml
      • name: start kibana
        shell: systemctl start kibana && systemctl enable kibana
    • hosts: node2
      remote_user: root
      tasks:
      • name: copy config
        copy: src=elk2.yml dest=/etc/elasticsearch/elasticsearch.yml
      • name: daemon-reload
        shell: systemctl daemon-reload
      • name: start elk
        shell: systemctl start elasticsearch && systemctl enable elasticsearch
      • name: install logstash
        shell: rpm -ivh logstash-6.0.0.rpm
      • name: copy config
        copy: src=logstash.yml dest=/etc/logstash/logstash.yml
      • name: copy config
        copy: src=syslog.conf dest=/etc/logstash/conf.d/syslog.conf
    • hosts: node3
      remote_user: root
      tasks:
      • name: copy config
        copy: src=elk3.yml dest=/etc/elasticsearch/elasticsearch.yml
      • name: daemon-reload
        shell: systemctl daemon-reload
      • name: start elk
        shell: systemctl start elasticsearch && systemctl enable elasticsearch
        执行Playbook完成ELK集群的部署,命令如下:
        [root@ansible example]# ansible-playbook cscc_install.yaml
        执行结果如图2所示:

    图2 执行结果
    浏览器访问node1节点5601端口,http://172.128.11.217:5601/,如图3所示:

    图3 部署成功
    【题目 3】Python 运维开发:基于OpenStack Restful API 实现镜像上传[1 分]
    使用OpenStack all-in-one 镜像,创建 OpenStack Python 运维开发环境。云主机的用户/ 密码为:“root/Abc@1234”,OpenStack 的域名/账号/密码为:“demo/admin/000000”。 提示说明:python 脚本文件头建议加入“#encoding:utf-8”避免编码错误;测试脚本代码用 python3 命令执行与测试。 在 controller 节点的/root 目录下创建api_image_manager.py 脚本,编写 python 代码对接OpenStack API,完成镜像的创建与上传。创建之前查询是否存在“同名镜像”,如果存在先删除该镜像。
    (1)创建镜像:要求在 OpenStack 私有云平台中上传镜像cirros-0.3.4-x86_64-disk.img,名字为 cirros001,disk_format 为 qcow2,container_format 为bare。
    (2)查询镜像:查询 cirros001 的详细信息,并以 json 格式文本输出到控制台。 完成后提交 OpenStack Python 运维开发环境Controller 节点的 IP 地址,用户名和密码提交。
    1.执行api_image_manager.py 脚本,成功创建镜像,计 0.5 分
    2.检查镜像状态正确,计 0.5 分
    (1)基础环境准备,安装python3,已经需要的依赖

    安装工具包

    yum install gcc -y
    yum install libffi-devel -y

    yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make

    安装python3

    tar -xzvf Python-3.6.8.tgz
    cd Python-3.6.8

    ./configure
    make # 编译
    make install # 编译安装

    验证:
    [root@controller ~]# python3
    Python 3.6.8 (default, Oct 29 2023, 22:01:11)
    [GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux
    Type “help”, “copyright”, “credits” or “license” for more information.

    [root@ controller ~]# python3 --version
    Python 3.6.8

    安装文件依赖包:

    安装setuptools

    tar -zxvf setuptools-41.1.0.post1.tar.gz
    cd setuptools-41.1.0.post1/
    python3 setup.py install

    安装pip

    tar -zxvf pip-19.2.2.tar.gz
    cd pip-19.2.2
    python3 setup.py install
    验证pip
    [root@controller pip-19.2.2]# pip -V
    pip 19.2.2 from /usr/local/lib/python3.6/site-packages/pip-19.2.2-py3.6.egg/pip (python 3.6)

    #使用提供的whl文件安装依赖
    [root@controller ~]# cd Python-api
    [root@controller ~]# pip3 install certifi-2019.11.28-py2.py3-none-any.whl
    [root@controller ~]# pip3 install urllib3-1.25.11-py3-none-any.whl
    [root@controller ~]# pip3 install idna-2.8-py2.py3-none-any.whl
    [root@controller ~]# pip3 install chardet-3.0.4-py2.py3-none-any.whl
    [root@controller ~]# pip3 install requests-2.24.0-py2.py3-none-any.whl
    (2)代码实现
    [root@controller ~]# vi api_image_manager.py
    #encoding:utf-8

    import requests,json,time
    def get_auth_token(controller_ip,domain,name,password):
    url = f"http://{controller_ip}:5000/v3/auth/tokens"
    body = {
    “auth”: {
    “identity”: {
    “methods”: [‘password’],
    “password”: {
    “user”: {
    “domain”: {“name”: domain},
    “name”: name,
    “password”: password,
    }
    }
    },
    “scope”: {
    “project”: {
    “domain”: {“name”: domain},
    “name”: name
    }
    }
    }
    }
    headers = {
    “Content-Type”: “application/json”
    }
    token = requests.post(url,headers=headers,data=json.dumps(body)).headers[‘X-Subject-Token’]
    headers = {
    “X-Auth-Token”: token
    }
    print(f"token值为:{token}")
    return headers

    class image_manager:
    def init(self,handers:dict,resUrl):
    self.headers = handers
    self.resUrl = resUrl

    def create_image(self,image_name,disk_format,container_format):
        body = {
            "name": image_name,
            "disk_format": disk_format,
            "container_format": container_format,
        }
    
        req = requests.post(self.resUrl,headers=self.headers,data=json.dumps(body)).text
        print(f"创建镜像的信息为:{req}")
        return req
    
    def get_image_id(self,name):
        req = json.loads(requests.get(self.resUrl,headers=self.headers).text)
        for image in req['images']:
            if image['name'] == name:
                return image['id']
        return "NONE"
    
    def upload_image(self,id,file_path:str):
        url = self.resUrl + "/" + id + "/file"
        self.headers["Content-Type"] = "application/octet-stream"
        req = requests.put(url,headers=self.headers,data=open(file_path,'rb').read())
        if req.status_code == 204:
            print("上传镜像成功",req.status_code)
        else:
            print("上传镜像失败",req.status_code)
    
        print(f"镜像上传信息:{req}")
        return req
    
    def get_image(self,id):
        url = self.resUrl + "/" + id
        req = json.loads(requests.get(self.resUrl,headers=self.headers).text)
        print(f"获取到的镜像信息为:{req}")
        return req
    
    def delete_image(self,id):
        url = self.resUrl + "/" + id
        req = requests.delete(url,headers=self.headers)
        print(f"删除信息:{req}")
        return req
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    if name == “main”:
    controller_ip = “192.168.100.10”
    domain = “demo”
    name = “admin”
    password = “000000”
    headers = get_auth_token(controller_ip, domain, name, password)
    print(headers)
    image_m = image_manager(headers,f"http://{controller_ip}:9292/v2/images")

    #create
    create_image = image_m.create_image("cirros001","qcow2","bare")
    
    #get id
    get_id = image_m.get_image_id("cirros001")
    print(f"cirros001镜像ID为:{get_id}")
    
    #upload
    upload_image = image_m.upload_image(get_id,"/root/cirros-0.3.0-x86_64-disk.img ")
    
    #get image
    get_image = image_m.get_image(get_id)
    with open("image_demo.json","w")as outfile:
        json.dump(get_image,outfile,indent=4)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    【题目 4】Python 运维开发:基于 Openstack Python SDK 实现云主机创建[1 分]
    使 用 已 建 好 的 OpenStack Python 运 维 开 发 环 境 ,在 /root 目 录 下 创 建sdk_server_manager.py 脚本,使用 python-openstacksdk Python 模块,完成云主机的创建和查询。创建之前查询是否存在“同名云主机”,如果存在先删除该镜像。
    (1)创建 1 台云主机:云主机信息如下:
    云主机名称如下:server001
    镜像文件:cirros-0.3.4-x86_64-disk.img
    云主机类型:m1.tiny
    网络等必要信息自己补充。
    (2)查询云主机:查询云主机 server001 的详细信息,并以 json 格式文本输出到控制台。 完成后提交 OpenStack Python 运维开发环境 Controller 节点的 IP 地址,用户名和密码提交。
    1.执行 sdk_server_manager.py 脚本,成功创建云主机,计 0.5 分
    2.检查创建的云主机状态正确,计 0.5 分
    (1)代码实现
    创建云主机所需的镜像、类型、网络是用name来识别的,需要提前创建好
    [root@container ~]# vi sdk_server_manager.py
    #encoding:utf-8
    import json,logging

    import openstack

    #文档地址

    https://docs.openstack.org/openstacksdk/latest/user/index.html

    def create_connection(auth_url, user_domain_name, username, password):
    “”"
    建立连接
    “”"
    return openstack.connect(
    auth_url=auth_url,
    user_domain_name=user_domain_name,
    username=username,
    password=password,
    )

    #user Manager

    参见文档

    https://docs.openstack.org/openstacksdk/latest/user/guides/identity.html

    #openstack.connection.Connection
    #云主机管理
    class server_manager:

    def __init__(self, connect):
        self.connect = connect
    
    def list_servers(self):
        """
        查询所有云主机.
        """
        #to json
        items = self.connect.compute.servers()
        server_jsons = {}
        for server in items:
            server_jsons[server['name']] = server
        # return ""
        return items# json.dumps(server_jsons,indent=2,skipkeys=True)
    
    def create_server(self, server_name, image_name, flavor_name,networ_name):
        image = self.connect.compute.find_image(image_name)
        flavor = self.connect.compute.find_flavor(flavor_name)
        network = self.connect.network.find_network(networ_name)
        server = self.connect.compute.create_server(
            name=server_name, image_id=image.id, flavor_id=flavor.id,
            networks=[{"uuid": network.id}])
        result = self.connect.compute.wait_for_server(server)
        return result#json.dumps(result,indent=2,skipkeys=True)
    
    def delete_server(self, server_name):
        """
        删除云主机
        """
        server = self.connect.compute.find_server(server_name)
        result = self.connect.compute.delete_server(server)
        return json.dumps(result, indent=2, skipkeys=True)
    
    def get_server(self, server_name):
        """
        获取云主机
        """
        server = self.connect.compute.find_server(server_name)
        if server:
            return json.dumps(server, indent=2, skipkeys=True)
        else:
            return None
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    class image_manager:

    def __init__(self, connect):
        self.connect = connect
    
    def list_images(self):
        """
        查询所有镜像
        """
        #to json
        items = self.connect.compute.images()
        images_jsons = {}
        for image in items:
            images_jsons[image['name']] = image
        return json.dumps(images_jsons,indent=2)
    
    def get_image(self, image_name:str):
        """
        查询镜像
        """
        #to json
        image = self.connect.compute.find_image(image_name)
    
        return json.dumps(image,indent=2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    class flavor_manager:

    def __init__(self, connect):
        self.connect = connect
    
    def list_flavors(self):
        """
        查询所有云主机类型
        """
        #to json
        items = self.connect.compute.flavors()
        flavors_jsons = {}
        for flavor in items:
            flavors_jsons[flavor['name']] = flavor
        return json.dumps(flavors_jsons,indent=2)
    
    def get_flavor(self, flavor_name:str):
        """
        根据名称获取云主机类.
        """
        #to json
        flavor = self.connect.compute.find_flavor(flavor_name)
        return json.dumps(flavor,indent=2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    class network_manager:

    def __init__(self, connect):
        self.connect = connect
    
    def list_networks(self):
        """
        查询所有网络.
        """
        #to json
        items = self.connect.network.networks()
        items_jsons = {}
        for network in items:
            items_jsons[network['name']] = network
        return json.dumps(items_jsons,indent=2)
    
    def get_network(self, network_name:str):
        """
        跟名称查询网络.
        """
        #to json
        flavor = self.connect.compute.find_network(network_name)
        return json.dumps(flavor,indent=2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    if name == ‘main’:

    # Initialize connection(通过配置文件)
    # controller_ip = "10.24.2.22"
    controller_ip = "controller"
    auth_url = "http://controller:5000/v3/"
    username = "admin"
    password = "000000"
    user_domain_name = 'demo'
    
    conn = create_connection(auth_url, user_domain_name, username, password)
    
    sdk_m = server_manager(conn)
    server = sdk_m.get_server("server001")
    if server:
        result = sdk_m.delete_server("server001")
        print("servers:", result)
    
    #2 创建云主机
    print("creat server--------")
    servers = sdk_m.create_server("server001","cirros001","m1.tiny","net")
    print("servers:", servers)
    
    #6 查询云主机
    server_info = sdk_m.get_server("server001")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    print(server_info)
    创建网络,命令如下:
    [root@controller ~]# source /etc/keystone/admin-openrc.sh
    [root@controller ~]# openstack network create --provider-network-type vlan --provider-physical-network provider --provider-segment 10 --project admin net
    [root@controller ~]# NET=“111.111.10.0/24”
    [root@controller ~]# ID=$(openstack network list --project admin |grep -v ID |grep net |awk -F "| " {‘print $2’})
    [root@controller ~]# openstack subnet create --project admin --subnet-range $NET --dhcp --network $ID ext-subnet
    执行sdk_server_manager.py文件:
    [root@controller ~]# python3 sdk_server_manager.py
    -----------------------------------------执行结果-----------------------------
    #创建云主机的信息为:{“server”: {“security_groups”: [{“name”: “default”}], “OS-DCF:diskConfig”: “MANUAL”, “id”: “f295eb8d-52a0-4e21-ba86-6
    4ba8c3e6359”, “links”: [{“href”: “http://192.168.106.10:8774/v2.1/servers/f295eb8d-52a0-4e21-ba86-64ba8c3e6359”, “rel”: “self”}, {“href”:
    “http://192.168.106.10:8774/servers/f295eb8d-52a0-4e21-ba86-64ba8c3e6359”, “rel”: “bookmark”}], “adminPass”: “oTs5jHFwkbWy”}}

    【题目 5】Python 运维开发:云主机类型管理的命令行工具开发[2 分]
    使用已建好的 OpenStack Python 运维开发环境,在/root 目录下创建 flavor_manager.py 脚本,完成云主机类型的管理,flavor_manager.py 程序支持命令行参数执行。提示说明:Python 标准库argparse 模块,可以提供命令行参数的解析。要求如下:
    (1)程序支持根据命令行参数,创建 1 个多云主机类型。返回 response。位置参数“create”,表示创建; 参数“-n”支持指定 flavor 名称,数据类型为字符串类型; 参数“-m”支持指定内存大小,数据类型为 int,单位 M; 参数“-v”支持指定虚拟 cpu 个数,数据类型为 int; 参数“-d”支持磁盘大小,内存大小类型为 int,单位 G; 参数“-id”支持指定 ID,类型为字符串。 参考运行实例: python3 flavor_manager.py create -n flavor_small -m 1024 -v 1 -d 10 -id 100000
    (2)程序支持查询目前 admin 账号下所有的云主机类型。位置参数“getall”,表示查询所有云主机类型; 查询结果,以 json 格式输出到控制台。参考执行实例如下: python3 flavor_manager.py getall
    (3)支持查询给定具体名称的云主机类型查询。位置参数“get”,表示查询 1 个云主机类型; 参数“-id”支持指定 ID 查询,类型为 string。 控制台以 json 格式输出创建结果。 参考执行实例如下: python3 flavor_manager.py get -id 100000
    (4)支持删除指定的 ID 云主机类型。 位置参数“delete”,表示删除一个云主机类型; 参数“-id”支持指定 ID 查询,返回 response,控制台输出response。参考执行实例如下: python3 flavor_manager.py delete -id 100001
    暂无
    1.执行 flavor_manager.py 脚本,指定 create 和配置参数,成功创建 1 个云主机类型,计 0.5 分; 2.执行 flavor_manager.py 脚本,指定 getall 参数,成功查询所有云主机类型,计 0.5 分;
    3.执行 flavor_manager.py 脚本,指定 get 和配置参数,成功查询具体名称的云主机类型,计 0.5 分;
    4.执行 flavor_manager.py 脚本,指定 delete 和配置参数,成功删除指定 ID 云主机类型,计 0.5 分。

    api_flavor_manager.py:
    import requests,json,time
    import logging

    #-----------logger-----------
    #get logger
    logger = logging.getLogger(name)

    level

    logger.setLevel(logging.DEBUG)

    format

    format = logging.Formatter(‘%(asctime)s %(message)s’)

    to console

    stream_handler = logging.StreamHandler()
    stream_handler .setFormatter(format)
    logger.addHandler(stream_handler )
    #-----------logger-----------

    def get_auth_token(controller_ip, domain, user, password):

    try:
        url = f"http://{controller_ip}:5000/v3/auth/tokens"
        body = {
                    "auth": {
                        "identity": {
                            "methods": [
                                "password"
                            ],
                            "password": {
                                "user": {
                                    "domain": {
                                        "name": domain
                                    },
                                    "name": user,
                                    "password": password
                                }
                            }
                        },
                        "scope": {
                            "project": {
                                "domain": {
                                    "name": domain
                                },
                                "name": user
                            }
                        }
                    }
                }
    
        headers = {
            "Content-Type": "application/json",
        }
        print(body)
        Token = requests.post(url, data=json.dumps(body), headers=headers).headers['X-Subject-Token']
    
        headers = {
            "X-Auth-Token": Token
        }
        logger.debug(f"获取Token值:{str(Token)}")
        return headers
    except Exception as e:
        logger.error(f"获取Token值失败,请检查访问云主机控制节点IP是否正确?输出错误信息如下:{str(e)}")
        exit(0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    class flavor_manager:

    def __init__(self,handers:dict,resUrl:str):
        self.headers=handers
        self.resUrl=resUrl
    #创建flavor类型
    def create_flavor(self,flavor_name:str,ram,vcpus,disk,id):
        self.headers['Content-Type']="application/json"
        body={
            "flavor":{
                "name":flavor_name,
                "ram":ram,
                "vcpus":vcpus,
                "disk":disk,
                "id":id,
            }
        }
        logger.debug(f"创建flavor请求body:{str(body)}")
        status_code = requests.post(self.resUrl, data=json.dumps(body), headers=self.headers).text
        logger.debug(f"返回状态:{str(status_code)}")
        return  status_code
    
    #获取all flavors
    def get_flavors(self):
        result = json.loads(requests.get(self.resUrl,headers=self.headers).text)
        logger.debug(f"返回信息:{str(result)}")
        return result
        # 获取flavor_id
    
    def get_flavor(self, id:str):
        api_url = self.resUrl + "/"+id
        result = json.loads(requests.get(api_url, headers=self.headers).text)
        logger.debug(f"返回信息:{str(result)}")
        return result
    
    def delete_flavor(self, id:str):
        api_url = self.resUrl + "/"+id
        response = requests.delete(api_url, headers=self.headers)
    
        #Normal response codes: 202 without return text
        if response.status_code == 202:
            return {"itemDeletedSuccess": response.status_code}
    
        result = json.loads(response.text)
        logger.debug(f"返回信息:{str(result)}")
        return result
    
    #http://192.168.200.226:8774/v2.1/ get apis version infomation.
    def update_flavor_desc(self, id: str, desc:str):
        # 特别注意:This API is available starting with microversion 2.55.
        self.headers['X-OpenStack-Nova-API-Version'] = "2.55"
    
        self.headers['Content-Type'] = "application/json"
        body = {
            "flavor": {
                "description": desc
            }
        }
    
        api_url = self.resUrl + "/" + id
        response = requests.put(api_url, data=json.dumps(body), headers=self.headers)
        # Normal response codes: 202 without return text
        if response.status_code == 202:
            return {"itemUpdateSuccess": response.status_code}
    
        result = json.loads(response.text)
        logger.debug(f"返回信息:{str(result)}")
        return result
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    if name == ‘main’:
    controller_ip = “controller”
    domain = “demo”
    user = “admin”
    password = “000000”
    headers = get_auth_token(controller_ip, domain, user, password)
    flavor_m = flavor_manager(headers, f"http://{controller_ip}:8774/v2.1/flavors")

    #1 查所有
    flavors = flavor_m.get_flavors()
    
    • 1
    • 2

    print(“查询所有flavors:”, flavors)

    flavor_manager.py:
    #encoding:utf-8
    import argparse

    import api_flavor_manager

    1. openstack allinone (controller ) credentials

    host ip address

    controller_ip = “10.24.2.22”

    controller_ip = “controller”

    domain name

    domain = “demo”

    user name

    user = “admin”

    user password

    password = “000000”
    headers = api_flavor_manager.get_auth_token(controller_ip,domain,user,password)
    print(“headers:”, headers)

    #. get token
    flavor_m = api_flavor_manager.flavor_manager(headers, “http://controller:8774/v2.1/flavors”)

    def define_args(parser):
    “”"
    定义程序支持的args
    :return:
    “”"
    # parser = argparse.ArgumentParser()

    #增加控制命令(postion 位置参数,必须)
    parser.add_argument('command',
                        help='Resource command name',
                        type=str)
    # parser.add_argument('delete',
    #                     help='delete a resource',
    #                     type=str)
    #可选参数(可有可无)
    parser.add_argument('-n', '--name',  # 可选参数,删除的名称
                        help='The Name of the resource',  # 输入-h展示
                        type=str)
    parser.add_argument('-o', '--output',  # 可选参数,删除的名称
                        help='The output file path ',  # 输入-h展示
                        type=str)
    parser.add_argument('-m', '--memory',  # 可选参数,删除的名称
                        help='The Name of the resource',  # 输入-h展示
                        type=str)
    parser.add_argument('-v', '--vcpu',  # 可选参数,删除的名称
                        help='The Name of the resource',  # 输入-h展示
                        type=str)
    parser.add_argument('-d', '--disk',  # 可选参数,删除的名称
                        help='The Name of the resource',  # 输入-h展示
                        type=str)
    parser.add_argument('-id', '--id',  # 可选参数,删除的名称
                        help='The Name of the resource',  # 输入-h展示
                        type=str)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    def parse_args(parser):

    args = parser.parse_args()
    if args.command:
        if args.command == "create":
            print("create some thing")
            create_flavor(args)
        elif args.command == "getall":
            print("getall some thing")
            getall_flavor(args)
        elif args.command == "get":
            print("get some thing")
            get_flavor(args)
        elif args.command == "delete":
            print("delete some thing")
            delete_flavor(args)
        else:
            print("Note support command name!")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    def create_flavor(args):
    print(‘Provided command value is %r.’ % args.command)
    print(‘Provided name value is %r.’ % args.name)
    print(‘Provided memory value is %r.’ % args.memory)
    print(‘Provided vcpu value is %r.’ % args.vcpu)
    print(‘Provided disk value is %r.’ % args.disk)
    print(‘Provided id value is %r.’ % args.id)
    result = flavor_m.create_flavor(args.name,args.memory,args.vcpu,args.disk,args.id)
    print(result)

    def delete_flavor(args):
    print(‘Provided command value is %r.’ % args.command)
    print(‘Provided id value is %r.’ % args.id)
    result = flavor_m.delete_flavor(args.id)
    print(result)

    def getall_flavor(args):
    print(‘Provided command value is %r.’ % args.command)
    result = flavor_m.get_flavors()
    print(result)

    def get_flavor(args):
    print(‘Provided command value is %r.’ % args.command)
    print(‘Provided id value is %r.’ % args.id)
    result = flavor_m.get_flavor(args.id)
    print(result)

    if name == ‘main’:
    parser = argparse.ArgumentParser()
    define_args(parser)
    parse_args(parser)

    -------------------------------------------执行结果--------------------------------
    {‘auth’: {‘identity’: {‘methods’: [‘password’], ‘password’: {‘user’: {‘domain’: {‘name’: ‘demo’}, ‘name’: ‘admin’, ‘password’: ‘000000’}}}, ‘scope’: {‘project’: {‘domain’: {‘name’: ‘demo’}, ‘name’: ‘admin’}}}}

    【题目 6】Python 运维开发:用户管理的命令行工具开发[2 分]
    使用已建好的OpenStack Python 运维开发环境,在/root 目录下创建 user_manager.py 脚本,完成用户管理功能开发,user_manager.py 程序支持命令行带参数执行。 提示说明:Python 标准库argparse 模块,可以提供命令行参数的解析。
    (1)程序支持根据命令行参数,创建 1 个用户。位置参数“create”,表示创建; 参数“-i 或–input”,格式为 json 格式文本用户数据。查询结果,以 json 格式输出到控制台。 参考执行实例如下: python3 user_manager.py create --input '{ “name”: “user01”, “password”: “000000”, “description”: “description” } ’
    (2)支持查询给定具体名称的用户查询。 位置参数“get”,表示查询 1 个用户; 参数“-n 或 --name”支持指定名称查询,类型为 string。 参数“-o 或 output”支持查询该用户信息输出到文件,格式为json 格式。参考执行实例如下: python3 user_manager.py get --name user01-o user.json
    (3)程序支持查询目前 admin 账号下所有的用户。位置参数“getall”,表示查询所有用户; 参数“-o 或–output”支持输出到文件,格式为 yaml 格式。参考执行实例如下: python3 user_manager.py getall -o openstack_all_user.yaml
    (4)支持删除指定的名称的用户。 位置参数“delete”,表示删除一个用户;返回 response,通过控制台输出。参数“-n 或–name”支持指定名称查询,类型为 string。 参考执行实例如下: python3 user_manager.py delete -name user01
    暂无
    1.执行user_manager.py 脚本,指定 create 和配置参数,成功创建 1 个用户,计 0.5 分;
    2.执行user_manager.py 脚本,指定 get 和配置参数,成功查询具体名称的用户,计 0.5 分;
    3.执行user_manager.py 脚本,指定 getall 和配置参数,成功查询 admin 账号下的所有用户, 计 0.5 分;
    4.执行user_manager.py 脚本,指定 delete 和配置参数,成功删除指定名称的用户,计 0.5 分
    api_user_manager.py:

    encoding:utf-8

    import requests, json, time
    import logging

    -----------logger-----------

    get logger

    logger = logging.getLogger(name)

    level

    logger.setLevel(logging.DEBUG)

    format

    format = logging.Formatter(‘%(asctime)s %(message)s’)

    to console

    stream_handler = logging.StreamHandler()
    stream_handler.setFormatter(format)
    logger.addHandler(stream_handler)

    -----------logger-----------

    def get_auth_token(controller_ip, domain, user, password):
    ‘’’
    :param controller_ip: openstack master ip address
    :param domain: current user’s domain
    :param user: user name
    :param password: user password
    :return: keystoen auth Token for current user.
    ‘’’

    try:
        url = "http://controller:5000/v3/auth/tokens"
        body = {
            "auth": {
                "identity": {
                    "methods": [
                        "password"
                    ],
                    "password": {
                        "user": {
                            "domain": {
                                "name": domain
                            },
                            "name": user,
                            "password": password
                        }
                    }
                },
                "scope": {
                    "project": {
                        "domain": {
                            "name": domain
                        },
                        "name": user
                    }
                }
            }
        }
    
        headers = {
            "Content-Type": "application/json",
        }
        print(body)
        Token = requests.post(url, data=json.dumps(body), headers=headers).headers['X-Subject-Token']
    
        headers = {
            "X-Auth-Token": Token
        }
        logger.debug(f"获取Token值:{str(Token)}")
        return headers
    except Exception as e:
        logger.error(f"获取Token值失败,请检查访问云主机控制节点IP是否正确?输出错误信息如下:{str(e)}")
        exit(0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    用户管理

    https://docs.openstack.org/api-ref/identity/v3/index.html#users

    class user_manager:
    def init(self, handers: dict, resUrl: str):
    self.headers = handers
    self.resUrl = resUrl

    #      POST  /v3/users  Create user
    def create_users(self, user_name, password: str, desc: str):
        """
        create a user with name and password and description.
        """
    
        body = {
            "user": {
                "name": user_name,
                "password": password,
                "description": desc,
            }
        }
        status_code = requests.post(self.resUrl, data=json.dumps(body), headers=self.headers).text
        logger.debug(f"返回状态:{str(status_code)}")
        return status_code
    
    # /v3/users    # List all users
    def get_users(self):
        """
        get user
        """
        status_code = requests.get(self.resUrl, headers=self.headers).text
        logger.debug(f"返回状态:{str(status_code)}")
        return status_code
    
    def get_user_id(self, user_name):
        """
        get user id by name.
        """
        result = json.loads(requests.get(self.resUrl, headers=self.headers).text)
        user_name = user_name
        for item in result['users']:
            if item['name'] == user_name:
                return item['id']
        return "NONE"
    
    def get_user(self, id: str):
        """
        get a flavor by id.
        """
        api_url = self.resUrl + "/" + id
        result = json.loads(requests.get(api_url, headers=self.headers).text)
        logger.debug(f"返回信息:{str(result)}")
        return result
    
    def delete_user(self, name: str):
        """
         delete a user by id.
         """
        id = self.get_user_id(name)
        api_url = self.resUrl + "/" + id
        response = requests.delete(api_url, headers=self.headers)
    
        # 204 - No Content  The server has fulfilled the request.
        if response.status_code == 204:
            return {"User itemDeletedSuccess": response.status_code}
    
        result = json.loads(response.text)
        logger.debug(f"返回信息:{str(result)}")
        return result
    
        # http://192.168.200.226:8774/v2.1/ get apis version infomation.
    
    def update_User_password(self, id: str, original_password: str, new_password: str):
        """
        update a flavor desc by id.
    
        """
        self.headers['Content-Type'] = "application/json"
        body = {
            "user": {
                "password": new_password,
                "original_password": original_password
            }
        }
    
        api_url = self.resUrl + "/" + id + "/password"
        response = requests.post(api_url, data=json.dumps(body), headers=self.headers)
        # Normal response codes: 204 without return text
        if response.status_code == 204:
            return {"item Update Password Success": response.status_code}
    
        result = json.loads(response.text)
        logger.debug(f"返回信息:{str(result)}")
        return result
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    if name == ‘main’:
    # 1. openstack allinone (controller ) credentials
    # host ip address
    # controller_ip = “10.24.2.22”
    controller_ip = “controller”
    # controller_ip = “10.24.2.22”
    # domain name
    domain = “demo”
    # user name
    user = “admin”
    # user password
    password = “000000”
    headers = get_auth_token(controller_ip, domain, user, password)
    print(“headers:”, headers)
    # get all user
    user_m = user_manager(headers, “http://controller:5000/v3/users”)
    # 1 查询所有
    users = user_m.get_users()
    print(“查询所有users:”, users)
    user_manager.py:
    #encoding:utf-8
    import argparse

    import api_user_manager
    import json
    import csv
    import yaml

    1. openstack allinone (controller ) credentials

    host ip address

    controller_ip = “10.24.2.22”

    controller_ip = “controller”

    controller_ip = “10.24.2.22”

    domain name

    domain = “demo”

    user name

    user = “admin”

    user password

    password = “000000”
    headers = api_user_manager.get_auth_token(controller_ip, domain, user, password)
    print(“headers:”, headers)

    get all user

    user_m = api_user_manager.user_manager(headers, “http://controller:5000/v3/users”)
    print(“-----------begin-----------------”)
    def define_args(parser):
    “”"
    定义程序支持的args
    :return:
    “”"
    # parser = argparse.ArgumentParser()

    #增加控制命令(postion 位置参数,必须)
    parser.add_argument('command',
                        help='Resource command name',
                        type=str)
    # parser.add_argument('delete',
    #                     help='delete a resource',
    #                     type=str)
    #可选参数(可有可无)
    parser.add_argument('-n', '--name',  # 可选参数,删除的名称
                        help='The Name of the resource',  # 输入-h展示
                        type=str)
    parser.add_argument('-i', '--input',  # 可选参数,删除的名称
                        help='The input json format text ',  # 输入-h展示
                        type=str)
    parser.add_argument('-o', '--output',  # 可选参数,删除的名称
                        help='The output file path ',  # 输入-h展示
                        type=str)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    def parse_args(parser):
    args = parser.parse_args()
    if args.command:
    if args.command == “create”:
    print(“create some thing”)
    create_user(args)
    elif args.command == “getall”:
    print(“getall some thing”)
    getall_users(args)
    elif args.command == “get”:
    print(“get some thing”)
    get_user(args)
    elif args.command == “delete”:
    print(“delete some thing”)
    delete_user(args)
    else:
    print(“Note support command name!”)

    def create_user(args):
    print(‘Provided command value is %r.’ % args.command)
    print(‘Provided input value is %r.’ % args.input)
    print(‘Provided output value is %r.’ % args.output)
    output_file = args.output
    # user_name, password: str, desc: str):
    user_dict = json.loads(args.input)
    result = user_m.create_users(user_dict[“name”],user_dict[“password”],user_dict[“description”])

    # 写出json文件
    print("--------write to json---------:", result)
    print(result)
    
    • 1
    • 2
    • 3

    def delete_user(args):
    print(‘Provided command value is %r.’ % args.command)
    print(‘Provided input value is %r.’ % args.input)
    print(‘Provided output value is %r.’ % args.output)
    result = user_m.delete_user(args.name)
    print(result)

    def getall_users(args):
    print(‘Provided command value is %r.’ % args.command)
    print(‘Provided input value is %r.’ % args.input)
    print(‘Provided output value is %r.’ % args.output)
    print(type(args.input))
    result = user_m.get_users()
    output_file = args.output
    # 写出json文件
    print(“--------result---------”)
    print(result)
    configuration = json.loads(result)
    # 写出yaml (dict)
    with open(output_file, ‘w’) as yaml_file:
    yaml.dump(configuration, yaml_file)

    print(result)
    
    • 1

    def get_user(args):
    print(‘Provided command value is %r.’ % args.command)
    print(‘Provided input value is %r.’ % args.input)
    print(‘Provided output value is %r.’ % args.output)
    id = user_m.get_user_id(args.name)
    result = user_m.get_user(id)
    output_file = args.output
    # 写出json文件
    with open(output_file, ‘w’) as jsonfile:
    json.dump(result, jsonfile, indent=4)
    print(result)

    if name == ‘main’:
    import sys
    print(sys.argv)
    parser = argparse.ArgumentParser()
    define_args(parser)
    parse_args(parser)

    ----------------------------------------------执行结果------------------------------------
    [root@controller python]# python3 user_manager.py create --input '{ “name”: “user01”, “password”: “000000”, “description”: “description” } ’
    {‘auth’: {‘identity’: {‘methods’: [‘password’], ‘password’: {‘user’: {‘domain’: {‘name’: ‘demo’}, ‘name’: ‘admin’, ‘password’: ‘000000’}}}, ‘scope’: {‘project’: {‘domain’: {‘name’: ‘demo’}, ‘name’: ‘admin’}}}}
    2022-10-11 17:39:22,843 获取Token值:gAAAAABjRTnKtdV9oDS_VfNDp8qtRC_sEElsQwJGqJTST8LHtqJUahTJtf8MVDa2Nplrjwo6_18D_Hm85j99D9G1TMq7jKEPqAynBx5nGkTXggQWJ-WJdPxad_e3qsrwfeL3JOqDK3RSHEkhZ1k1EQKWl3nxgMBhycHDs_3-CA4Cyfcmi9S15pQ
    headers: {‘X-Auth-Token’: ‘gAAAAABjRTnKtdV9oDS_VfNDp8qtRC_sEElsQwJGqJTST8LHtqJUahTJtf8MVDa2Nplrjwo6_18D_Hm85j99D9G1TMq7jKEPqAynBx5nGkTXggQWJ-WJdPxad_e3qsrwfeL3JOqDK3RSHEkhZ1k1EQKWl3nxgMBhycHDs_3-CA4Cyfcmi9S15pQ’}
    -----------begin-----------------
    [‘user_manager.py’, ‘create’, ‘–input’, '{ “name”: “user01”, “password”: “000000”, “description”: “description” } ']
    create some thing
    Provided command value is ‘create’.
    Provided input value is '{ “name”: “user01”, “password”: “000000”, “description”: “description” } '.
    Provided output value is None.
    2022-10-11 17:39:23,137 返回状态:{“user”: {“description”: “description”, “name”: “user01”, “domain_id”: “default”, “enabled”: true, “links”: {“self”: “http://controller:5000/v3/users/01eebcdbcbf24bc4a5435f1dcd0949a7”}, “options”: {}, “id”: “01eebcdbcbf24bc4a5435f1dcd0949a7”, “password_expires_at”: null}}

    --------write to json---------: {“user”: {“description”: “description”, “name”: “user01”, “domain_id”: “default”, “enabled”: true, “links”: {“self”: “http://controller:5000/v3/users/01eebcdbcbf24bc4a5435f1dcd0949a7”}, “options”: {}, “id”: “01eebcdbcbf24bc4a5435f1dcd0949a7”, “password_expires_at”: null}}

    {“user”: {“description”: “description”, “name”: “user01”, “domain_id”: “default”, “enabled”: true, “links”: {“self”: “http://controller:5000/v3/users/01eebcdbcbf24bc4a5435f1dcd0949a7”}, “options”: {}, “id”: “01eebcdbcbf24bc4a5435f1dcd0949a7”, “password_expires_at”: null}}

  • 相关阅读:
    Flutter的Don‘t use ‘BuildContext‘s across async gaps警告解决方法
    【我的OpenGL学习进阶之旅】向量点乘和叉乘的几何意义
    【快应用】deeplink 3种跳转格式现象总结
    Servlet生命周期
    adb server version (19045) doesn‘t match this client (41); killing.的解决办法
    A_A01_002 MDK5(STM32)软件安装
    基于Flume+Kafka+Hbase+Flink+FineBI的实时综合案例(三)离线分析
    java try 自动关闭流
    git:一些撤销操作
    Spark系列之Spark启动与基础使用
  • 原文地址:https://blog.csdn.net/weixin_43384009/article/details/134560440