saltstack

1、salt入門php

一、saltstack的3種運行方式:html

    localjava

    Master/Minionnode

    Salt SSHpython

二、salttstack的3大功能:mysql

    遠程執行react

    配置管理linux

    雲管理ios

三、saltstack的基礎配置和通訊原理nginx

編輯minion的配置文件:/etc/salt/minion,修改master

master: 192.168.74.20

schedule:
  highstate:
    function: state.highstate
    seconds: 30

客戶端每隔30s到server端同步一次數據; 

注意:關於主機名的解析

[root@linux-node1 master]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.74.20  linux-node1.example.com  linux-node1
192.168.74.21  linux-node2.example.com  linux-node2 

salt安裝完畢以後,首先須要在minion端坐初始化操做;須要指定server的地址以及id(能夠不指定,默認爲minion的hostname);

 

[root@linux-node1 minion]# pwd
/etc/salt/pki/minion
[root@linux-node1 minion]# ls    #minion.pub是minion的公鑰,會拷貝到master端,須要master贊成以後才能夠實現通訊
minion.pem  minion.pub

 

[root@linux-node1 master]# pwd
/etc/salt/pki/master
[root@linux-node1 master]# tree 
.
├── master.pem
├── master.pub
├── minions
├── minions_autosign
├── minions_denied
├── minions_pre
│?? ├── linux-node1.example.com
│?? └── linux-node2.example.com
└── minions_rejected

5 directories, 4 files

 

[root@linux-node1 master]# salt-key 
Accepted Keys:
Denied Keys:
Unaccepted Keys:
linux-node1.example.com
linux-node2.example.com
Rejected Keys:

  

接下來須要贊成公鑰:

[root@linux-node1 master]# salt-key -a linux*     #支持通配符
The following keys are going to be accepted:
Unaccepted Keys:
linux-node1.example.com
linux-node2.example.com
Proceed? [n/Y] y
Key for minion linux-node1.example.com accepted.
Key for minion linux-node2.example.com accepted.
[root@linux-node1 master]# tree
.
├── master.pem
├── master.pub
├── minions
│?? ├── linux-node1.example.com      #這個是minion的公鑰
│?? └── linux-node2.example.com
├── minions_autosign
├── minions_denied
├── minions_pre
└── minions_rejected
[root@linux-node1 minions]# ls
linux-node1.example.com  linux-node2.example.com
[root@linux-node1 minions]# file linux-node1.example.com 
linux-node1.example.com: ASCII text
[root@linux-node1 minions]# cat linux-node1.example.com 
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvrmgglfYeMpUay3Tx8GG
Pre5gzFe//2jwT8S6tZBA8nyzhOa0ONJSF5aGojdDCb6J6pKZvZOnj3cPdee4oeX
Z4E0bpH7aW2ZpR4yTFaXbJ8xj3TCspVF2of7HMr+eA/CKDojg2NhRGvsUkH+LRry
fCBsHTj/SSUp/k7jH+Yf2gYhT7cRSbKbiEC2V4EsQfIxX4ER7kYgjMUZPdvkRgTY
kgds3Ol4eeL9ZjZguRQen1qI7DQWU9JlhEDerlsnoTreH8XPHBDJ9JvC0BuK4YQm
oyIJUkDY4JFWcCjkecRgVGh9AYHkmofgBaEmf2TKgLrK5lvOK5miViKjc+hVN4zP
2QIDAQAB
-----END PUBLIC KEY-----

 

在minion端,也將master的公鑰拿過來了

[root@linux-node2 ~]# cd /etc/salt/pki/
[root@linux-node2 pki]# ls
minion
[root@linux-node2 pki]# cd minion/
[root@linux-node2 minion]# ls
minion_master.pub  minion.pem  minion.pub
[root@linux-node2 minion]# cat minion_master.pub 
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0+xX8cUQXZ9eRVMS6J+P
BxgEbB9o2nr7SGhu781RJoYQ2IOT64CsICrx0W/ACbCEqjaRV809VPkGndPfGgu1
IVZ+OP6eVub12ZpOXUpnJRKaVC2v2li6h+OqgCuESitlNpPGtYnbBqcROKv/mtYa
FwLZvIa5aVHUj3UReKky8WAtGCuHHx3TdQQCVJfgkmUG97Y+62wPBbBeop4L0c5d
iO8rICFlty0uMt/0FRmnKj+vN/a/B3DabHWUbf4GUDVevFJCZFZyF24Yhgx4jlu7
+QBRGLjQkKsv+SIR14diIy1Wex3i8f67KtQnlQLlWb2nJmSXiXShAWhccZagXGPF
xwIDAQAB
-----END PUBLIC KEY----- 

配置完成以後,還不能實現master和minion的通訊,須要master和minion配置相似於openssh的信任關係,要藉助salt-key命令完成,參數-a 能夠添加特定的信任主機,-A添加全部的主機,

-D刪除全部,-d刪除特定的信任主機;

salt-key命令執行完畢以後,minion會將本地的私鑰拷貝到master上的/etc/salt/pki/master/minions目錄下,同時master會講本身的公鑰拷貝到minion端;

 

2、配置管理

修改/etc/salt/master文件,建立file_roots目錄;

file_roots:
  base:
    - /srv/salt

建立/srv/salt目錄,而後重啓salt-master;

  

下面安裝apache,並啓動

/srv/salt目錄下:
[root@22-57 salt]# cat apache.sls apache-install: #名稱 pkg.installed: #pkg是模塊的名稱,installed是模塊中的方法 - names: #names是參數 - httpd - httpd-devel apache-service: service.running: - name: httpd - enable: True - reload: True

 

使用:set list查看不顯示的字符:

apache-install:$
  pkg.installed:$
    - names:$
      - httpd$
      - httpd-devel$
$
$
apache-service:$
  service.running:$
    - name: httpd$
    - enable: True$
    - reload: True$

 

 

而後執行salt '*' state.sls apache就會執行上述sls文件: state表示模塊,sls爲模塊中的方法名

 

也能夠在top.sls文件中制定上述sls文件,看下面:

[root@22-57 salt]# cat top.sls
base:
  '*':
    - apache

而後執行salt '*' state.highstate就執行apache.sls啦!

 

3、數據系統 Grains

grains中存放着minion啓動時獲取的系統信息,只有在minion啓動的時候纔會收集,而後就不變了,只有重啓的時候纔會從新收集;

grains有三個做用:採集minion數據、在遠程執行的時候匹配minion、在top文件執行的時候匹配minion;

 

一、收集信息數據

[root@linux-node1 ~]# salt 'linux-node1*' grains.ls    #將grains的全部key列出來
linux-node1.example.com:
    - SSDs
    - biosreleasedate
    - biosversion
    - cpu_flags
    - cpu_model
    - cpuarch
    - domain
    - fqdn
    - fqdn_ip4
    - fqdn_ip6
    - gpus
    - host
    - hwaddr_interfaces
    - id
    - init
    - ip4_interfaces
    - ip6_interfaces
    - ip_interfaces
    - ipv4
    - ipv6
    - kernel
    - kernelrelease
    - locale_info
    - localhost
    - lsb_distrib_id
    - machine_id
    - manufacturer
    - master
    - mdadm
    - mem_total
    - nodename
    - num_cpus
    - num_gpus
    - os
    - os_family
    - osarch
    - oscodename
    - osfinger
    - osfullname
    - osmajorrelease
    - osrelease
    - osrelease_info
    - path
    - productname
    - ps
    - pythonexecutable
    - pythonpath
    - pythonversion
    - saltpath
    - saltversion
    - saltversioninfo
    - selinux
    - serialnumber
    - server_id
    - shell
    - systemd
    - virtual
    - zmqversion

 

root@linux-node1 ~]# salt 'linux-node1*' grains.items    #將grains的全部內容顯示出來
linux-node1.example.com:
    ----------
    SSDs:
    biosreleasedate:
        06/02/2011
    biosversion:
        6.00
    cpu_flags:
        - fpu
        - vme
        - de
        - pse
        - tsc
        - msr
        - pae
        - mce
        - cx8
        - apic
        - sep
        - mtrr
        - pge
        - mca
        - cmov
        - pat
        - pse36
        - clflush
        - dts
        - acpi
        - mmx
        - fxsr
        - sse
        - sse2
        - ss
        - syscall
        - nx
        - rdtscp
        - lm
        - constant_tsc
        - arch_perfmon
        - pebs
        - bts
        - nopl
        - xtopology
        - tsc_reliable
        - nonstop_tsc
        - aperfmperf
        - eagerfpu
        - pni
        - pclmulqdq
        - ssse3
        - fma
        - cx16
        - sse4_1
        - sse4_2
        - movbe
        - popcnt
        - aes
        - xsave
        - avx
        - hypervisor
        - lahf_lm
        - ida
        - arat
        - epb
        - pln
        - pts
        - dtherm
        - hwp
        - hwp_noitfy
        - hwp_act_window
        - hwp_epp
        - xsaveopt
        - xsavec
        - xgetbv1
        - xsaves
    cpu_model:
        Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
    cpuarch:
        x86_64
    domain:
        example.com
    fqdn:
        linux-node1.example.com
    fqdn_ip4:
        - 192.168.74.20
    fqdn_ip6:
    gpus:
        |_
          ----------
          model:
              SVGA II Adapter
          vendor:
              unknown
    host:
        linux-node1
    hwaddr_interfaces:
        ----------
        ens33:
            00:0c:29:89:6a:8f
        lo:
            00:00:00:00:00:00
        virbr0:
            00:00:00:00:00:00
        virbr0-nic:
            52:54:00:95:4d:38
    id:
        linux-node1.example.com
    init:
        systemd
    ip4_interfaces:
        ----------
        ens33:
            - 192.168.74.20
        lo:
            - 127.0.0.1
        virbr0:
            - 192.168.122.1
        virbr0-nic:
    ip6_interfaces:
        ----------
        ens33:
            - fe80::20c:29ff:fe89:6a8f
        lo:
            - ::1
        virbr0:
        virbr0-nic:
    ip_interfaces:
        ----------
        ens33:
            - 192.168.74.20
            - fe80::20c:29ff:fe89:6a8f
        lo:
            - 127.0.0.1
            - ::1
        virbr0:
            - 192.168.122.1
        virbr0-nic:
    ipv4:
        - 127.0.0.1
        - 192.168.122.1
        - 192.168.74.20
    ipv6:
        - ::1
        - fe80::20c:29ff:fe89:6a8f
    kernel:
        Linux
    kernelrelease:
        3.10.0-327.el7.x86_64
    locale_info:
        ----------
        defaultencoding:
            UTF-8
        defaultlanguage:
            en_US
        detectedencoding:
            UTF-8
    localhost:
        linux-node1
    lsb_distrib_id:
        CentOS Linux
    machine_id:
        ae71ba43e74c41a7b705e17fff4a03fb
    manufacturer:
        VMware, Inc.
    master:
        192.168.74.20
    mdadm:
    mem_total:
        1836
    nodename:
        linux-node1
    num_cpus:
        1
    num_gpus:
        1
    os:
        CentOS
    os_family:
        RedHat
    osarch:
        x86_64
    oscodename:
        Core
    osfinger:
        CentOS Linux-7
    osfullname:
        CentOS Linux
    osmajorrelease:
        7
    osrelease:
        7.2.1511
    osrelease_info:
        - 7
        - 2
        - 1511
    path:
        /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
    productname:
        VMware Virtual Platform
    ps:
        ps -efH
    pythonexecutable:
        /usr/bin/python
    pythonpath:
        - /usr/bin
        - /usr/lib/python2.7/site-packages/aliyun_python_sdk_rds-2.0.0-py2.7.egg
        - /usr/lib/python2.7/site-packages/aliyun_python_sdk_core-2.0.36-py2.7.egg
        - /usr/lib64/python27.zip
        - /usr/lib64/python2.7
        - /usr/lib64/python2.7/plat-linux2
        - /usr/lib64/python2.7/lib-tk
        - /usr/lib64/python2.7/lib-old
        - /usr/lib64/python2.7/lib-dynload
        - /usr/lib64/python2.7/site-packages
        - /usr/lib64/python2.7/site-packages/Twisted-16.6.0-py2.7-linux-x86_64.egg
        - /usr/lib64/python2.7/site-packages/constantly-15.1.0-py2.7.egg
        - /usr/lib64/python2.7/site-packages/zope.interface-4.3.3-py2.7-linux-x86_64.egg
        - /root/Twisted-16.6.0/.eggs/incremental-16.10.1-py2.7.egg
        - /usr/lib64/python2.7/site-packages/gtk-2.0
        - /usr/lib/python2.7/site-packages
    pythonversion:
        - 2
        - 7
        - 5
        - final
        - 0
    saltpath:
        /usr/lib/python2.7/site-packages/salt
    saltversion:
        2015.5.10
    saltversioninfo:
        - 2015
        - 5
        - 10
        - 0
    selinux:
        ----------
        enabled:
            True
        enforced:
            Enforcing
    serialnumber:
        VMware-56 4d bd b4 2a f9 c1 e4-53 27 19 67 df 89 6a 8f
    server_id:                       #物理機的快速服務代碼
        1981947194
    shell:
        /bin/sh
    systemd:
        ----------
        features:
            +PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ -LZ4 -SECCOMP +BLKID +ELFUTILS +KMOD +IDN
        version:
            219
    virtual:
        VMware
    zmqversion:
        3.2.5

 

[root@linux-node1 ~]# salt 'linux-node1*' grains.item fqdn     #獲取其中單獨一項
linux-node1.example.com:
    ----------
    fqdn:
        linux-node1.example.com
[root@linux-node1 ~]# salt 'linux-node1*' grains.get fqdn 
linux-node1.example.com:
    linux-node1.example.com

[root@linux-node1 ~]# salt 'linux-node1*' grains.get ip_interfaces:ens33
linux-node1.example.com:
    - 192.168.74.20
    - fe80::20c:29ff:fe89:6a8f

 

系統默認的grains數據都包含在salt項目下的: /salt/grains/core.py下面,有興趣能夠看看;

 

二、匹配minion

[root@linux-node1 ~]# salt 'linux-node1*' grains.get os
linux-node1.example.com:
    CentOS

[root@linux-node1 ~]# salt -G os:Centos cmd.run 'w'    #-G表示匹配grains
linux-node1.example.com:
     21:17:27 up  5:36,  1 user,  load average: 0.16, 0.05, 0.06
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    root     pts/0    192.168.74.1     20:30    7.00s  0.48s  0.36s /usr/bin/python /usr/bin/salt -G os:Centos cmd.run w
linux-node2.example.com:
     21:17:26 up  5:36,  2 users,  load average: 0.02, 0.05, 0.05
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    root     pts/0    192.168.74.1     13:17    3:48m  0.38s  0.38s -bash
    root     pts/1    192.168.74.1     20:30   46:14   0.05s  0.05s -bash

 

三、自定義grains

若是自帶的grains不知足需求,能夠自定義grains;自定義grains有兩種方式實現:在server端寫好後推到client,或者直接在client端編輯;

在server端定義以下:

#定義file_roots的base目錄,並新建grains目錄
 file_roots:
   base:
     - /srv/salt/base
[root@linux-node1 _grains]# pwd
/srv/salt/base/_grains

#定義grains,獲取客戶端的ulimit -n 的值
[root@linux-node1 _grains]# cat file.py   
import os
def file():
    grains={}
    file = os.popen('ulimit -n').read()
    grains['file']=file
    return grains

#將grains推送到須要的客戶端
salt '*' saltutil.sync_all

#獲取grains的值
[root@linux-node1 _grains]# salt '*' grains.get file
linux-node2-computer:
    1024
linux-node1.oldboyedu.com:
    8192

 

在客戶端編輯以下:

將minion的配置文件/etc/salt/minion以下的註釋去掉

grains:
  roles:
    - webserver
    - memcache

而後重啓minion,測試:

[root@linux-node1 ~]# salt -G 'roles:memcache'  cmd.run 'echo hehe'        
linux-node1.example.com:
    hehe

  

固然了,自定義grains通常不寫在配置文件中,放在文件/etc/salt/grains 中:

[root@linux-node1 ~]# cat /etc/salt/grains 
web: nginx    #這裏的web,即key不能喝系統已經有的grains的key有重複,不然會出問題啦

測試一下:

[root@linux-node1 ~]# systemctl restart salt-minion    #每次自定義grains,都必須重啓minion,不然不生效啊
[root@linux-node1 ~]# salt '*' grains.item roles
linux-node1.example.com:
    ----------
    roles:
        - webserver
        - memcache
linux-node2.example.com:
    ----------
    roles:
[root@linux-node1 ~]# salt '*' grains.item web
linux-node2.example.com:
    ----------
    web:
linux-node1.example.com:
    ----------
    web:
        nginx
[root@linux-node1 ~]# 
[root@linux-node1 ~]# salt -G 'web:nginx' cmd.run 'w'
linux-node1.example.com:
     21:42:33 up  6:02,  1 user,  load average: 0.25, 0.19, 0.11
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    root     pts/0    192.168.74.1     20:30    1.00s  0.76s  0.34s /usr/bin/python /usr/bin/salt -G web:nginx cmd.run w

 

另外,grains 在minion端還能夠寫在default_include: minion.d/*.conf 下面

[root@linux-node2 minion.d]# cat charles.conf 
grains:
  FD: 2
  charles: 5
  cpis:
    - a
    - b
  charles.net:
    ++++++++++++++++++++++++++++

    +++++++++++++++++++=========
 
    ***************************


#測試結果
[root@linux-node1 _grains]# salt 'linux-node2-computer' grains.items
linux-node2-computer:
    ----------
    FD:
        2
    SSDs:
    biosreleasedate:
        06/02/2011
    biosversion:
        6.00
    charles:
        5
    charles.net:
        ++++++++++++++++++++++++++++
        +++++++++++++++++++=========
        ***************************
    cpis:
        - a
        - b

 

三、在top file中使用grains

匹配web: nginx的機器,執行apache.sls

[root@linux-node1 salt]# pwd
/srv/salt
[root@linux-node1 salt]# cat top.sls 
base:
  'web:nginx':
    - match: grain
    - apache

 

指定結果以下:

salt '*' state.highstate 

[root@linux-node1 salt]# salt '*' state.highstate
linux-node2.example.com:
----------
          ID: states
    Function: no.None
      Result: False
     Comment: No Top file or external nodes data matches found.
     Started: 
    Duration: 
     Changes:   

Summary
------------
Succeeded: 0
Failed:    1
------------
Total states run:     1
linux-node1.example.com:
----------
          ID: apache-install
    Function: pkg.installed
        Name: httpd
      Result: True
     Comment: Package httpd is already installed.
     Started: 21:51:23.629420
    Duration: 1910.371 ms
     Changes:   
----------
          ID: apache-install
    Function: pkg.installed
        Name: httpd-devel
      Result: True
     Comment: Package httpd-devel is already installed.
     Started: 21:51:25.539976
    Duration: 0.369 ms
     Changes:   
----------
          ID: apache-service
    Function: service.running
        Name: httpd
      Result: True
     Comment: Service httpd is already enabled, and is in the desired state
     Started: 21:51:25.540793
    Duration: 647.157 ms
     Changes:   

Summary
------------
Succeeded: 3
Failed:    0
------------
Total states run:     3
ERROR: Minions returned with non-zero exit code
[root@linux-node1 salt]# 

 

4、數據系統 pillar(給minion指定其想要的數據)

Grains:是部署在minion端的,支持靜態數據,minion啓動的時候採集,也可使用saltutil.sync_grains進行刷新;應用好比:存儲minion基本數據,好比用於匹配minion,自身數據能夠用來作資產管理等;

Pillar:部署在master端,支持動態數據,在master端定義,指定給對應的minion,可使用saltutil.reflesh_pillar刷新;應用舉例:存儲master指定的數據,只有指定的minion能夠看到,用於敏感數據保存;

 

一、pillar默認是沒有的內容的,須要在/etc/salt/master下面設置,將參數pillar_opts: True,而後重啓mater就有啦

東西不少,然而並無什麼用~~,那就關掉吧

linux-node2.example.com:
    ----------
    master:
        ----------
        __role:
            master
        auth_mode:
            1
        auto_accept:
            False
        cache_sreqs:
            True
        cachedir:
            /var/cache/salt/master
        cli_summary:
            False
        client_acl:
            ----------
        client_acl_blacklist:
            ----------
        cluster_masters:
        cluster_mode:
            paranoid
        con_cache:
            False
        conf_file:
            /etc/salt/master
        config_dir:
            /etc/salt
        cython_enable:
            False
        daemon:
            False
        default_include:
            master.d/*.conf
        enable_gpu_grains:
            False
        enforce_mine_cache:
            False
        enumerate_proxy_minions:
            False
        environment:
            None
        event_return:
        event_return_blacklist:
        event_return_queue:
            0
        event_return_whitelist:
        ext_job_cache:
        ext_pillar:
        extension_modules:
            /var/cache/salt/extmods
        external_auth:
            ----------
        failhard:
            False
        file_buffer_size:
            1048576
        file_client:
            local
        file_ignore_glob:
            None
        file_ignore_regex:
            None
        file_recv:
            False
        file_recv_max_size:
            100
        file_roots:
            ----------
            base:
                - /srv/salt
        fileserver_backend:
            - roots
        fileserver_followsymlinks:
            True
        fileserver_ignoresymlinks:
            False
        fileserver_limit_traversal:
            False
        gather_job_timeout:
            10
        gitfs_base:
            master
        gitfs_env_blacklist:
        gitfs_env_whitelist:
        gitfs_insecure_auth:
            False
        gitfs_mountpoint:
        gitfs_passphrase:
        gitfs_password:
        gitfs_privkey:
        gitfs_pubkey:
        gitfs_remotes:
        gitfs_root:
        gitfs_user:
        hash_type:
            md5
        hgfs_base:
            default
        hgfs_branch_method:
            branches
        hgfs_env_blacklist:
        hgfs_env_whitelist:
        hgfs_mountpoint:
        hgfs_remotes:
        hgfs_root:
        id:
            linux-node2.example.com
        interface:
            0.0.0.0
        ioflo_console_logdir:
        ioflo_period:
            0.01
        ioflo_realtime:
            True
        ioflo_verbose:
            0
        ipv6:
            False
        jinja_lstrip_blocks:
            False
        jinja_trim_blocks:
            False
        job_cache:
            True
        keep_jobs:
            24
        key_logfile:
            /var/log/salt/key
        keysize:
            2048
        log_datefmt:
            %H:%M:%S
        log_datefmt_logfile:
            %Y-%m-%d %H:%M:%S
        log_file:
            /var/log/salt/master
        log_fmt_console:
            [%(levelname)-8s] %(message)s
        log_fmt_logfile:
            %(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s][%(process)d] %(message)s
        log_granular_levels:
            ----------
        log_level:
            warning
        loop_interval:
            60
        maintenance_floscript:
            /usr/lib/python2.7/site-packages/salt/daemons/flo/maint.flo
        master_floscript:
            /usr/lib/python2.7/site-packages/salt/daemons/flo/master.flo
        master_job_cache:
            local_cache
        master_pubkey_signature:
            master_pubkey_signature
        master_roots:
            ----------
            base:
                - /srv/salt-master
        master_sign_key_name:
            master_sign
        master_sign_pubkey:
            False
        master_tops:
            ----------
        master_use_pubkey_signature:
            False
        max_event_size:
            1048576
        max_minions:
            0
        max_open_files:
            100000
        minion_data_cache:
            True
        minionfs_blacklist:
        minionfs_env:
            base
        minionfs_mountpoint:
        minionfs_whitelist:
        nodegroups:
            ----------
        open_mode:
            False
        order_masters:
            False
        outputter_dirs:
        peer:
            ----------
        permissive_pki_access:
            False
        pidfile:
            /var/run/salt-master.pid
        pillar_opts:
            True
        pillar_roots:
            ----------
            base:
                - /srv/pillar
        pillar_safe_render_error:
            True
        pillar_source_merging_strategy:
            smart
        pillar_version:
            2
        pillarenv:
            None
        ping_on_rotate:
            False
        pki_dir:
            /etc/salt/pki/master
        preserve_minion_cache:
            False
        pub_hwm:
            1000
        publish_port:
            4505
        publish_session:
            86400
        queue_dirs:
        raet_alt_port:
            4511
        raet_clear_remotes:
            False
        raet_main:
            True
        raet_mutable:
            False
        raet_port:
            4506
        range_server:
            range:80
        reactor:
        reactor_refresh_interval:
            60
        reactor_worker_hwm:
            10000
        reactor_worker_threads:
            10
        renderer:
            yaml_jinja
        ret_port:
            4506
        root_dir:
            /
        rotate_aes_key:
            True
        runner_dirs:
        saltversion:
            2015.5.10
        search:
        search_index_interval:
            3600
        serial:
            msgpack
        show_jid:
            False
        show_timeout:
            True
        sign_pub_messages:
            False
        sock_dir:
            /var/run/salt/master
        sqlite_queue_dir:
            /var/cache/salt/master/queues
        ssh_passwd:
        ssh_port:
            22
        ssh_scan_ports:
            22
        ssh_scan_timeout:
            0.01
        ssh_sudo:
            False
        ssh_timeout:
            60
        ssh_user:
            root
        state_aggregate:
            False
        state_auto_order:
            True
        state_events:
            False
        state_output:
            full
        state_top:
            salt://top.sls
        state_top_saltenv:
            None
        state_verbose:
            True
        sudo_acl:
            False
        svnfs_branches:
            branches
        svnfs_env_blacklist:
        svnfs_env_whitelist:
        svnfs_mountpoint:
        svnfs_remotes:
        svnfs_root:
        svnfs_tags:
            tags
        svnfs_trunk:
            trunk
        syndic_dir:
            /var/cache/salt/master/syndics
        syndic_event_forward_timeout:
            0.5
        syndic_jid_forward_cache_hwm:
            100
        syndic_master:
        syndic_max_event_process_time:
            0.5
        syndic_wait:
            5
        timeout:
            5
        token_dir:
            /var/cache/salt/master/tokens
        token_expire:
            43200
        transport:
            zeromq
        user:
            root
        verify_env:
            True
        win_gitrepos:
            - https://github.com/saltstack/salt-winrepo.git
        win_repo:
            /srv/salt/win/repo
        win_repo_mastercachefile:
            /srv/salt/win/repo/winrepo.p
        worker_floscript:
            /usr/lib/python2.7/site-packages/salt/daemons/flo/worker.flo
        worker_threads:
            5
        zmq_filtering:
            False

 

pillar的用途:a針對敏感數據,好比對數據加密,只能是指定的minion看到;b變量的差別性;

 

二、使用自定義的pillar

開啓pillar的sls,須要在master的配置文件中去掉以下的註釋;

vim /etc/salt/master
pillar_roots:
  base:
    - /srv/pillar

建立pillar的目錄和top file

[root@linux-node1 base]# pwd
/srv/pillar/base
[root@linux-node1 base]# ls
sc.sls  top.sls  zabbix
[root@linux-node1 base]# pwd
/srv/pillar/base
[root@linux-node1 base]# cat top.sls 
base:
  '*':
    - zabbix.agent                    #zabbix 下的agent.sls
  'linux-node2-computer':
    - sc                                     #sc.sls


#sc.sls
[root@linux-node1 base]# cat sc.sls 
cange: 1
charles.net: 2
DF: 22222
12_1T: cache_dir coss /data/cache1/coss 6000 max
    
       cache_dir aufs /data/cach1 20000 128 128 min
[root@linux-node1 base]# cat sc.sls 
cange: 1
charles.net: 2
DF: 22222
12_1T: cache_dir coss /data/cache1/coss 6000 max    #兩行,必須空一行
    
       cache_dir aufs /data/cach1 20000 128 128 min


#測試
[root@linux-node1 base]# salt '*' pillar.data
linux-node2-computer:
    ----------
    12_1T:
        cache_dir coss /data/cache1/coss 6000 max
        cache_dir aufs /data/cach1 20000 128 128 min
    DF:
        22222
    cange:
        1
    charles.net:
        2
    zabbix-agent:
        ----------
        Zabbix_Server:
            192.168.74.20
linux-node1.oldboyedu.com:
    ----------
    zabbix-agent:
        ----------
        Zabbix_Server:
            192.168.74.20

 

 

在pillar的base目錄下建立apache.sls文件,這裏咱們使用jinjia模板:

[root@22-57 pillar]# pwd
/srv/pillar
[root@22-57 pillar]# cat apache.sls 
{% if grains['os'] == 'CentOS' %}
apache: httpd
{% elif grains['os'] == 'Debian'%}
apache: apche2
{% endif %}

 

在top file中指定哪些minion能夠接收到該pillar的數據:

[root@linux-node1 ~]# cd /srv/pillar/
[root@linux-node1 pillar]# cat top.sls 
base:
  '*':
    - apache

執行以下:

[root@22-57 pillar]# salt '*' pillar.items
172.16.22.35:
    ----------
    apache:
        httpd
172.16.22.57:
    ----------
    apache:
        httpd

 

 

固然pillar也用來匹配minion,前提是須要先刷新

刷新pillar,並執行pillar的sls;

[root@22-57 ~]# salt '*' saltutil.refresh_pillar
172.16.22.57:
    True
172.16.22.35:
    True
[root@22-57 ~]# salt -I 'apache:httpd' test.ping    #使用pillar匹配
172.16.22.35:
    True
172.16.22.57:
    True

 

三、grains和pillar的使用

#定義base 環境的top file
[root@linux-node1 base]# pwd
/srv/salt/base
[root@linux-node1 base]# cat top.sls
base:
  'linux-node2-computer':
    - test.test    #執行test目錄下面的test.sls文件

#test.sls
[root@linux-node1 test]# cat test.sls 
/tmp/squid.conf:
  file.managed:
    - source: salt://test/squid.conf.jinjia     #引入jinja文件
    - template: jinja


#jinja文件
visible_host {{ grains['fqdn'] }}
{{ pillar['12_1T'] }}
{{ pillar['DF'] }}

test {{ grains['charles'] }}


#執行
[root@linux-node1 test]# salt '*' state.highstate 
linux-node1.oldboyedu.com:
----------
          ID: states
    Function: no.None
      Result: False
     Comment: No Top file or external nodes data matches found.
     Changes:   

Summary for linux-node1.oldboyedu.com
------------
Succeeded: 0
Failed:    1
------------
Total states run:     1
Total run time:   0.000 ms
linux-node2-computer:
----------
          ID: /tmp/squid.conf
    Function: file.managed
      Result: True
     Comment: File /tmp/squid.conf is in the correct state
     Started: 11:58:26.578232
    Duration: 38.022 ms
     Changes:   

Summary for linux-node2-computer
------------
Succeeded: 1
Failed:    0
------------
Total states run:     1
Total run time:  38.022 ms


#執行結果
[root@linux-node2 salt]# cat /tmp/squid.conf 
visible_host linux-node2.openstack.com
cache_dir coss /data/cache1/coss 6000 max
cache_dir aufs /data/cach1 20000 128 128 min
22222

test 5

  

5、遠程執行詳解

一、目標

a、使用通配符

[root@linux-node1 ~]# salt 'linux-node?.example.com' cmd.run 'w'
linux-node1.example.com:
     21:22:28 up  5:27,  2 users,  load average: 0.15, 0.17, 0.11
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    root     pts/0    192.168.74.1     14:17    3:49m  0.13s  0.13s -bash
    root     pts/1    192.168.74.1     21:18    4.00s  0.54s  0.35s /usr/bin/python /usr/bin/salt linux-node?.example.com cmd.run w
linux-node2.example.com:
     21:22:28 up  5:27,  2 users,  load average: 0.02, 0.04, 0.05
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    root     pts/0    192.168.74.1     14:17    5:51m  0.22s  0.22s -bash
    root     pts/1    192.168.74.1     21:18   28.00s  0.06s  0.06s -bash
[root@linux-node1 ~]# salt 'linux-node[1,2].example.com' cmd.run 'w' 
linux-node2.example.com:
     21:24:32 up  5:29,  2 users,  load average: 0.07, 0.06, 0.05
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    root     pts/0    192.168.74.1     14:17    5:53m  0.22s  0.22s -bash
    root     pts/1    192.168.74.1     21:18    2:32   0.06s  0.06s -bash
linux-node1.example.com:
     21:24:32 up  5:29,  2 users,  load average: 0.09, 0.14, 0.11
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    root     pts/0    192.168.74.1     14:17    3:51m  0.13s  0.13s -bash
    root     pts/1    192.168.74.1     21:18    0.00s  0.56s  0.36s /usr/bin/python /usr/bin/salt linux-node[1,2].example.com cmd.run w
[root@linux-node1 ~]# salt 'linux-node[1-2].example.com' cmd.run 'w'  
linux-node1.example.com:
     21:24:39 up  5:29,  2 users,  load average: 0.09, 0.14, 0.11
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    root     pts/0    192.168.74.1     14:17    3:51m  0.13s  0.13s -bash
    root     pts/1    192.168.74.1     21:18    7.00s  0.55s  0.34s /usr/bin/python /usr/bin/salt linux-node[1-2].example.com cmd.run w
linux-node2.example.com:
     21:24:39 up  5:29,  2 users,  load average: 0.07, 0.06, 0.05
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    root     pts/0    192.168.74.1     14:17    5:53m  0.22s  0.22s -bash
    root     pts/1    192.168.74.1     21:18    2:39   0.06s  0.06s -bash

b、正則表達式

[root@linux-node1 ~]# salt -E 'linux-node(1|2).example.com' test.ping    #正則表達式使用-E
linux-node1.example.com:
    True
linux-node2.example.com:
    True
[root@linux-node1 ~]# salt -L 'linux-node1.example.com,linux-node2.example.com' test.ping    #list
linux-node1.example.com:
    True
linux-node2.example.com:
    True  

c、ip地址

[root@linux-node1 ~]# salt -S '192.168.74.20' test.ping
linux-node1.example.com:
    True
[root@linux-node1 ~]# salt -S '192.168.74.0/24' test.ping  
linux-node1.example.com:
    True
linux-node2.example.com:
    True

  

多種匹配條件

[root@linux-node1 ~]# salt -C 'S@192.168.74.21 or G@web:nginx' test.ping   
linux-node1.example.com:
    True
linux-node2.example.com:
    True

  

注意:minion-id很重要

二、模塊

a、service模塊

[root@linux-node1 ~]# salt '*' service.available sshd     #判斷sshd是否啓動
linux-node1.example.com:
    True
linux-node2.example.com:
    True
salt '*' service.get_all    #獲取全部的服務
[root@linux-node1 ~]# salt '*' service.missing sshd
linux-node2.example.com:
    False
linux-node1.example.com:
    False

 

重啓服務 

[root@linux-node1 ~]# salt '*' service.reload httpd
linux-node2.example.com:
    True
linux-node1.example.com:
    True
[root@linux-node1 ~]# salt '*' service.status httpd
linux-node1.example.com:
    True
linux-node2.example.com:
    True
[root@linux-node1 ~]# salt '*' service.stop httpd  
linux-node1.example.com:
    True
linux-node2.example.com:
    True
[root@linux-node1 ~]# salt '*' service.start httpd
linux-node1.example.com:
    True
linux-node2.example.com:
    True

b、network

[root@linux-node1 ~]# salt '*' network.interface ens33 
linux-node1.example.com:
    |_
      ----------
      address:
          192.168.74.20
      broadcast:
          192.168.74.255
      label:
          ens33
      netmask:
          255.255.255.0
linux-node2.example.com:
    |_
      ----------
      address:
          192.168.74.21
      broadcast:
          192.168.74.255
      label:
          ens33
      netmask:
          255.255.255.0

 

這裏介紹一下模塊的ACL:

client_acl:
  oldboy:
    - test.ping      #只能夠指定test.ping 和Network模塊
    - network.*
  user01:
    - linux-node1*:      #支隊node1能夠執行test.ping
      - test.ping

    同時須要賦予普通用戶對文件操做的權限

chmod 755 /var/cache/salt/ /var/cache/salt/master/ /var/cache/salt/master/jobs/  /var/run/salt /var/run/salt/master/

測試:

[user01@linux-node1 ~]$ salt 'linux-node1*' test.ping
linux-node1.example.com:
    True
[user01@linux-node1 ~]$ salt '*' test.ping           
Failed to authenticate! This is most likely because this user is not permitted to execute commands, but there is a small possibility that a disk error occurred (check disk/inode usage).

 

 salt還能夠設置黑名單

#
#client_acl_blacklist:
#  users:
#    - root
#    - '^(?!sudo_).*$'   #  all non sudo users
#  modules:
#    - cmd

 

三、返回

 

建立salt的庫和表:

mysql> CREATE DATABASE`salt`
    -> DEFAULT CHARACTER SET utf8
    -> DEFAULT COLLATE utf8_general_ci;
Query OK, 1 row affected (0.03 sec)

mysql> use salt;
Database changed

DROP TABLE IF EXISTS `jids`;
CREATE TABLE `jids` (
  `jid` varchar(255) NOT NULL,
  `load` mediumtext NOT NULL,
  UNIQUE KEY `jid` (`jid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE INDEX jid ON jids(jid) USING BTREE;

--
-- Table structure for table `salt_returns`
--

DROP TABLE IF EXISTS `salt_returns`;
CREATE TABLE `salt_returns` (
  `fun` varchar(50) NOT NULL,
  `jid` varchar(255) NOT NULL,
  `return` mediumtext NOT NULL,
  `id` varchar(255) NOT NULL,
  `success` varchar(10) NOT NULL,
  `full_ret` mediumtext NOT NULL,
  `alter_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  KEY `id` (`id`),
  KEY `jid` (`jid`),
  KEY `fun` (`fun`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Table structure for table `salt_events`
--

DROP TABLE IF EXISTS `salt_events`;
CREATE TABLE `salt_events` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`tag` varchar(255) NOT NULL,
`data` mediumtext NOT NULL,
`alter_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`master_id` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `tag` (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
mysql> show tables;
+----------------+
| Tables_in_salt |
+----------------+
| jids           |
| salt_events    |
| salt_returns   |
+----------------+
3 rows in set (0.00 sec)

mysql> 
mysql> grant all on salt.* to salt@'192.168.74.0/255.255.255.0' identified by 'salt';
Query OK, 0 rows affected (0.00 sec)

 

全部的minion和master都必須安裝MySQL-python的包;

 

在master和minion的配置文件中添加以下配置:

return: mysql   #若是沒有該參數,須要加--return參數

mysql.host: '192.168.74.20'
mysql.user: 'salt'
mysql.pass: 'salt'
mysql.db: 'salt'
mysql.port: 3306

 

重啓配置文件以後

[root@linux-node1 ~]# salt '*' cmd.run 'uptime'
linux-node2.example.com:
     22:09:15 up 10:21,  1 user,  load average: 0.07, 0.07, 0.07
linux-node1.example.com:
     22:09:15 up 10:21,  2 users,  load average: 0.16, 0.15, 0.11
[root@linux-node1 ~]# salt '*' cmd.run 'df -h' --return mysql
linux-node1.example.com:
    Filesystem               Size  Used Avail Use% Mounted on
    /dev/mapper/centos-root   18G   12G  5.6G  69% /
    devtmpfs                 485M     0  485M   0% /dev
    tmpfs                    495M   16K  495M   1% /dev/shm
    tmpfs                    495M   14M  482M   3% /run
    tmpfs                    495M     0  495M   0% /sys/fs/cgroup
    /dev/sda1                497M  125M  373M  26% /boot
    tmpfs                     99M     0   99M   0% /run/user/0
    /dev/sr0                 4.1G  4.1G     0 100% /mnt
linux-node2.example.com:
    Filesystem               Size  Used Avail Use% Mounted on
    /dev/mapper/centos-root   18G   12G  5.6G  68% /
    devtmpfs                 215M     0  215M   0% /dev
    tmpfs                    225M   16K  225M   1% /dev/shm
    tmpfs                    225M   13M  212M   6% /run
    tmpfs                    225M     0  225M   0% /sys/fs/cgroup
    /dev/sda1                497M  125M  373M  26% /boot
    tmpfs                     45M     0   45M   0% /run/user/0
    /dev/sr0                 4.1G  4.1G     0 100% /mnt

 

返回的數據會存放在salt_returns表中

 

或者使用job_cache,將接受到的數據寫入mysql中

master_job_cache: mysql

mysql.host: '192.168.74.20'
mysql.user: 'salt'
mysql.pass: 'salt'
mysql.db: 'salt'
mysql.port: 3306

 

 

 

 6、salt配置管理

salt的配置管理基於遠程執行的

在master的配置文件中指定top file文件位置

file_roots:
  base:
    - /srv/salt/base
  test:
    - /srv/salt/test
  prod:
    - /srv/salt/prod
[root@22-57 ~]# mkdir /srv/salt/{base,test,prod}
[root@22-57 ~]# cd /srv/salt/
[root@22-57 salt]# mv top.sls apache.sls base/
[root@22-57 salt]# ll
總用量 0
drwxr-xr-x 2 root root 37 12月 12 23:08 base
drwxr-xr-x 2 root root  6 12月 12 23:07 prod
drwxr-xr-x 2 root root  6 12月 12 23:07 test
[root@22-57 salt]# pwd
/srv/salt

 

練習:salt文件管理

[root@22-57 base]# cat dns.sls 
/var/log/secure:
  file.managed:
    - souce: salt://files/secure
    - user: root
    - group: root
    - mode: 777

有兩種執行方式:

salt '*' state.sls dns

或者定義top file,

[root@22-57 base]# cat top.sls 
base:
  '*':
    - dns

salt '*' state.highstate執行

 

6、saltstak配置管理  -yaml和jinjia

配置管理三步分:一、系統初始化。二、功能模塊。三、業務模塊。

一、yaml語法規則

規則一:縮進,每個縮進級別使用兩個空格組成。

規則二:冒號,每個冒號後面都有一個空格,處理以冒號結尾和使用冒號表示路徑以外。

規則三:短橫線,想要表示列表項,使用短橫線加空格。

二、YAML和Jinjia

步驟:系統初始化-->功能模塊-->業務模塊

YAML的語法規則:

a、使用兩個空格表示層級關係;  
b、冒號,每一個冒號以後都有一個空格,除了以冒號結尾的;
c、短橫線(列表),每個短橫線後面都須要有一個空格;

 

Jinjia模板:這裏使用例子說明,仍是以前的DNS文件

		[root@linux-node1 base]# cat dns.sls 
				/etc/resolv.conf:
				  file.managed:
					- source: salt://files/resolv.conf
					- user: root
					- group: root
					- mode: 644
					- template: jinja      #使用jinj模板
					- defaults: 
					  DNS_SERVER: 8.8.8.8   #定義模板變量
		[root@linux-node1 base]# cat files/resolv.conf 
				#dns server
				nameserver {{ DNS_SERVER }}    #引用模板變量
		
		[root@linux-node1 base]# cat files/resolv.conf 
				#dns server
				# {{ grains['fqdn_ip4']}}      #也能夠在Jinja模板中使用grains
				nameserver {{ DNS_SERVER }}
				
				#jinja模板中也能夠加執行模塊,加pillar

 

三、系統初始化

將系統初始化的sls所有放在/srv/salt/base/init下面,使用top.sls進行調研

[root@22-57 base]# tree 
.
├── apache.sls
├── init
│   ├── audit.sls
│   ├── dns.sls
│   ├── env_init.sls
│   ├── files
│   │   ├── resolv.conf
│   │   └── secure
│   ├── history.sls
│   └── sysctl.sls
└── top.sls

 

[root@22-57 base]# cat top.sls 
base:
  '*':
    - init.env_init

[root@22-57 init]# cat env_init.sls 
include:
  - init.dns
  - init.history
  - init.audit
  - init.sysct

  

初始化文件所有寫在init目錄下

增長環境變量,讓history文件記錄時間戳
[root@22-57 init]# cat history.sls 
/etc/profile:
  file.append:
    - text:
      - export HISTTIMEFORMAT="%F %T `whoami` "


日誌審計:將操做命令所有記錄在message文件中
[root@22-57 init]# cat audit.sls 
/etc/bashrc:
  file.append:
    - text:
      - export PROMPT_COMMAND='{ msg=$(history 1 | { read x y; echo $y;});logger "[euid=$(whoami)]":$(who am i):[`pwd`]"$msg";}'


調整內核參數:不使用交換分區,從新規劃端口範圍,設定能夠打開的文件數量
[root@22-57 init]# cat sysctl.sls 
vm.swappiness:
  sysctl.present:
    - value: 0


net.ipv4.ip_local_port_range:
  sysctl.present:
    - value: 10000 65000

fs.file-max:
  sysctl.present:
    - value: 100000

 

如今基礎環境下有了這幾個sls:

[root@linux-node1 init]# ls -l
	total 20
	-rw-r--r--. 1 root root 168 Jan 26 18:19 audit.sls
	-rw-r--r--. 1 root root 194 Jan 25 22:27 dns.sls
	drwxr-xr-x. 2 root root  24 Jan 25 22:27 files
	-rw-r--r--. 1 root root  88 Jan 26 18:09 history.sls
	-rw-r--r--. 1 root root 175 Jan 26 18:35 sysctl.sls

 

若是將上面的這些SLS放入TOP file中執行的話,top file的內容會不少,因此定義一個sls,將全部的sls include進去:

[root@linux-node1 init]# cat env_init.sls     #init.表示文件的路徑,全部的sls是從base目錄開始查找的
	include:
	  - init.dns
	  - init.history
	  - init.audit
	  - init.sysctl

 

最後調用:

[root@linux-node1 base]# cat top.sls     #最後在top file 中引用
	base:
	  '*':
		- init.env_init 	

[root@linux-node1 ~]# salt '*' state.highstate test=True     #測試
[root@linux-node1 ~]# salt '*' state.highstate

 

ps:涉及到的系統知識

export PROMPT_COMMAND='{msg=$(history 1 | {read x y;echo $y;});logger "[euid=$(whoami)]";$(who am i):[`pwd`]"$msg";}'    #將操做日誌加到message中


cat /proc/sys/net/ipv4/ip_local_port_range     #端口範圍

 
cat /proc/sys/fs/file-max    #打開文件描述符數



salt '*' state.highstate test=True    #測試sls,看執行了哪些操做

  

四、業務引用haproxy

[root@22-57 cluster]# cat haproxy-outside.sls 
include:
  - haproxy.install

haproxy-service:
  file.managed:
    - name: /etc/haproxy/haproxy.cfg
    - source: salt://cluster/files/haproxy-outside.cfg
    - user: root
    - group: root
    - mode: 644
  service.running:
    - name: haproxy
    - enable: True
    - reload: True
    - require:
      - cmd: haproxy-init
    - watch:                 #配置文件改變,自動reload
      - file: haproxy-service
[root@22-57 cluster]# pwd
/srv/salt/prod/cluste

 

[root@22-57 base]# cat top.sls 
base:
  '*':
    - init.env_init

prod:
  '172.16.22.57':
    - cluster.haproxy-outside
  '172.16.22.35':
    - cluster.haproxy-outside
[root@22-57 base]# pwd
/srv/salt/bas

 

haproxy的配置文件以下:

[root@22-57 files]# cat haproxy-outside.cfg 
global
maxconn 100000
chroot /usr/local/haproxy
uid 99
gid 99
daemon
nbproc 1
pidfile /usr/local/haproxy/logs/haproxy.pid
log 127.0.0.1 local3 info

defaults
option http-keep-alive
maxconn 100000
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms

listen stats
mode http
bind 0.0.0.0:8888
stats enable
stats uri    /haproxy-status
stats auth   haproxy:saltstack

frontend frontend_www_example_com
bind 172.16.22.50:80
mode http
option httplog
log global
    default_bankend backend_www_example_com

backend backend_www_example_com
option forwardfor header X-REAL-IP
option httpchk HEAD / HTTP/1.0
balance source
server web-node1 172.16.22.57:8080 check inter 2000 rise 30 fall 15
server web-node2 172.16.22.35:8080 check inter 2000 rise 30 fall 15

 

http://192.168.74.20:8888/haproxy-status
解決haproxy 403問題:修改/var/www/html/index.html內容
http://192.168.74.21:8080/

須要的salt的文件目錄以下:

[root@linux-node1 prod]# pwd
/srv/salt/prod
[root@linux-node1 prod]# ls -l
total 0
drwxr-xr-x. 3 root root 81 Jan 26 22:40 cluster #業務模塊
drwxr-xr-x. 3 root root 36 Jan 26 23:01 haproxy #haproxy的功能模塊
drwxr-xr-x. 2 root root 25 Jan 26 22:52 pkg #全部包的安裝

 

五、配置管理keepalived

keepalived是vrrp協議的實現;

[root@linux-node1 keepalived]# cat install.sls   #安裝
		keepalived-install:
		  file.managed:
			- name: /usr/local/src/keepalived-1.2.17.tar.gz
			- source: salt://keepalived/files/keepalived-1.2.17.tar.gz
			- mode: 755
			- user: root
			- group: root
		  cmd.run:
			- name: cd /usr/local/src && tar zxf keepalived-1.2.17.tar.gz && cd keepalived-1.2.17 && ./configure --prefix=/usr/local/keepalived --disable-fwmark && make && make install
			- unless: test -d /usr/local/keepalived
			- require:
			  - file: keepalived-install

		/etc/sysconfig/keepalived:
		  file.managed:
			- source: salt://keepalived/files/keepalived.sysconfig
			- mode: 644
			- user: root
			- group: root

		/etc/init.d/keepalived:
		  file.managed:
			- source: salt://keepalived/files/keepalived.init
			- mode: 755
			- user: root
			- group: root

		keepalived-init:
		  cmd.run:
			- name: chkconfig --add keepalived
			- unless: chkconfig --list | grep keepalived
			- require:
			  - file: /etc/init.d/keepalived

		/etc/keepalived:
		  file.directory:
			- user: root
			- group: root
			
			
[root@linux-node1 ~]# salt '*' state.sls keepalived.install env=prod    #執行

 

六、業務引用keepalived

[root@22-57 init]# cat zabbix_agent.sls 
zabbix-agent-install:
  pkg.installed:
    - name: zabbix-agent

  file.managed:
    - name: /etc/zabbix/zabbix_agent.d.conf
    - source: salt://init/files/zabbix_agentd.conf
    - template: jiajia
    - defaults:
      Server: {{ pillar['zabbix-agent']['Zabbix_Server'] }}    #使用自定義的pillar
    - require:
      - pkg: zabbix-agent-install

  service.running:
    - name: zabbix-agent
    - enable: True
    - watch:
      - pkg: zabbix-agent-install
      - file: zabbix-agent-install
[root@22-57 init]# pwd
/srv/salt/base/init

 

[root@linux-node1 zabbix]# cat agent.sls 
zabbix-agent:
  Zabbix_Server: 192.168.74.20
[root@linux-node1 zabbix]# pwd
/srv/pillar/base/zabbix

  

[root@linux-node1 base]# cat top.sls 
base:
  '*':
    - zabbix.agent
[root@linux-node1 base]# pwd
/srv/pillar/base

 

[root@linux-node1 base]# salt '*' pillar.items
linux-node2.example.com:
    ----------
    zabbix-agent:
        ----------
        Zabbix_Server:
            192.168.74.20
linux-node1.example.com:
        ----------
    zabbix-agent:
        ----------
        Zabbix_Server:
            192.168.74.20 

須要注意的是,在master的配置文件中,須要將pillar_roots的目錄設置爲/srv/pillar/base;

 

將zabbix-agent加載的基礎服務中

[root@22-57 init]# cat env_init.sls 
include:
  - init.dns
  - init.history
  - init.audit
  - init.sysctl
  - init.zabbix_agent

  

詳細代碼請參考:

https://github.com/unixhot/saltbook-code 

 

zabbix監控mysal的插件:

https://www.percona.com/software/database-tools/percona-monitoring-and-management 

  

7、saltstack架構擴展

生產應用不要使用root用戶;
online/offline;
最佳實踐不是刪掉,而是mv到offline下面;
做業:mysql安裝,實現自動化安裝

salt-minion從新啓動以後,須要等待一會時間再使用;

共享配置文件建議放在版本管理中;

 

一、

安裝python-setproctitle能夠查看python程序的進程名;

重啓salt-master以後,就能夠查看master的進程名啦!

通常狀況下,salt若是因爲網絡延遲形成返回失敗的話,能夠將timeout的時間定的長一點(在master的配置文件中設定);

 

固然,saltstak支持無master的狀況,也就是在minion端本地執行salt的相關命令,須要首先在minion的配置文件中將file_client配置爲remote,重啓minion,

[root@22-57 ~]# salt-call --local test.ping
local:
    True

這樣就能夠經過salt-call執行啦!

主要是在沒有master的狀況下,安裝master的時候使用,能夠將master的相關配置文件(source),放置到公網上,使用http進行相關配置(source支持http),配置目錄結構等;

 

 

 

minion固然支持多個mater,不建議使用;

 

二、syndic

必須運行在一個master上,而且在鏈接到另一個master(比他更高級)。

有兩臺機器:172.16.22.35和172.16.22.57

 

首先在172.16.22.35上安裝salt-master和salt-syndic,在172.16.22.57上安裝salt-master;

在172.16.22.57上的master的配置文件中配置syndic的地址:  syndic_master: 172.16.22.57;

中止兩臺機器的minion,並將/etc/salt/pki/minion的內容清空,若是有minion_id文件,建議刪除(必須中止minion後在刪除密鑰文件);

重啓172.16.22.35上的master,啓動salt-syndic,將minion端的配置文件的Server地址配置爲172.16.22.35;

啓動minion;

這樣在172.16.22.57 上執行salt-key,能夠看到172.16.22.35,這是master,不是minion,信任他吧!同時在172.16.22.35上能夠看到35和57l兩臺機器,一樣信任;這樣,就能夠在master上執行salt的命令了,實現了分佈式!

[root@22-57 pki]# salt-key 
Accepted Keys:
172.16.22.35
Denied Keys:
Unaccepted Keys:
Rejected Keys:
[root@22-57 pki]# 
[root@22-57 pki]# 
[root@22-57 pki]# 
[root@22-57 pki]# salt '*' test.ping
172.16.22.57:
    True
172.16.22.35:
    True

  

優勢:能夠創建多層級的架構;   minion<-->syndic<--->master

缺點:須要保證syndic的file_roots須要和master一致,而且master不知道syndic有幾個,master不知道minion歸哪一個syndic管理;

發送時,syndic二次下發給minion,收的時候,由syndic將接收的結果發送給master;

 

8、syndic二次開發

一、自定義grains

首先在master的file_roots的base目錄下,建立自定義grains保存的目錄,並建立一個grains的實例;

[root@22-57 _grains]# pwd
/srv/salt/base/_grains
[root@22-57 _grains]# cat my_grains.py 
#!/usr/bin/env python
def my_grains():
    '''
    My Custom Grains
    '''
    grains= {'hehe1':'haha1','hehe2':'haha2'}
    return grains

使用命令:salt '*' saltutil.sync_grains,就會把自定義的grains傳到minion的緩存下面,緩存目錄爲:

[root@qiangqiang grains]# pwd
/var/cache/salt/minion/extmods/grains
[root@qiangqiang grains]# ls
my_grains.py  my_grains.pyc

 

這樣就可使用自定義的grains了

[root@22-57 _grains]# salt '*' grains.item hehe1
172.16.22.57:
    ----------
    hehe1:
        haha1
172.16.22.35:
    ----------
    hehe1:
        haha1
[root@22-57 _grains]# salt '*' grains.item hehe2
172.16.22.35:
    ----------
    hehe2:
        haha2
172.16.22.57:
    ----------
    hehe2:
        haha2

 

二、自定義模塊

在base目錄下建立modules的目錄,並建立自定義的module

[root@22-57 _modules]# pwd
/srv/salt/base/_modules
[root@22-57 _modules]# ls
my_disk.py

salt的模塊存放路徑爲:/usr/lib/python2.6/site-packages/salt/,salt的module支持調用其餘的salt模塊;

[root@22-57 _modules]# cat my_disk.py 
def list():
    cmd= 'df -h'
    ret = __salt__['cmd.run'](cmd)
    return ret

 

salt '*' saltutil.sync_modules將自定義的模塊發送到minion端;

salt '*' my_disk.list  調用自定義的salt模塊;

 

9、運維自動化

一、運維自動化的三個階段

第一:標準化,工具化;

     運維標準化,操做工具化,變動流程化,標準化運維;

第二:web化:爲了弱化流程,直接走下一步就能夠了,減小由於執行順序的緣由致使認爲失誤; 

    操做web化,權限控制、統計分析、統一調度 、web化運維

第三:服務化,API化;

    DNS服務、負載均衡服務、監控服務、分佈式緩存服務、分佈式存儲服務、CMDB;

二、自動化擴容加etcd部署;

job的添加,須要經過web進行添加;

job添加須要提交申請;

 

Zabbix監控-->Action-->建立一臺虛擬機/Docker容器--->部署服務-->部署代碼-->測試狀態-->加入集羣-->加入監控-->通知

 

etcd介紹:用於共享配置和服務發現

    a、簡單:curl能夠訪問用戶的API(HTTP+JSON);

    b、使用Raft保證數據的一直性;

 

思路:將haproxy的配置寫在etcd中,而後只有salt.pillar.etcd_pillar,就能夠動態獲取到這個pillar,最後在Jinjia中使用for循環就搞定了;

安裝etcd:

將etcd的安裝文件放置在/usr/local/src下,解壓
拷貝二進制文件到/usr/local/bin下;
[root@22-57 ~]# cd /usr/local/src/etcd-v2.2.1-linux-amd64
[root@22-57 etcd-v2.2.1-linux-amd64]# ls
Documentation  etcd  etcdctl  README-etcdctl.md  README.md
[root@22-57 etcd-v2.2.1-linux-amd64]# cp etcd etcdctl /usr/local/bin/

[root@22-57 ~]# etcd --version    #查看版本
etcd Version: 2.2.1
Git SHA: 75f8282
Go Version: go1.5.1
Go OS/Arch: linux/amd6

 

啓動etcd:

[root@22-57 ~]# nohup etcd --name auto_scale --data-dir /data/etcd/ --listen-peer-urls 'http://172.16.22.57:2380,http://172.16.22.57:7001' --listen-client-urls 'http://172.16.22.57:2379,http://172.16.22.57:4001' --advertise-client-urls 'http://172.16.22.57:2379,http://172.16.22.57:4001' &
[root@22-57 ~]# netstat -ntlp|grep etcd
tcp        0      0 172.16.22.57:4001           0.0.0.0:*                   LISTEN      53941/etcd          
tcp        0      0 172.16.22.57:2379           0.0.0.0:*                   LISTEN      53941/etcd          
tcp        0      0 172.16.22.57:2380           0.0.0.0:*                   LISTEN      53941/etcd          
tcp        0      0 172.16.22.57:7001           0.0.0.0:*                   LISTEN      53941/etcd 

 

使用curl put數據

[root@22-57 ~]# curl -s http://172.16.22.57:2379/v2/keys/message -XPUT -d value="Hello world" |python -m json.tool     #-d表示put的數據的內容
{
    "action": "set", 
    "node": {
        "createdIndex": 6, 
        "key": "/message", 
        "modifiedIndex": 6, 
        "value": "Hello world"
    }, 
    "prevNode": {
        "createdIndex": 5, 
        "key": "/message", 
        "modifiedIndex": 5, 
        "value": "Hello world"
    }
}

獲取put的值:

[root@22-57 ~]# curl -s http://172.16.22.57:2379/v2/keys/message |python -m json.tool
{
    "action": "get", 
    "node": {
        "createdIndex": 6, 
        "key": "/message", 
        "modifiedIndex": 6, 
        "value": "Hello world"
    }
}

 

刪除值:

[root@22-57 ~]# curl -s http://172.16.22.57:2379/v2/keys/message -XDELETE |python -m json.tool
{
    "action": "delete", 
    "node": {
        "createdIndex": 6, 
        "key": "/message", 
        "modifiedIndex": 7
    }, 
    "prevNode": {
        "createdIndex": 6, 
        "key": "/message", 
        "modifiedIndex": 6, 
        "value": "Hello world"
    }
}
[root@22-57 ~]# curl -s http://172.16.22.57:2379/v2/keys/message |python -m json.tool
{
    "cause": "/message", 
    "errorCode": 100, 
    "index": 7, 
    "message": "Key not found"
}

  

put一條數據,設置5秒以後自動刪除:

[root@22-57 ~]# curl -s http://172.16.22.57:2379/v2/keys/ttl_use -XPUT -d value="Hello world 1" -d ttl=5 |python -m json.tool
{
    "action": "set", 
    "node": {
        "createdIndex": 10, 
        "expiration": "2016-12-18T02:57:52.420004181Z", 
        "key": "/ttl_use", 
        "modifiedIndex": 10, 
        "ttl": 5, 
        "value": "Hello world 1"
    }
}


#自動刪除
[root@22-57 ~]# curl -s http://172.16.22.57:2379/v2/keys/ttl_use |python -m json.tool
{
    "action": "get", 
    "node": {
        "createdIndex": 10, 
        "expiration": "2016-12-18T02:57:52.420004181Z", 
        "key": "/ttl_use", 
        "modifiedIndex": 10, 
        "ttl": 1,     #剩餘時間
        "value": "Hello world 1"
    }
}
[root@22-57 ~]# curl -s http://172.16.22.57:2379/v2/keys/ttl_use |python -m json.tool
{
    "cause": "/ttl_use", 
    "errorCode": 100, 
    "index": 11, 
    "message": "Key not found"
}

 

這樣,在haproxy中添加的配合到時也會清除;

 

三、基於etcd和salstack的自動化擴容

 

 首先編輯salt-master的配置文件,並重啓;這樣salt-master和etcd就能夠通訊了;能夠經過salt '*' pillar.items就能夠獲取到設置的pillar了;

etcd_pillar_config:
  etcd.host: 172.16.22.57
  etcd.port: 4001

ext-pillar:
  - etcd: etcd_pillar_config root=/salt/haproxy/

 而後重啓salt-master

使用pip 安裝python-etcd

 

向etcd中添加key,value的數據,須要安裝python-etcd的模塊以後才能夠獲取pillar的值;

[root@linux-node1 ~]# curl -s http://192.168.74.20:2379/v2/keys/salt/haproxy/backend_www_oldboyedu_com/web-node1 -XPUT -d value="192.168.74.20:8080" |python -m json.tool
{
    "action": "set",
    "node": {
        "createdIndex": 20,
        "key": "/salt/haproxy/backend_www_oldboyedu_com/web-node1",
        "modifiedIndex": 20,
        "value": "192.168.74.20:8080"
    }
}    #建立了一個key,這個key在/salt/haproxy/backend_www_oldboyedu_com/下,key爲web-node1,key的值爲192.168.74.20:8080

 

	[root@linux-node1 ~]# salt '*' pillar.items
	linux-node1.example.com:
		----------
		backend_www_oldboyedu_com:
			----------
			web-node1:
				192.168.74.20:8080
		bankend_www_oldboyedu_com:
			----------
			web-node1:
				192.168.74.20:8080
		zabbix-agent:
			----------
			Zabbix_Server:
				192.168.74.20
	linux-node2.example.com:
		----------
		backend_www_oldboyedu_com:
			----------
			web-node1:
				192.168.74.20:8080
		bankend_www_oldboyedu_com:
			----------
			web-node1:
				192.168.74.20:8080
		zabbix-agent:
			----------
			Zabbix_Server:
				192.168.74.20

  

 

而後在haproxy的配置文件中替換最後的server的相關配置爲jinja模板的數據:

	[root@linux-node1 files]# pwd
	/srv/salt/prod/cluster/files
	[root@linux-node1 files]# cat haproxy-outside.cfg 
	global
	maxconn 100000
	chroot /usr/local/haproxy
	uid 99  
	gid 99 
	daemon
	nbproc 1 
	pidfile /usr/local/haproxy/logs/haproxy.pid 
	log 127.0.0.1 local3 info

	defaults
	option http-keep-alive
	maxconn 100000
	mode http
	timeout connect 5000ms
	timeout client  50000ms
	timeout server 50000ms

	listen stats
	mode http
	bind 0.0.0.0:8888
	stats enable
	stats uri     /haproxy-status 
	stats auth    haproxy:saltstack

	frontend frontend_www_example_com
	bind 192.168.74.22:80
	mode http
	option httplog
	log global
		default_backend backend_www_example_com

	backend backend_www_example_com
	option forwardfor header X-REAL-IP
	option httpchk HEAD / HTTP/1.0
	balance roundrobin
	 
	{% for web,web_ip in pillar.backend_www_oldboyedu_com.iteritems() %}
	server {{ web }} {{ web_ip }} check inter 2000 rise 30 fall 15
	{% endfor %}
	
	
	[root@linux-node1 cluster]# cat haproxy-outside-keepalived.sls   #而且要告訴sls使用的是Jinja模板
	include:
	  - keepalived.install
	keepalived-server:
	  file.managed:
		- name: /etc/keepalived/keepalived.conf
		- source: salt://cluster/files/haproxy-outside-keepalived.conf
		- mode: 644
		- user: root
		- group: root
		- template: jinja
		{% if grains['fqdn'] == 'linux-node1.example.com' %}
		- ROUTEID: haproxy_ha
		- STATEID: MASTER
		- PRIORITYID: 150
		{% elif grains['fqdn'] == 'linux-node2.example.com' %}
		- ROUTEID: haproxy_ha
		- STATEID: BACKUP
		- PRIORITYID: 100
		{% endif %}
	  service.running:
		- name: keepalived
		- enable: True
		- watch:
		  - file: keepalived-server
		  

 

[root@22-57 cluster]# cat haproxy-outside.sls 
include:
  - haproxy.install

haproxy-service:
  file.managed:
    - name: /etc/haproxy/haproxy.cfg
    - source: salt://cluster/files/haproxy-outside.cfg
    - user: root
    - group: root
    - mode: 644
    - template: jinja    #必定要指名模板
  service.running:
    - name: haproxy
    - enable: True
    - reload: True
    - require:
      - cmd: haproxy-init
    - watch:
      - file: haproxy-service
[root@22-57 cluster]# pwd
/srv/salt/prod/cluster

  

這樣就能夠經過在etcd中添加數據,來增長haproxy的節點了;

 

curl -s http://192.168.74.20:2379/v2/keys/salt/haproxy/backend_www_oldboyedu_com/web-node2 -XPUT -d value="192.168.74.20:8080" |python -m json.tool
curl -s http://192.168.74.20:2379/v2/keys/salt/haproxy/backend_www_oldboyedu_com/web-node3 -XPUT -d value="192.168.74.20:8080" |python -m json.tool
curl -s http://192.168.74.20:2379/v2/keys/salt/haproxy/backend_www_oldboyedu_com/web-node4 -XPUT -d value="192.168.74.20:8080" |python -m json.tool

最後:salt '*' state.highstate

  

 

經過腳本實現以下:

[root@22-57 ~]# cat auth.sh 
#!/bin/sh

create_host(){
    echo "create host"
}

deploy_service(){
    salt '172.16.22.35' state.sls nginx.install env=prod
    ADD_HOST="172.16.22.35"
    ADD_HOST_PORT="8080"
}

delpoy_code(){
   echo "deploy code ok"
}

service_check(){
    STATUS=$(curl -s --head http://"$ADD_HOST":"$ADD_HOST_PORT"/ |grep '200 OK')
    if [ -n "$STATUS" ]
        echo "ok"
    else
        echo "not ok"
        exit
    fi

}

etcd_key(){
    curl "http://172.16.22.57:2379/v2/keys/salt/haproxy/backend_www_oldboyedu_com/web-node1 -XPUT -d value="${ADD_HOT}:${ADD_HOST_PORT}""

}

sync_state(){
    salt '*' '172.16.22.57' state.sls cluster.haproxy-outside env=prod
}

main(){
    create_host;
    deploy_service;
    deploy_code;
    etcd_key;
    sync_state;
}

main

  

# -*- coding: utf-8 -*-
'''
The static grains, these are the core, or built in grains.

When grains are loaded they are not loaded in the same way that modules are
loaded, grain functions are detected and executed, the functions MUST
return a dict which will be applied to the main grains dict. This module
will always be executed first, so that any grains loaded here in the core
module can be overwritten just by returning dict keys with the same value
as those returned here
'''

# Import python libs
from __future__ import absolute_import
import os
import json
import socket
import sys
import re
import platform
import logging
import locale
import uuid
from errno import EACCES, EPERM

__proxyenabled__ = ['*']
__FQDN__ = None

# Extend the default list of supported distros. This will be used for the
# /etc/DISTRO-release checking that is part of linux_distribution()
from platform import _supported_dists
_supported_dists += ('arch', 'mageia', 'meego', 'vmware', 'bluewhite64',
'slamd64', 'ovs', 'system', 'mint', 'oracle', 'void')

# linux_distribution deprecated in py3.7
try:
from platform import linux_distribution
except ImportError:
from distro import linux_distribution

# Import salt libs
import salt.exceptions
import salt.log
import salt.utils
import salt.utils.network
import salt.utils.dns
import salt.ext.six as six
from salt.ext.six.moves import range

if salt.utils.is_windows():
import salt.utils.win_osinfo

# Solve the Chicken and egg problem where grains need to run before any
# of the modules are loaded and are generally available for any usage.
import salt.modules.cmdmod
import salt.modules.smbios

__salt__ = {
'cmd.run': salt.modules.cmdmod._run_quiet,
'cmd.retcode': salt.modules.cmdmod._retcode_quiet,
'cmd.run_all': salt.modules.cmdmod._run_all_quiet,
'smbios.records': salt.modules.smbios.records,
'smbios.get': salt.modules.smbios.get,
}
log = logging.getLogger(__name__)

HAS_WMI = False
if salt.utils.is_windows():
# attempt to import the python wmi module
# the Windows minion uses WMI for some of its grains
try:
import wmi # pylint: disable=import-error
import salt.utils.winapi
import win32api
import salt.modules.reg
HAS_WMI = True
__salt__['reg.read_value'] = salt.modules.reg.read_value
except ImportError:
log.exception(
'Unable to import Python wmi module, some core grains '
'will be missing'
)

_INTERFACES = {}


def _windows_cpudata():
'''
Return some CPU information on Windows minions
'''
# Provides:
# num_cpus
# cpu_model
grains = {}
if 'NUMBER_OF_PROCESSORS' in os.environ:
# Cast to int so that the logic isn't broken when used as a
# conditional in templating. Also follows _linux_cpudata()
try:
grains['num_cpus'] = int(os.environ['NUMBER_OF_PROCESSORS'])
except ValueError:
grains['num_cpus'] = 1
grains['cpu_model'] = __salt__['reg.read_value'](
"HKEY_LOCAL_MACHINE",
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
"ProcessorNameString").get('vdata')
return grains


def _linux_cpudata():
'''
Return some CPU information for Linux minions
'''
# Provides:
# num_cpus
# cpu_model
# cpu_flags
grains = {}
cpuinfo = '/proc/cpuinfo'
# Parse over the cpuinfo file
if os.path.isfile(cpuinfo):
with salt.utils.fopen(cpuinfo, 'r') as _fp:
for line in _fp:
comps = line.split(':')
if not len(comps) > 1:
continue
key = comps[0].strip()
val = comps[1].strip()
if key == 'processor':
grains['num_cpus'] = int(val) + 1
elif key == 'model name':
grains['cpu_model'] = val
elif key == 'flags':
grains['cpu_flags'] = val.split()
elif key == 'Features':
grains['cpu_flags'] = val.split()
# ARM support - /proc/cpuinfo
#
# Processor : ARMv6-compatible processor rev 7 (v6l)
# BogoMIPS : 697.95
# Features : swp half thumb fastmult vfp edsp java tls
# CPU implementer : 0x41
# CPU architecture: 7
# CPU variant : 0x0
# CPU part : 0xb76
# CPU revision : 7
#
# Hardware : BCM2708
# Revision : 0002
# Serial : 00000000
elif key == 'Processor':
grains['cpu_model'] = val.split('-')[0]
grains['num_cpus'] = 1
if 'num_cpus' not in grains:
grains['num_cpus'] = 0
if 'cpu_model' not in grains:
grains['cpu_model'] = 'Unknown'
if 'cpu_flags' not in grains:
grains['cpu_flags'] = []
return grains


def _linux_gpu_data():
'''
num_gpus: int
gpus:
- vendor: nvidia|amd|ati|...
model: string
'''
if __opts__.get('enable_lspci', True) is False:
return {}

if __opts__.get('enable_gpu_grains', True) is False:
return {}

lspci = salt.utils.which('lspci')
if not lspci:
log.debug(
'The `lspci` binary is not available on the system. GPU grains '
'will not be available.'
)
return {}

# dominant gpu vendors to search for (MUST be lowercase for matching below)
known_vendors = ['nvidia', 'amd', 'ati', 'intel']
gpu_classes = ('vga compatible controller', '3d controller')

devs = []
try:
lspci_out = __salt__['cmd.run']('{0} -vmm'.format(lspci))

cur_dev = {}
error = False
# Add a blank element to the lspci_out.splitlines() list,
# otherwise the last device is not evaluated as a cur_dev and ignored.
lspci_list = lspci_out.splitlines()
lspci_list.append('')
for line in lspci_list:
# check for record-separating empty lines
if line == '':
if cur_dev.get('Class', '').lower() in gpu_classes:
devs.append(cur_dev)
cur_dev = {}
continue
if re.match(r'^\w+:\s+.*', line):
key, val = line.split(':', 1)
cur_dev[key.strip()] = val.strip()
else:
error = True
log.debug('Unexpected lspci output: \'{0}\''.format(line))

if error:
log.warning(
'Error loading grains, unexpected linux_gpu_data output, '
'check that you have a valid shell configured and '
'permissions to run lspci command'
)
except OSError:
pass

gpus = []
for gpu in devs:
vendor_strings = gpu['Vendor'].lower().split()
# default vendor to 'unknown', overwrite if we match a known one
vendor = 'unknown'
for name in known_vendors:
# search for an 'expected' vendor name in the list of strings
if name in vendor_strings:
vendor = name
break
gpus.append({'vendor': vendor, 'model': gpu['Device']})

grains = {}
grains['num_gpus'] = len(gpus)
grains['gpus'] = gpus
return grains


def _netbsd_gpu_data():
'''
num_gpus: int
gpus:
- vendor: nvidia|amd|ati|...
model: string
'''
known_vendors = ['nvidia', 'amd', 'ati', 'intel', 'cirrus logic', 'vmware']

gpus = []
try:
pcictl_out = __salt__['cmd.run']('pcictl pci0 list')

for line in pcictl_out.splitlines():
for vendor in known_vendors:
vendor_match = re.match(
r'[0-9:]+ ({0}) (.+) \(VGA .+\)'.format(vendor),
line,
re.IGNORECASE
)
if vendor_match:
gpus.append({'vendor': vendor_match.group(1), 'model': vendor_match.group(2)})
except OSError:
pass

grains = {}
grains['num_gpus'] = len(gpus)
grains['gpus'] = gpus
return grains


def _osx_gpudata():
'''
num_gpus: int
gpus:
- vendor: nvidia|amd|ati|...
model: string
'''

gpus = []
try:
pcictl_out = __salt__['cmd.run']('system_profiler SPDisplaysDataType')

for line in pcictl_out.splitlines():
fieldname, _, fieldval = line.partition(': ')
if fieldname.strip() == "Chipset Model":
vendor, _, model = fieldval.partition(' ')
vendor = vendor.lower()
gpus.append({'vendor': vendor, 'model': model})

except OSError:
pass

grains = {}
grains['num_gpus'] = len(gpus)
grains['gpus'] = gpus
return grains


def _bsd_cpudata(osdata):
'''
Return CPU information for BSD-like systems
'''
# Provides:
# cpuarch
# num_cpus
# cpu_model
# cpu_flags
sysctl = salt.utils.which('sysctl')
arch = salt.utils.which('arch')
cmds = {}

if sysctl:
cmds.update({
'num_cpus': '{0} -n hw.ncpu'.format(sysctl),
'cpuarch': '{0} -n hw.machine'.format(sysctl),
'cpu_model': '{0} -n hw.model'.format(sysctl),
})

if arch and osdata['kernel'] == 'OpenBSD':
cmds['cpuarch'] = '{0} -s'.format(arch)

if osdata['kernel'] == 'Darwin':
cmds['cpu_model'] = '{0} -n machdep.cpu.brand_string'.format(sysctl)
cmds['cpu_flags'] = '{0} -n machdep.cpu.features'.format(sysctl)

grains = dict([(k, __salt__['cmd.run'](v)) for k, v in six.iteritems(cmds)])

if 'cpu_flags' in grains and isinstance(grains['cpu_flags'], six.string_types):
grains['cpu_flags'] = grains['cpu_flags'].split(' ')

if osdata['kernel'] == 'NetBSD':
grains['cpu_flags'] = []
for line in __salt__['cmd.run']('cpuctl identify 0').splitlines():
cpu_match = re.match(r'cpu[0-9]:\ features[0-9]?\ .+<(.+)>', line)
if cpu_match:
flag = cpu_match.group(1).split(',')
grains['cpu_flags'].extend(flag)

if osdata['kernel'] == 'FreeBSD' and os.path.isfile('/var/run/dmesg.boot'):
grains['cpu_flags'] = []
# TODO: at least it needs to be tested for BSD other then FreeBSD
with salt.utils.fopen('/var/run/dmesg.boot', 'r') as _fp:
cpu_here = False
for line in _fp:
if line.startswith('CPU: '):
cpu_here = True # starts CPU descr
continue
if cpu_here:
if not line.startswith(' '):
break # game over
if 'Features' in line:
start = line.find('<')
end = line.find('>')
if start > 0 and end > 0:
flag = line[start + 1:end].split(',')
grains['cpu_flags'].extend(flag)
try:
grains['num_cpus'] = int(grains['num_cpus'])
except ValueError:
grains['num_cpus'] = 1

return grains


def _sunos_cpudata():
'''
Return the CPU information for Solaris-like systems
'''
# Provides:
# cpuarch
# num_cpus
# cpu_model
# cpu_flags
grains = {}
grains['cpu_flags'] = []

grains['cpuarch'] = __salt__['cmd.run']('isainfo -k')
psrinfo = '/usr/sbin/psrinfo 2>/dev/null'
grains['num_cpus'] = len(__salt__['cmd.run'](psrinfo, python_shell=True).splitlines())
kstat_info = 'kstat -p cpu_info:*:*:brand'
for line in __salt__['cmd.run'](kstat_info).splitlines():
match = re.match(r'(\w+:\d+:\w+\d+:\w+)\s+(.+)', line)
if match:
grains['cpu_model'] = match.group(2)
isainfo = 'isainfo -n -v'
for line in __salt__['cmd.run'](isainfo).splitlines():
match = re.match(r'^\s+(.+)', line)
if match:
cpu_flags = match.group(1).split()
grains['cpu_flags'].extend(cpu_flags)

return grains


def _memdata(osdata):
'''
Gather information about the system memory
'''
# Provides:
# mem_total
grains = {'mem_total': 0}
if osdata['kernel'] == 'Linux':
meminfo = '/proc/meminfo'

if os.path.isfile(meminfo):
with salt.utils.fopen(meminfo, 'r') as ifile:
for line in ifile:
comps = line.rstrip('\n').split(':')
if not len(comps) > 1:
continue
if comps[0].strip() == 'MemTotal':
# Use floor division to force output to be an integer
grains['mem_total'] = int(comps[1].split()[0]) // 1024
elif osdata['kernel'] in ('FreeBSD', 'OpenBSD', 'NetBSD', 'Darwin'):
sysctl = salt.utils.which('sysctl')
if sysctl:
if osdata['kernel'] == 'Darwin':
mem = __salt__['cmd.run']('{0} -n hw.memsize'.format(sysctl))
else:
mem = __salt__['cmd.run']('{0} -n hw.physmem'.format(sysctl))
if osdata['kernel'] == 'NetBSD' and mem.startswith('-'):
mem = __salt__['cmd.run']('{0} -n hw.physmem64'.format(sysctl))
grains['mem_total'] = int(mem) / 1024 / 1024
elif osdata['kernel'] == 'SunOS':
prtconf = '/usr/sbin/prtconf 2>/dev/null'
for line in __salt__['cmd.run'](prtconf, python_shell=True).splitlines():
comps = line.split(' ')
if comps[0].strip() == 'Memory' and comps[1].strip() == 'size:':
grains['mem_total'] = int(comps[2].strip())
elif osdata['kernel'] == 'Windows' and HAS_WMI:
# get the Total Physical memory as reported by msinfo32
tot_bytes = win32api.GlobalMemoryStatusEx()['TotalPhys']
# return memory info in gigabytes
grains['mem_total'] = int(tot_bytes / (1024 ** 2))
return grains


def _windows_virtual(osdata):
'''
Returns what type of virtual hardware is under the hood, kvm or physical
'''
# Provides:
# virtual
# virtual_subtype
grains = dict()
if osdata['kernel'] != 'Windows':
return grains

# It is possible that the 'manufacturer' and/or 'productname' grains
# exist but have a value of None.
manufacturer = osdata.get('manufacturer', '')
if manufacturer is None:
manufacturer = ''
productname = osdata.get('productname', '')
if productname is None:
productname = ''

if 'QEMU' in manufacturer:
# FIXME: Make this detect between kvm or qemu
grains['virtual'] = 'kvm'
if 'Bochs' in manufacturer:
grains['virtual'] = 'kvm'
# Product Name: (oVirt) www.ovirt.org
# Red Hat Community virtualization Project based on kvm
elif 'oVirt' in productname:
grains['virtual'] = 'kvm'
grains['virtual_subtype'] = 'oVirt'
# Red Hat Enterprise Virtualization
elif 'RHEV Hypervisor' in productname:
grains['virtual'] = 'kvm'
grains['virtual_subtype'] = 'rhev'
# Product Name: VirtualBox
elif 'VirtualBox' in productname:
grains['virtual'] = 'VirtualBox'
# Product Name: VMware Virtual Platform
elif 'VMware Virtual Platform' in productname:
grains['virtual'] = 'VMware'
# Manufacturer: Microsoft Corporation
# Product Name: Virtual Machine
elif 'Microsoft' in manufacturer and \
'Virtual Machine' in productname:
grains['virtual'] = 'VirtualPC'
# Manufacturer: Parallels Software International Inc.
elif 'Parallels Software' in manufacturer:
grains['virtual'] = 'Parallels'
# Apache CloudStack
elif 'CloudStack KVM Hypervisor' in productname:
grains['virtual'] = 'kvm'
grains['virtual_subtype'] = 'cloudstack'
return grains


def _virtual(osdata):
'''
Returns what type of virtual hardware is under the hood, kvm or physical
'''
# This is going to be a monster, if you are running a vm you can test this
# grain with please submit patches!
# Provides:
# virtual
# virtual_subtype
grains = {'virtual': 'physical'}

# Skip the below loop on platforms which have none of the desired cmds
# This is a temporary measure until we can write proper virtual hardware
# detection.
skip_cmds = ('AIX',)

# list of commands to be executed to determine the 'virtual' grain
_cmds = ['systemd-detect-virt', 'virt-what', 'dmidecode']
# test first for virt-what, which covers most of the desired functionality
# on most platforms
if not salt.utils.is_windows() and osdata['kernel'] not in skip_cmds:
if salt.utils.which('virt-what'):
_cmds = ['virt-what']
else:
log.debug(
'Please install \'virt-what\' to improve results of the '
'\'virtual\' grain.'
)
# Check if enable_lspci is True or False
if __opts__.get('enable_lspci', True) is False:
# /proc/bus/pci does not exists, lspci will fail
if os.path.exists('/proc/bus/pci'):
_cmds += ['lspci']

# Add additional last resort commands
if osdata['kernel'] in skip_cmds:
_cmds = ()

# Quick backout for BrandZ (Solaris LX Branded zones)
# Don't waste time trying other commands to detect the virtual grain
if osdata['kernel'] == 'Linux' and 'BrandZ virtual linux' in os.uname():
grains['virtual'] = 'zone'
return grains

failed_commands = set()
for command in _cmds:
args = []
if osdata['kernel'] == 'Darwin':
command = 'system_profiler'
args = ['SPDisplaysDataType']
elif osdata['kernel'] == 'SunOS':
command = 'prtdiag'
args = []

cmd = salt.utils.which(command)

if not cmd:
continue

cmd = '{0} {1}'.format(cmd, ' '.join(args))

try:
ret = __salt__['cmd.run_all'](cmd)

if ret['retcode'] > 0:
if salt.log.is_logging_configured():
# systemd-detect-virt always returns > 0 on non-virtualized
# systems
# prtdiag only works in the global zone, skip if it fails
if salt.utils.is_windows() or 'systemd-detect-virt' in cmd or 'prtdiag' in cmd:
continue
failed_commands.add(command)
continue
except salt.exceptions.CommandExecutionError:
if salt.log.is_logging_configured():
if salt.utils.is_windows():
continue
failed_commands.add(command)
continue

output = ret['stdout']
if command == "system_profiler":
macoutput = output.lower()
if '0x1ab8' in macoutput:
grains['virtual'] = 'Parallels'
if 'parallels' in macoutput:
grains['virtual'] = 'Parallels'
if 'vmware' in macoutput:
grains['virtual'] = 'VMware'
if '0x15ad' in macoutput:
grains['virtual'] = 'VMware'
if 'virtualbox' in macoutput:
grains['virtual'] = 'VirtualBox'
# Break out of the loop so the next log message is not issued
break
elif command == 'systemd-detect-virt':
if output in ('qemu', 'kvm', 'oracle', 'xen', 'bochs', 'chroot', 'uml', 'systemd-nspawn'):
grains['virtual'] = output
break
elif 'vmware' in output:
grains['virtual'] = 'VMware'
break
elif 'microsoft' in output:
grains['virtual'] = 'VirtualPC'
break
elif 'lxc' in output:
grains['virtual'] = 'LXC'
break
elif 'systemd-nspawn' in output:
grains['virtual'] = 'LXC'
break
elif command == 'virt-what':
if output in ('kvm', 'qemu', 'uml', 'xen', 'lxc'):
grains['virtual'] = output
break
elif 'vmware' in output:
grains['virtual'] = 'VMware'
break
elif 'parallels' in output:
grains['virtual'] = 'Parallels'
break
elif 'hyperv' in output:
grains['virtual'] = 'HyperV'
break
elif command == 'dmidecode':
# Product Name: VirtualBox
if 'Vendor: QEMU' in output:
# FIXME: Make this detect between kvm or qemu
grains['virtual'] = 'kvm'
if 'Manufacturer: QEMU' in output:
grains['virtual'] = 'kvm'
if 'Vendor: Bochs' in output:
grains['virtual'] = 'kvm'
if 'Manufacturer: Bochs' in output:
grains['virtual'] = 'kvm'
if 'BHYVE' in output:
grains['virtual'] = 'bhyve'
# Product Name: (oVirt) www.ovirt.org
# Red Hat Community virtualization Project based on kvm
elif 'Manufacturer: oVirt' in output:
grains['virtual'] = 'kvm'
grains['virtual_subtype'] = 'ovirt'
# Red Hat Enterprise Virtualization
elif 'Product Name: RHEV Hypervisor' in output:
grains['virtual'] = 'kvm'
grains['virtual_subtype'] = 'rhev'
elif 'VirtualBox' in output:
grains['virtual'] = 'VirtualBox'
# Product Name: VMware Virtual Platform
elif 'VMware' in output:
grains['virtual'] = 'VMware'
# Manufacturer: Microsoft Corporation
# Product Name: Virtual Machine
elif ': Microsoft' in output and 'Virtual Machine' in output:
grains['virtual'] = 'VirtualPC'
# Manufacturer: Parallels Software International Inc.
elif 'Parallels Software' in output:
grains['virtual'] = 'Parallels'
elif 'Manufacturer: Google' in output:
grains['virtual'] = 'kvm'
# Proxmox KVM
elif 'Vendor: SeaBIOS' in output:
grains['virtual'] = 'kvm'
# Break out of the loop, lspci parsing is not necessary
break
elif command == 'lspci':
# dmidecode not available or the user does not have the necessary
# permissions
model = output.lower()
if 'vmware' in model:
grains['virtual'] = 'VMware'
# 00:04.0 System peripheral: InnoTek Systemberatung GmbH
# VirtualBox Guest Service
elif 'virtualbox' in model:
grains['virtual'] = 'VirtualBox'
elif 'qemu' in model:
grains['virtual'] = 'kvm'
elif 'virtio' in model:
grains['virtual'] = 'kvm'
# Break out of the loop so the next log message is not issued
break
elif command == 'virt-what':
# if 'virt-what' returns nothing, it's either an undetected platform
# so we default just as virt-what to 'physical', otherwise use the
# platform detected/returned by virt-what
if output:
grains['virtual'] = output.lower()
break
elif command == 'prtdiag':
model = output.lower().split("\n")[0]
if 'vmware' in model:
grains['virtual'] = 'VMware'
elif 'virtualbox' in model:
grains['virtual'] = 'VirtualBox'
elif 'qemu' in model:
grains['virtual'] = 'kvm'
elif 'joyent smartdc hvm' in model:
grains['virtual'] = 'kvm'
break
else:
if osdata['kernel'] not in skip_cmds:
log.debug(
'All tools for virtual hardware identification failed to '
'execute because they do not exist on the system running this '
'instance or the user does not have the necessary permissions '
'to execute them. Grains output might not be accurate.'
)

choices = ('Linux', 'HP-UX')
isdir = os.path.isdir
sysctl = salt.utils.which('sysctl')
if osdata['kernel'] in choices:
if os.path.isdir('/proc'):
try:
self_root = os.stat('/')
init_root = os.stat('/proc/1/root/.')
if self_root != init_root:
grains['virtual_subtype'] = 'chroot'
except (IOError, OSError):
pass
if os.path.isfile('/proc/1/cgroup'):
try:
with salt.utils.fopen('/proc/1/cgroup', 'r') as fhr:
if ':/lxc/' in fhr.read():
grains['virtual_subtype'] = 'LXC'
with salt.utils.fopen('/proc/1/cgroup', 'r') as fhr:
fhr_contents = fhr.read()
if ':/docker/' in fhr_contents or ':/system.slice/docker' in fhr_contents:
grains['virtual_subtype'] = 'Docker'
except IOError:
pass
if isdir('/proc/vz'):
if os.path.isfile('/proc/vz/version'):
grains['virtual'] = 'openvzhn'
elif os.path.isfile('/proc/vz/veinfo'):
grains['virtual'] = 'openvzve'
# a posteriori, it's expected for these to have failed:
failed_commands.discard('lspci')
failed_commands.discard('dmidecode')
# Provide additional detection for OpenVZ
if os.path.isfile('/proc/self/status'):
with salt.utils.fopen('/proc/self/status') as status_file:
vz_re = re.compile(r'^envID:\s+(\d+)$')
for line in status_file:
vz_match = vz_re.match(line.rstrip('\n'))
if vz_match and int(vz_match.groups()[0]) != 0:
grains['virtual'] = 'openvzve'
elif vz_match and int(vz_match.groups()[0]) == 0:
grains['virtual'] = 'openvzhn'
if isdir('/proc/sys/xen') or \
isdir('/sys/bus/xen') or isdir('/proc/xen'):
if os.path.isfile('/proc/xen/xsd_kva'):
# Tested on CentOS 5.3 / 2.6.18-194.26.1.el5xen
# Tested on CentOS 5.4 / 2.6.18-164.15.1.el5xen
grains['virtual_subtype'] = 'Xen Dom0'
else:
if grains.get('productname', '') == 'HVM domU':
# Requires dmidecode!
grains['virtual_subtype'] = 'Xen HVM DomU'
elif os.path.isfile('/proc/xen/capabilities') and \
os.access('/proc/xen/capabilities', os.R_OK):
with salt.utils.fopen('/proc/xen/capabilities') as fhr:
if 'control_d' not in fhr.read():
# Tested on CentOS 5.5 / 2.6.18-194.3.1.el5xen
grains['virtual_subtype'] = 'Xen PV DomU'
else:
# Shouldn't get to this, but just in case
grains['virtual_subtype'] = 'Xen Dom0'
# Tested on Fedora 10 / 2.6.27.30-170.2.82 with xen
# Tested on Fedora 15 / 2.6.41.4-1 without running xen
elif isdir('/sys/bus/xen'):
if 'xen:' in __salt__['cmd.run']('dmesg').lower():
grains['virtual_subtype'] = 'Xen PV DomU'
elif os.listdir('/sys/bus/xen/drivers'):
# An actual DomU will have several drivers
# whereas a paravirt ops kernel will not.
grains['virtual_subtype'] = 'Xen PV DomU'
# If a Dom0 or DomU was detected, obviously this is xen
if 'dom' in grains.get('virtual_subtype', '').lower():
grains['virtual'] = 'xen'
if os.path.isfile('/proc/cpuinfo'):
with salt.utils.fopen('/proc/cpuinfo', 'r') as fhr:
if 'QEMU Virtual CPU' in fhr.read():
grains['virtual'] = 'kvm'
if os.path.isfile('/sys/devices/virtual/dmi/id/product_name'):
try:
with salt.utils.fopen('/sys/devices/virtual/dmi/id/product_name', 'r') as fhr:
output = fhr.read()
if 'VirtualBox' in output:
grains['virtual'] = 'VirtualBox'
elif 'RHEV Hypervisor' in output:
grains['virtual'] = 'kvm'
grains['virtual_subtype'] = 'rhev'
elif 'oVirt Node' in output:
grains['virtual'] = 'kvm'
grains['virtual_subtype'] = 'ovirt'
elif 'Google' in output:
grains['virtual'] = 'gce'
except IOError:
pass
elif osdata['kernel'] == 'FreeBSD':
kenv = salt.utils.which('kenv')
if kenv:
product = __salt__['cmd.run'](
'{0} smbios.system.product'.format(kenv)
)
maker = __salt__['cmd.run'](
'{0} smbios.system.maker'.format(kenv)
)
if product.startswith('VMware'):
grains['virtual'] = 'VMware'
if product.startswith('VirtualBox'):
grains['virtual'] = 'VirtualBox'
if maker.startswith('Xen'):
grains['virtual_subtype'] = '{0} {1}'.format(maker, product)
grains['virtual'] = 'xen'
if maker.startswith('Microsoft') and product.startswith('Virtual'):
grains['virtual'] = 'VirtualPC'
if maker.startswith('OpenStack'):
grains['virtual'] = 'OpenStack'
if maker.startswith('Bochs'):
grains['virtual'] = 'kvm'
if sysctl:
hv_vendor = __salt__['cmd.run']('{0} hw.hv_vendor'.format(sysctl))
model = __salt__['cmd.run']('{0} hw.model'.format(sysctl))
jail = __salt__['cmd.run'](
'{0} -n security.jail.jailed'.format(sysctl)
)
if 'bhyve' in hv_vendor:
grains['virtual'] = 'bhyve'
if jail == '1':
grains['virtual_subtype'] = 'jail'
if 'QEMU Virtual CPU' in model:
grains['virtual'] = 'kvm'
elif osdata['kernel'] == 'OpenBSD':
if osdata['manufacturer'] == 'QEMU':
grains['virtual'] = 'kvm'
elif osdata['kernel'] == 'SunOS':
# Check if it's a "regular" zone. (i.e. Solaris 10/11 zone)
zonename = salt.utils.which('zonename')
if zonename:
zone = __salt__['cmd.run']('{0}'.format(zonename))
if zone != 'global':
grains['virtual'] = 'zone'
if salt.utils.is_smartos_zone():
grains.update(_smartos_zone_data())
# Check if it's a branded zone (i.e. Solaris 8/9 zone)
if isdir('/.SUNWnative'):
grains['virtual'] = 'zone'
elif osdata['kernel'] == 'NetBSD':
if sysctl:
if 'QEMU Virtual CPU' in __salt__['cmd.run'](
'{0} -n machdep.cpu_brand'.format(sysctl)):
grains['virtual'] = 'kvm'
elif 'invalid' not in __salt__['cmd.run'](
'{0} -n machdep.xen.suspend'.format(sysctl)):
grains['virtual'] = 'Xen PV DomU'
elif 'VMware' in __salt__['cmd.run'](
'{0} -n machdep.dmi.system-vendor'.format(sysctl)):
grains['virtual'] = 'VMware'
# NetBSD has Xen dom0 support
elif __salt__['cmd.run'](
'{0} -n machdep.idle-mechanism'.format(sysctl)) == 'xen':
if os.path.isfile('/var/run/xenconsoled.pid'):
grains['virtual_subtype'] = 'Xen Dom0'

for command in failed_commands:
log.info(
"Although '{0}' was found in path, the current user "
'cannot execute it. Grains output might not be '
'accurate.'.format(command)
)
return grains


def _ps(osdata):
'''
Return the ps grain
'''
grains = {}
bsd_choices = ('FreeBSD', 'NetBSD', 'OpenBSD', 'MacOS')
if osdata['os'] in bsd_choices:
grains['ps'] = 'ps auxwww'
elif osdata['os_family'] == 'Solaris':
grains['ps'] = '/usr/ucb/ps auxwww'
elif osdata['os'] == 'Windows':
grains['ps'] = 'tasklist.exe'
elif osdata.get('virtual', '') == 'openvzhn':
grains['ps'] = (
'ps -fH -p $(grep -l \"^envID:[[:space:]]*0\\$\" '
'/proc/[0-9]*/status | sed -e \"s=/proc/\\([0-9]*\\)/.*=\\1=\") '
'| awk \'{ $7=\"\"; print }\''
)
elif osdata['os_family'] == 'AIX':
grains['ps'] = '/usr/bin/ps auxww'
else:
grains['ps'] = 'ps -efHww'
return grains


def _clean_value(key, val):
'''
Clean out well-known bogus values.
If it isn't clean (for example has value 'None'), return None.
Otherwise, return the original value.

NOTE: This logic also exists in the smbios module. This function is
for use when not using smbios to retrieve the value.
'''
if (val is None or
not len(val) or
re.match('none', val, flags=re.IGNORECASE)):
return None
elif 'uuid' in key:
# Try each version (1-5) of RFC4122 to check if it's actually a UUID
for uuidver in range(1, 5):
try:
uuid.UUID(val, version=uuidver)
return val
except ValueError:
continue
log.trace('HW {0} value {1} is an invalid UUID'.format(key, val.replace('\n', ' ')))
return None
elif re.search('serial|part|version', key):
# 'To be filled by O.E.M.
# 'Not applicable' etc.
# 'Not specified' etc.
# 0000000, 1234567 etc.
# begone!
if (re.match(r'^[0]+$', val) or
re.match(r'[0]?1234567[8]?[9]?[0]?', val) or
re.search(r'sernum|part[_-]?number|specified|filled|applicable', val, flags=re.IGNORECASE)):
return None
elif re.search('asset|manufacturer', key):
# AssetTag0. Manufacturer04. Begone.
if re.search(r'manufacturer|to be filled|available|asset|^no(ne|t)', val, flags=re.IGNORECASE):
return None
else:
# map unspecified, undefined, unknown & whatever to None
if (re.search(r'to be filled', val, flags=re.IGNORECASE) or
re.search(r'un(known|specified)|no(t|ne)? (asset|provided|defined|available|present|specified)',
val, flags=re.IGNORECASE)):
return None
return val


def _windows_platform_data():
'''
Use the platform module for as much as we can.
'''
# Provides:
# kernelrelease
# kernelversion
# osversion
# osrelease
# osservicepack
# osmanufacturer
# manufacturer
# productname
# biosversion
# serialnumber
# osfullname
# timezone
# windowsdomain
# motherboard.productname
# motherboard.serialnumber
# virtual

if not HAS_WMI:
return {}

with salt.utils.winapi.Com():
wmi_c = wmi.WMI()
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394102%28v=vs.85%29.aspx
systeminfo = wmi_c.Win32_ComputerSystem()[0]
# https://msdn.microsoft.com/en-us/library/aa394239(v=vs.85).aspx
osinfo = wmi_c.Win32_OperatingSystem()[0]
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394077(v=vs.85).aspx
biosinfo = wmi_c.Win32_BIOS()[0]
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394498(v=vs.85).aspx
timeinfo = wmi_c.Win32_TimeZone()[0]

# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394072(v=vs.85).aspx
motherboard = {'product': None,
'serial': None}
try:
motherboardinfo = wmi_c.Win32_BaseBoard()[0]
motherboard['product'] = motherboardinfo.Product
motherboard['serial'] = motherboardinfo.SerialNumber
except IndexError:
log.debug('Motherboard info not available on this system')

os_release = platform.release()
kernel_version = platform.version()
info = salt.utils.win_osinfo.get_os_version_info()

# Starting with Python 2.7.12 and 3.5.2 the `platform.uname()` function
# started reporting the Desktop version instead of the Server version on
# Server versions of Windows, so we need to look those up
# Check for Python >=2.7.12 or >=3.5.2
ver = pythonversion()['pythonversion']
if ((six.PY2 and
salt.utils.compare_versions(ver, '>=', [2, 7, 12, 'final', 0]))
or
(six.PY3 and
salt.utils.compare_versions(ver, '>=', [3, 5, 2, 'final', 0]))):
# (Product Type 1 is Desktop, Everything else is Server)
if info['ProductType'] > 1:
server = {'Vista': '2008Server',
'7': '2008ServerR2',
'8': '2012Server',
'8.1': '2012ServerR2',
'10': '2016Server'}
os_release = server.get(os_release,
'Grain not found. Update lookup table '
'in the `_windows_platform_data` '
'function in `grains\\core.py`')

service_pack = None
if info['ServicePackMajor'] > 0:
service_pack = ''.join(['SP', str(info['ServicePackMajor'])])

grains = {
'kernelrelease': _clean_value('kernelrelease', osinfo.Version),
'kernelversion': _clean_value('kernelversion', kernel_version),
'osversion': _clean_value('osversion', osinfo.Version),
'osrelease': _clean_value('osrelease', os_release),
'osservicepack': _clean_value('osservicepack', service_pack),
'osmanufacturer': _clean_value('osmanufacturer', osinfo.Manufacturer),
'manufacturer': _clean_value('manufacturer', systeminfo.Manufacturer),
'productname': _clean_value('productname', systeminfo.Model),
# bios name had a bunch of whitespace appended to it in my testing
# 'PhoenixBIOS 4.0 Release 6.0 '
'biosversion': _clean_value('biosversion', biosinfo.Name.strip()),
'serialnumber': _clean_value('serialnumber', biosinfo.SerialNumber),
'osfullname': _clean_value('osfullname', osinfo.Caption),
'timezone': _clean_value('timezone', timeinfo.Description),
'windowsdomain': _clean_value('windowsdomain', systeminfo.Domain),
'motherboard': {
'productname': _clean_value('motherboard.productname', motherboard['product']),
'serialnumber': _clean_value('motherboard.serialnumber', motherboard['serial']),
}
}

# test for virtualized environments
# I only had VMware available so the rest are unvalidated
if 'VRTUAL' in biosinfo.Version: # (not a typo)
grains['virtual'] = 'HyperV'
elif 'A M I' in biosinfo.Version:
grains['virtual'] = 'VirtualPC'
elif 'VMware' in systeminfo.Model:
grains['virtual'] = 'VMware'
elif 'VirtualBox' in systeminfo.Model:
grains['virtual'] = 'VirtualBox'
elif 'Xen' in biosinfo.Version:
grains['virtual'] = 'Xen'
if 'HVM domU' in systeminfo.Model:
grains['virtual_subtype'] = 'HVM domU'
elif 'OpenStack' in systeminfo.Model:
grains['virtual'] = 'OpenStack'

return grains


def _osx_platform_data():
'''
Additional data for macOS systems
Returns: A dictionary containing values for the following:
- model_name
- boot_rom_version
- smc_version
- system_serialnumber
'''
cmd = 'system_profiler SPHardwareDataType'
hardware = __salt__['cmd.run'](cmd)

grains = {}
for line in hardware.splitlines():
field_name, _, field_val = line.partition(': ')
if field_name.strip() == "Model Name":
key = 'model_name'
grains[key] = _clean_value(key, field_val)
if field_name.strip() == "Boot ROM Version":
key = 'boot_rom_version'
grains[key] = _clean_value(key, field_val)
if field_name.strip() == "SMC Version (system)":
key = 'smc_version'
grains[key] = _clean_value(key, field_val)
if field_name.strip() == "Serial Number (system)":
key = 'system_serialnumber'
grains[key] = _clean_value(key, field_val)

return grains


def id_():
'''
Return the id
'''
return {'id': __opts__.get('id', '')}

_REPLACE_LINUX_RE = re.compile(r'\W(?:gnu/)?linux', re.IGNORECASE)

# This maps (at most) the first ten characters (no spaces, lowercased) of
# 'osfullname' to the 'os' grain that Salt traditionally uses.
# Please see os_data() and _supported_dists.
# If your system is not detecting properly it likely needs an entry here.
_OS_NAME_MAP = {
'redhatente': 'RedHat',
'gentoobase': 'Gentoo',
'archarm': 'Arch ARM',
'arch': 'Arch',
'debian': 'Debian',
'raspbian': 'Raspbian',
'fedoraremi': 'Fedora',
'chapeau': 'Chapeau',
'korora': 'Korora',
'amazonami': 'Amazon',
'alt': 'ALT',
'enterprise': 'OEL',
'oracleserv': 'OEL',
'cloudserve': 'CloudLinux',
'cloudlinux': 'CloudLinux',
'pidora': 'Fedora',
'scientific': 'ScientificLinux',
'synology': 'Synology',
'nilrt': 'NILinuxRT',
'nilrt-xfce': 'NILinuxRT-XFCE',
'manjaro': 'Manjaro',
'antergos': 'Antergos',
'sles': 'SUSE',
'slesexpand': 'RES',
'void': 'Void',
'linuxmint': 'Mint',
'neon': 'KDE neon',
}

# Map the 'os' grain to the 'os_family' grain
# These should always be capitalized entries as the lookup comes
# post-_OS_NAME_MAP. If your system is having trouble with detection, please
# make sure that the 'os' grain is capitalized and working correctly first.
_OS_FAMILY_MAP = {
'Ubuntu': 'Debian',
'Fedora': 'RedHat',
'Chapeau': 'RedHat',
'Korora': 'RedHat',
'FedBerry': 'RedHat',
'CentOS': 'RedHat',
'GoOSe': 'RedHat',
'Scientific': 'RedHat',
'Amazon': 'RedHat',
'CloudLinux': 'RedHat',
'OVS': 'RedHat',
'OEL': 'RedHat',
'XCP': 'RedHat',
'XenServer': 'RedHat',
'RES': 'RedHat',
'Sangoma': 'RedHat',
'Mandrake': 'Mandriva',
'ESXi': 'VMware',
'Mint': 'Debian',
'VMwareESX': 'VMware',
'Bluewhite64': 'Bluewhite',
'Slamd64': 'Slackware',
'SLES': 'Suse',
'SUSE Enterprise Server': 'Suse',
'SUSE Enterprise Server': 'Suse',
'SLED': 'Suse',
'openSUSE': 'Suse',
'SUSE': 'Suse',
'openSUSE Leap': 'Suse',
'openSUSE Tumbleweed': 'Suse',
'SLES_SAP': 'Suse',
'Solaris': 'Solaris',
'SmartOS': 'Solaris',
'OmniOS': 'Solaris',
'OpenIndiana Development': 'Solaris',
'OpenIndiana': 'Solaris',
'OpenSolaris Development': 'Solaris',
'OpenSolaris': 'Solaris',
'Oracle Solaris': 'Solaris',
'Arch ARM': 'Arch',
'Manjaro': 'Arch',
'Antergos': 'Arch',
'ALT': 'RedHat',
'Trisquel': 'Debian',
'GCEL': 'Debian',
'Linaro': 'Debian',
'elementary OS': 'Debian',
'ScientificLinux': 'RedHat',
'Raspbian': 'Debian',
'Devuan': 'Debian',
'antiX': 'Debian',
'NILinuxRT': 'NILinuxRT',
'NILinuxRT-XFCE': 'NILinuxRT',
'KDE neon': 'Debian',
'Void': 'Void',
}


def _linux_bin_exists(binary):
'''
Does a binary exist in linux (depends on which, type, or whereis)
'''
for search_cmd in ('which', 'type -ap'):
try:
return __salt__['cmd.retcode'](
'{0} {1}'.format(search_cmd, binary)
) == 0
except salt.exceptions.CommandExecutionError:
pass

try:
return len(__salt__['cmd.run_all'](
'whereis -b {0}'.format(binary)
)['stdout'].split()) > 1
except salt.exceptions.CommandExecutionError:
return False


def _get_interfaces():
'''
Provide a dict of the connected interfaces and their ip addresses
'''

global _INTERFACES
if not _INTERFACES:
_INTERFACES = salt.utils.network.interfaces()
return _INTERFACES


def _parse_os_release():
'''
Parse /etc/os-release and return a parameter dictionary

See http://www.freedesktop.org/software/systemd/man/os-release.html
for specification of the file format.
'''

filename = '/etc/os-release'
if not os.path.isfile(filename):
filename = '/usr/lib/os-release'

data = dict()
with salt.utils.fopen(filename) as ifile:
regex = re.compile('^([\\w]+)=(?:\'|")?(.*?)(?:\'|")?$')
for line in ifile:
match = regex.match(line.strip())
if match:
# Shell special characters ("$", quotes, backslash, backtick)
# are escaped with backslashes
data[match.group(1)] = re.sub(r'\\([$"\'\\`])', r'\1', match.group(2))

return data


def os_data():
'''
Return grains pertaining to the operating system
'''
grains = {
'num_gpus': 0,
'gpus': [],
}

# Windows Server 2008 64-bit
# ('Windows', 'MINIONNAME', '2008ServerR2', '6.1.7601', 'AMD64',
# 'Intel64 Fam ily 6 Model 23 Stepping 6, GenuineIntel')
# Ubuntu 10.04
# ('Linux', 'MINIONNAME', '2.6.32-38-server',
# '#83-Ubuntu SMP Wed Jan 4 11:26:59 UTC 2012', 'x86_64', '')

# pylint: disable=unpacking-non-sequence
(grains['kernel'], grains['nodename'],
grains['kernelrelease'], grains['kernelversion'], grains['cpuarch'], _) = platform.uname()
# pylint: enable=unpacking-non-sequence

if salt.utils.is_proxy():
grains['kernel'] = 'proxy'
grains['kernelrelease'] = 'proxy'
grains['kernelversion'] = 'proxy'
grains['osrelease'] = 'proxy'
grains['os'] = 'proxy'
grains['os_family'] = 'proxy'
grains['osfullname'] = 'proxy'
elif salt.utils.is_windows():
grains['os'] = 'Windows'
grains['os_family'] = 'Windows'
grains.update(_memdata(grains))
grains.update(_windows_platform_data())
grains.update(_windows_cpudata())
grains.update(_windows_virtual(grains))
grains.update(_ps(grains))

if 'Server' in grains['osrelease']:
osrelease_info = grains['osrelease'].split('Server', 1)
osrelease_info[1] = osrelease_info[1].lstrip('R')
else:
osrelease_info = grains['osrelease'].split('.')

for idx, value in enumerate(osrelease_info):
if not value.isdigit():
continue
osrelease_info[idx] = int(value)
grains['osrelease_info'] = tuple(osrelease_info)

grains['osfinger'] = '{os}-{ver}'.format(
os=grains['os'],
ver=grains['osrelease'])

grains['init'] = 'Windows'

return grains
elif salt.utils.is_linux():
# Add SELinux grain, if you have it
if _linux_bin_exists('selinuxenabled'):
grains['selinux'] = {}
grains['selinux']['enabled'] = __salt__['cmd.retcode'](
'selinuxenabled'
) == 0
if _linux_bin_exists('getenforce'):
grains['selinux']['enforced'] = __salt__['cmd.run'](
'getenforce'
).strip()

# Add systemd grain, if you have it
if _linux_bin_exists('systemctl') and _linux_bin_exists('localectl'):
grains['systemd'] = {}
systemd_info = __salt__['cmd.run'](
'systemctl --version'
).splitlines()
grains['systemd']['version'] = systemd_info[0].split()[1]
grains['systemd']['features'] = systemd_info[1]

# Add init grain
grains['init'] = 'unknown'
try:
os.stat('/run/systemd/system')
grains['init'] = 'systemd'
except (OSError, IOError):
if os.path.exists('/proc/1/cmdline'):
with salt.utils.fopen('/proc/1/cmdline') as fhr:
init_cmdline = fhr.read().replace('\x00', ' ').split()
try:
init_bin = salt.utils.which(init_cmdline[0])
except IndexError:
# Emtpy init_cmdline
init_bin = None
log.warning(
"Unable to fetch data from /proc/1/cmdline"
)
if init_bin is not None and init_bin.endswith('bin/init'):
supported_inits = (six.b('upstart'), six.b('sysvinit'), six.b('systemd'))
edge_len = max(len(x) for x in supported_inits) - 1
try:
buf_size = __opts__['file_buffer_size']
except KeyError:
# Default to the value of file_buffer_size for the minion
buf_size = 262144
try:
with salt.utils.fopen(init_bin, 'rb') as fp_:
buf = True
edge = six.b('')
buf = fp_.read(buf_size).lower()
while buf:
buf = edge + buf
for item in supported_inits:
if item in buf:
if six.PY3:
item = item.decode('utf-8')
grains['init'] = item
buf = six.b('')
break
edge = buf[-edge_len:]
buf = fp_.read(buf_size).lower()
except (IOError, OSError) as exc:
log.error(
'Unable to read from init_bin ({0}): {1}'
.format(init_bin, exc)
)
elif salt.utils.which('supervisord') in init_cmdline:
grains['init'] = 'supervisord'
elif init_cmdline == ['runit']:
grains['init'] = 'runit'
else:
log.info(
'Could not determine init system from command line: ({0})'
.format(' '.join(init_cmdline))
)

# Add lsb grains on any distro with lsb-release
try:
import lsb_release # pylint: disable=import-error
release = lsb_release.get_distro_information()
for key, value in six.iteritems(release):
key = key.lower()
lsb_param = 'lsb_{0}{1}'.format(
'' if key.startswith('distrib_') else 'distrib_',
key
)
grains[lsb_param] = value
# Catch a NameError to workaround possible breakage in lsb_release
# See https://github.com/saltstack/salt/issues/37867
except (ImportError, NameError):
# if the python library isn't available, default to regex
if os.path.isfile('/etc/lsb-release'):
# Matches any possible format:
# DISTRIB_ID="Ubuntu"
# DISTRIB_ID='Mageia'
# DISTRIB_ID=Fedora
# DISTRIB_RELEASE='10.10'
# DISTRIB_CODENAME='squeeze'
# DISTRIB_DESCRIPTION='Ubuntu 10.10'
regex = re.compile((
'^(DISTRIB_(?:ID|RELEASE|CODENAME|DESCRIPTION))=(?:\'|")?'
'([\\w\\s\\.\\-_]+)(?:\'|")?'
))
with salt.utils.fopen('/etc/lsb-release') as ifile:
for line in ifile:
match = regex.match(line.rstrip('\n'))
if match:
# Adds:
# lsb_distrib_{id,release,codename,description}
grains[
'lsb_{0}'.format(match.groups()[0].lower())
] = match.groups()[1].rstrip()
if grains.get('lsb_distrib_description', '').lower().startswith('antergos'):
# Antergos incorrectly configures their /etc/lsb-release,
# setting the DISTRIB_ID to "Arch". This causes the "os" grain
# to be incorrectly set to "Arch".
grains['osfullname'] = 'Antergos Linux'
elif 'lsb_distrib_id' not in grains:
if os.path.isfile('/etc/os-release') or os.path.isfile('/usr/lib/os-release'):
os_release = _parse_os_release()
if 'NAME' in os_release:
grains['lsb_distrib_id'] = os_release['NAME'].strip()
if 'VERSION_ID' in os_release:
grains['lsb_distrib_release'] = os_release['VERSION_ID']
if 'PRETTY_NAME' in os_release:
grains['lsb_distrib_codename'] = os_release['PRETTY_NAME']
if 'CPE_NAME' in os_release:
if ":suse:" in os_release['CPE_NAME'] or ":opensuse:" in os_release['CPE_NAME']:
grains['os'] = "SUSE"
# openSUSE `osfullname` grain normalization
if os_release.get("NAME") == "openSUSE Leap":
grains['osfullname'] = "Leap"
elif os_release.get("VERSION") == "Tumbleweed":
grains['osfullname'] = os_release["VERSION"]
elif os.path.isfile('/etc/SuSE-release'):
grains['lsb_distrib_id'] = 'SUSE'
version = ''
patch = ''
with salt.utils.fopen('/etc/SuSE-release') as fhr:
for line in fhr:
if 'enterprise' in line.lower():
grains['lsb_distrib_id'] = 'SLES'
grains['lsb_distrib_codename'] = re.sub(r'\(.+\)', '', line).strip()
elif 'version' in line.lower():
version = re.sub(r'[^0-9]', '', line)
elif 'patchlevel' in line.lower():
patch = re.sub(r'[^0-9]', '', line)
grains['lsb_distrib_release'] = version
if patch:
grains['lsb_distrib_release'] += '.' + patch
patchstr = 'SP' + patch
if grains['lsb_distrib_codename'] and patchstr not in grains['lsb_distrib_codename']:
grains['lsb_distrib_codename'] += ' ' + patchstr
if not grains.get('lsb_distrib_codename'):
grains['lsb_distrib_codename'] = 'n.a'
elif os.path.isfile('/etc/altlinux-release'):
# ALT Linux
grains['lsb_distrib_id'] = 'altlinux'
with salt.utils.fopen('/etc/altlinux-release') as ifile:
# This file is symlinked to from:
# /etc/fedora-release
# /etc/redhat-release
# /etc/system-release
for line in ifile:
# ALT Linux Sisyphus (unstable)
comps = line.split()
if comps[0] == 'ALT':
grains['lsb_distrib_release'] = comps[2]
grains['lsb_distrib_codename'] = \
comps[3].replace('(', '').replace(')', '')
elif os.path.isfile('/etc/centos-release'):
# CentOS Linux
grains['lsb_distrib_id'] = 'CentOS'
with salt.utils.fopen('/etc/centos-release') as ifile:
for line in ifile:
# Need to pull out the version and codename
# in the case of custom content in /etc/centos-release
find_release = re.compile(r'\d+\.\d+')
find_codename = re.compile(r'(?<=\()(.*?)(?=\))')
release = find_release.search(line)
codename = find_codename.search(line)
if release is not None:
grains['lsb_distrib_release'] = release.group()
if codename is not None:
grains['lsb_distrib_codename'] = codename.group()
elif os.path.isfile('/etc.defaults/VERSION') \
and os.path.isfile('/etc.defaults/synoinfo.conf'):
grains['osfullname'] = 'Synology'
with salt.utils.fopen('/etc.defaults/VERSION', 'r') as fp_:
synoinfo = {}
for line in fp_:
try:
key, val = line.rstrip('\n').split('=')
except ValueError:
continue
if key in ('majorversion', 'minorversion',
'buildnumber'):
synoinfo[key] = val.strip('"')
if len(synoinfo) != 3:
log.warning(
'Unable to determine Synology version info. '
'Please report this, as it is likely a bug.'
)
else:
grains['osrelease'] = (
'{majorversion}.{minorversion}-{buildnumber}'
.format(**synoinfo)
)

# Use the already intelligent platform module to get distro info
# (though apparently it's not intelligent enough to strip quotes)
(osname, osrelease, oscodename) = \
[x.strip('"').strip("'") for x in
linux_distribution(supported_dists=_supported_dists)]
# Try to assign these three names based on the lsb info, they tend to
# be more accurate than what python gets from /etc/DISTRO-release.
# It's worth noting that Ubuntu has patched their Python distribution
# so that linux_distribution() does the /etc/lsb-release parsing, but
# we do it anyway here for the sake for full portability.
if 'osfullname' not in grains:
grains['osfullname'] = \
grains.get('lsb_distrib_id', osname).strip()
if 'osrelease' not in grains:
# NOTE: This is a workaround for CentOS 7 os-release bug
# https://bugs.centos.org/view.php?id=8359
# /etc/os-release contains no minor distro release number so we fall back to parse
# /etc/centos-release file instead.
# Commit introducing this comment should be reverted after the upstream bug is released.
if 'CentOS Linux 7' in grains.get('lsb_distrib_codename', ''):
grains.pop('lsb_distrib_release', None)
grains['osrelease'] = \
grains.get('lsb_distrib_release', osrelease).strip()
grains['oscodename'] = grains.get('lsb_distrib_codename', '').strip() or oscodename
if 'Red Hat' in grains['oscodename']:
grains['oscodename'] = oscodename
distroname = _REPLACE_LINUX_RE.sub('', grains['osfullname']).strip()
# return the first ten characters with no spaces, lowercased
shortname = distroname.replace(' ', '').lower()[:10]
# this maps the long names from the /etc/DISTRO-release files to the
# traditional short names that Salt has used.
if 'os' not in grains:
grains['os'] = _OS_NAME_MAP.get(shortname, distroname)
grains.update(_linux_cpudata())
grains.update(_linux_gpu_data())
elif grains['kernel'] == 'SunOS':
if salt.utils.is_smartos():
# See https://github.com/joyent/smartos-live/issues/224
uname_v = os.uname()[3] # format: joyent_20161101T004406Z
uname_v = uname_v[uname_v.index('_')+1:]
grains['os'] = grains['osfullname'] = 'SmartOS'
# store a parsed version of YYYY.MM.DD as osrelease
grains['osrelease'] = ".".join([
uname_v.split('T')[0][0:4],
uname_v.split('T')[0][4:6],
uname_v.split('T')[0][6:8],
])
# store a untouched copy of the timestamp in osrelease_stamp
grains['osrelease_stamp'] = uname_v
if salt.utils.is_smartos_globalzone():
grains.update(_smartos_computenode_data())
elif os.path.isfile('/etc/release'):
with salt.utils.fopen('/etc/release', 'r') as fp_:
rel_data = fp_.read()
try:
release_re = re.compile(
r'((?:Open|Oracle )?Solaris|OpenIndiana|OmniOS) (Development)?'
r'\s*(\d+\.?\d*|v\d+)\s?[A-Z]*\s?(r\d+|\d+\/\d+|oi_\S+|snv_\S+)?'
)
osname, development, osmajorrelease, osminorrelease = \
release_re.search(rel_data).groups()
except AttributeError:
# Set a blank osrelease grain and fallback to 'Solaris'
# as the 'os' grain.
grains['os'] = grains['osfullname'] = 'Solaris'
grains['osrelease'] = ''
else:
if development is not None:
osname = ' '.join((osname, development))
uname_v = os.uname()[3]
grains['os'] = grains['osfullname'] = osname
if osname in ['Oracle Solaris'] and uname_v.startswith(osmajorrelease):
# Oracla Solars 11 and up have minor version in uname
grains['osrelease'] = uname_v
elif osname in ['OmniOS']:
# OmniOS
osrelease = []
osrelease.append(osmajorrelease[1:])
osrelease.append(osminorrelease[1:])
grains['osrelease'] = ".".join(osrelease)
grains['osrelease_stamp'] = uname_v
else:
# Sun Solaris 10 and earlier/comparable
osrelease = []
osrelease.append(osmajorrelease)
if osminorrelease:
osrelease.append(osminorrelease)
grains['osrelease'] = ".".join(osrelease)
grains['osrelease_stamp'] = uname_v

grains.update(_sunos_cpudata())
elif grains['kernel'] == 'VMkernel':
grains['os'] = 'ESXi'
elif grains['kernel'] == 'Darwin':
osrelease = __salt__['cmd.run']('sw_vers -productVersion')
osname = __salt__['cmd.run']('sw_vers -productName')
osbuild = __salt__['cmd.run']('sw_vers -buildVersion')
grains['os'] = 'MacOS'
grains['os_family'] = 'MacOS'
grains['osfullname'] = "{0} {1}".format(osname, osrelease)
grains['osrelease'] = osrelease
grains['osbuild'] = osbuild
grains['init'] = 'launchd'
grains.update(_bsd_cpudata(grains))
grains.update(_osx_gpudata())
grains.update(_osx_platform_data())
else:
grains['os'] = grains['kernel']
if grains['kernel'] == 'FreeBSD':
try:
grains['osrelease'] = __salt__['cmd.run']('freebsd-version -u').split('-')[0]
except salt.exceptions.CommandExecutionError:
# freebsd-version was introduced in 10.0.
# derive osrelease from kernelversion prior to that
grains['osrelease'] = grains['kernelrelease'].split('-')[0]
grains.update(_bsd_cpudata(grains))
if grains['kernel'] in ('OpenBSD', 'NetBSD'):
grains.update(_bsd_cpudata(grains))
grains['osrelease'] = grains['kernelrelease'].split('-')[0]
if grains['kernel'] == 'NetBSD':
grains.update(_netbsd_gpu_data())
if not grains['os']:
grains['os'] = 'Unknown {0}'.format(grains['kernel'])
grains['os_family'] = 'Unknown'
else:
# this assigns family names based on the os name
# family defaults to the os name if not found
grains['os_family'] = _OS_FAMILY_MAP.get(grains['os'],
grains['os'])

# Build the osarch grain. This grain will be used for platform-specific
# considerations such as package management. Fall back to the CPU
# architecture.
if grains.get('os_family') == 'Debian':
osarch = __salt__['cmd.run']('dpkg --print-architecture').strip()
elif grains.get('os_family') == 'RedHat':
osarch = __salt__['cmd.run']('rpm --eval %{_host_cpu}').strip()
elif grains.get('os_family') == 'NILinuxRT':
archinfo = {}
for line in __salt__['cmd.run']('opkg print-architecture').splitlines():
if line.startswith('arch'):
_, arch, priority = line.split()
archinfo[arch.strip()] = int(priority.strip())

# Return osarch in priority order (higher to lower)
osarch = sorted(archinfo, key=archinfo.get, reverse=True)
else:
osarch = grains['cpuarch']
grains['osarch'] = osarch

grains.update(_memdata(grains))

# Get the hardware and bios data
grains.update(_hw_data(grains))

# Get zpool data
grains.update(_zpool_data(grains))

# Load the virtual machine info
grains.update(_virtual(grains))
grains.update(_ps(grains))

if grains.get('osrelease', ''):
osrelease_info = grains['osrelease'].split('.')
for idx, value in enumerate(osrelease_info):
if not value.isdigit():
continue
osrelease_info[idx] = int(value)
grains['osrelease_info'] = tuple(osrelease_info)
try:
grains['osmajorrelease'] = int(grains['osrelease_info'][0])
except (IndexError, TypeError, ValueError):
log.debug(
'Unable to derive osmajorrelease from osrelease_info \'%s\'. '
'The osmajorrelease grain will not be set.',
grains['osrelease_info']
)
os_name = grains['os' if grains.get('os') in (
'FreeBSD', 'OpenBSD', 'NetBSD', 'Mac', 'Raspbian') else 'osfullname']
grains['osfinger'] = '{0}-{1}'.format(
os_name, grains['osrelease'] if os_name in ('Ubuntu',) else grains['osrelease_info'][0])

return grains


def locale_info():
'''
Provides
defaultlanguage
defaultencoding
'''
grains = {}
grains['locale_info'] = {}

if salt.utils.is_proxy():
return grains

try:
(
grains['locale_info']['defaultlanguage'],
grains['locale_info']['defaultencoding']
) = locale.getdefaultlocale()
except Exception:
# locale.getdefaultlocale can ValueError!! Catch anything else it
# might do, per #2205
grains['locale_info']['defaultlanguage'] = 'unknown'
grains['locale_info']['defaultencoding'] = 'unknown'
grains['locale_info']['detectedencoding'] = __salt_system_encoding__
return grains


def hostname():
'''
Return fqdn, hostname, domainname
'''
# This is going to need some work
# Provides:
# fqdn
# host
# localhost
# domain
global __FQDN__
grains = {}

if salt.utils.is_proxy():
return grains

grains['localhost'] = socket.gethostname()
if __FQDN__ is None:
__FQDN__ = salt.utils.network.get_fqhostname()

# On some distros (notably FreeBSD) if there is no hostname set
# salt.utils.network.get_fqhostname() will return None.
# In this case we punt and log a message at error level, but force the
# hostname and domain to be localhost.localdomain
# Otherwise we would stacktrace below
if __FQDN__ is None: # still!
log.error('Having trouble getting a hostname. Does this machine have its hostname and domain set properly?')
__FQDN__ = 'localhost.localdomain'

grains['fqdn'] = __FQDN__
(grains['host'], grains['domain']) = grains['fqdn'].partition('.')[::2]
return grains


def append_domain():
'''
Return append_domain if set
'''

grain = {}

if salt.utils.is_proxy():
return grain

if 'append_domain' in __opts__:
grain['append_domain'] = __opts__['append_domain']
return grain


def ip_fqdn():
'''
Return ip address and FQDN grains
'''
if salt.utils.is_proxy():
return {}

ret = {}
ret['ipv4'] = salt.utils.network.ip_addrs(include_loopback=True)
ret['ipv6'] = salt.utils.network.ip_addrs6(include_loopback=True)

_fqdn = hostname()['fqdn']
for socket_type, ipv_num in ((socket.AF_INET, '4'), (socket.AF_INET6, '6')):
key = 'fqdn_ip' + ipv_num
if not ret['ipv' + ipv_num]:
ret[key] = []
else:
try:
info = socket.getaddrinfo(_fqdn, None, socket_type)
ret[key] = list(set(item[4][0] for item in info))
except socket.error:
if __opts__['__role'] == 'master':
log.warning('Unable to find IPv{0} record for "{1}" causing a 10 second timeout when rendering grains. '
'Set the dns or /etc/hosts for IPv{0} to clear this.'.format(ipv_num, _fqdn))
ret[key] = []

return ret


def ip_interfaces():
'''
Provide a dict of the connected interfaces and their ip addresses
The addresses will be passed as a list for each interface
'''
# Provides:
# ip_interfaces

if salt.utils.is_proxy():
return {}

ret = {}
ifaces = _get_interfaces()
for face in ifaces:
iface_ips = []
for inet in ifaces[face].get('inet', []):
if 'address' in inet:
iface_ips.append(inet['address'])
for inet in ifaces[face].get('inet6', []):
if 'address' in inet:
iface_ips.append(inet['address'])
for secondary in ifaces[face].get('secondary', []):
if 'address' in secondary:
iface_ips.append(secondary['address'])
ret[face] = iface_ips
return {'ip_interfaces': ret}


def ip4_interfaces():
'''
Provide a dict of the connected interfaces and their ip4 addresses
The addresses will be passed as a list for each interface
'''
# Provides:
# ip_interfaces

if salt.utils.is_proxy():
return {}

ret = {}
ifaces = _get_interfaces()
for face in ifaces:
iface_ips = []
for inet in ifaces[face].get('inet', []):
if 'address' in inet:
iface_ips.append(inet['address'])
for secondary in ifaces[face].get('secondary', []):
if 'address' in secondary:
iface_ips.append(secondary['address'])
ret[face] = iface_ips
return {'ip4_interfaces': ret}


def ip6_interfaces():
'''
Provide a dict of the connected interfaces and their ip6 addresses
The addresses will be passed as a list for each interface
'''
# Provides:
# ip_interfaces

if salt.utils.is_proxy():
return {}

ret = {}
ifaces = _get_interfaces()
for face in ifaces:
iface_ips = []
for inet in ifaces[face].get('inet6', []):
if 'address' in inet:
iface_ips.append(inet['address'])
for secondary in ifaces[face].get('secondary', []):
if 'address' in secondary:
iface_ips.append(secondary['address'])
ret[face] = iface_ips
return {'ip6_interfaces': ret}


def hwaddr_interfaces():
'''
Provide a dict of the connected interfaces and their
hw addresses (Mac Address)
'''
# Provides:
# hwaddr_interfaces
ret = {}
ifaces = _get_interfaces()
for face in ifaces:
if 'hwaddr' in ifaces[face]:
ret[face] = ifaces[face]['hwaddr']
return {'hwaddr_interfaces': ret}


def dns():
'''
Parse the resolver configuration file

.. versionadded:: 2016.3.0
'''
# Provides:
# dns
if salt.utils.is_windows() or 'proxyminion' in __opts__:
return {}

resolv = salt.utils.dns.parse_resolv()
for key in ('nameservers', 'ip4_nameservers', 'ip6_nameservers',
'sortlist'):
if key in resolv:
resolv[key] = [str(i) for i in resolv[key]]

return {'dns': resolv} if resolv else {}


def get_machine_id():
'''
Provide the machine-id
'''
# Provides:
# machine-id
locations = ['/etc/machine-id', '/var/lib/dbus/machine-id']
existing_locations = [loc for loc in locations if os.path.exists(loc)]
if not existing_locations:
return {}
else:
with salt.utils.fopen(existing_locations[0]) as machineid:
return {'machine_id': machineid.read().strip()}


def path():
'''
Return the path
'''
# Provides:
# path
return {'path': os.environ.get('PATH', '').strip()}


def pythonversion():
'''
Return the Python version
'''
# Provides:
# pythonversion
return {'pythonversion': list(sys.version_info)}


def pythonpath():
'''
Return the Python path
'''
# Provides:
# pythonpath
return {'pythonpath': sys.path}


def pythonexecutable():
'''
Return the python executable in use
'''
# Provides:
# pythonexecutable
return {'pythonexecutable': sys.executable}


def saltpath():
'''
Return the path of the salt module
'''
# Provides:
# saltpath
salt_path = os.path.abspath(os.path.join(__file__, os.path.pardir))
return {'saltpath': os.path.dirname(salt_path)}


def saltversion():
'''
Return the version of salt
'''
# Provides:
# saltversion
from salt.version import __version__
return {'saltversion': __version__}


def zmqversion():
'''
Return the zeromq version
'''
# Provides:
# zmqversion
try:
import zmq
return {'zmqversion': zmq.zmq_version()} # pylint: disable=no-member
except ImportError:
return {}


def saltversioninfo():
'''
Return the version_info of salt

.. versionadded:: 0.17.0
'''
# Provides:
# saltversioninfo
from salt.version import __version_info__
return {'saltversioninfo': list(__version_info__)}


def _hw_data(osdata):
'''
Get system specific hardware data from dmidecode

Provides
biosversion
productname
manufacturer
serialnumber
biosreleasedate
uuid

.. versionadded:: 0.9.5
'''

if salt.utils.is_proxy():
return {}

grains = {}
if osdata['kernel'] == 'Linux' and os.path.exists('/sys/class/dmi/id'):
# On many Linux distributions basic firmware information is available via sysfs
# requires CONFIG_DMIID to be enabled in the Linux kernel configuration
sysfs_firmware_info = {
'biosversion': 'bios_version',
'productname': 'product_name',
'manufacturer': 'sys_vendor',
'biosreleasedate': 'bios_date',
'uuid': 'product_uuid',
'serialnumber': 'product_serial'
}
for key, fw_file in sysfs_firmware_info.items():
contents_file = os.path.join('/sys/class/dmi/id', fw_file)
if os.path.exists(contents_file):
try:
with salt.utils.fopen(contents_file, 'r') as ifile:
grains[key] = ifile.read()
if key == 'uuid':
grains['uuid'] = grains['uuid'].lower()
except (IOError, OSError) as err:
# PermissionError is new to Python 3, but corresponds to the EACESS and
# EPERM error numbers. Use those instead here for PY2 compatibility.
if err.errno == EACCES or err.errno == EPERM:
# Skip the grain if non-root user has no access to the file.
pass
elif salt.utils.which_bin(['dmidecode', 'smbios']) is not None and not (
salt.utils.is_smartos() or
( # SunOS on SPARC - 'smbios: failed to load SMBIOS: System does not export an SMBIOS table'
osdata['kernel'] == 'SunOS' and
osdata['cpuarch'].startswith('sparc')
)):
# On SmartOS (possibly SunOS also) smbios only works in the global zone
# smbios is also not compatible with linux's smbios (smbios -s = print summarized)
grains = {
'biosversion': __salt__['smbios.get']('bios-version'),
'productname': __salt__['smbios.get']('system-product-name'),
'manufacturer': __salt__['smbios.get']('system-manufacturer'),
'biosreleasedate': __salt__['smbios.get']('bios-release-date'),
'uuid': __salt__['smbios.get']('system-uuid')
}
grains = dict([(key, val) for key, val in grains.items() if val is not None])
uuid = __salt__['smbios.get']('system-uuid')
if uuid is not None:
grains['uuid'] = uuid.lower()
for serial in ('system-serial-number', 'chassis-serial-number', 'baseboard-serial-number'):
serial = __salt__['smbios.get'](serial)
if serial is not None:
grains['serialnumber'] = serial
break
elif salt.utils.which_bin(['fw_printenv']) is not None:
# ARM Linux devices expose UBOOT env variables via fw_printenv
hwdata = {
'manufacturer': 'manufacturer',
'serialnumber': 'serial#',
}
for grain_name, cmd_key in six.iteritems(hwdata):
result = __salt__['cmd.run_all']('fw_printenv {0}'.format(cmd_key))
if result['retcode'] == 0:
uboot_keyval = result['stdout'].split('=')
grains[grain_name] = _clean_value(grain_name, uboot_keyval[1])
elif osdata['kernel'] == 'FreeBSD':
# On FreeBSD /bin/kenv (already in base system)
# can be used instead of dmidecode
kenv = salt.utils.which('kenv')
if kenv:
# In theory, it will be easier to add new fields to this later
fbsd_hwdata = {
'biosversion': 'smbios.bios.version',
'manufacturer': 'smbios.system.maker',
'serialnumber': 'smbios.system.serial',
'productname': 'smbios.system.product',
'biosreleasedate': 'smbios.bios.reldate',
'uuid': 'smbios.system.uuid',
}
for key, val in six.iteritems(fbsd_hwdata):
value = __salt__['cmd.run']('{0} {1}'.format(kenv, val))
grains[key] = _clean_value(key, value)
elif osdata['kernel'] == 'OpenBSD':
sysctl = salt.utils.which('sysctl')
hwdata = {'biosversion': 'hw.version',
'manufacturer': 'hw.vendor',
'productname': 'hw.product',
'serialnumber': 'hw.serialno',
'uuid': 'hw.uuid'}
for key, oid in six.iteritems(hwdata):
value = __salt__['cmd.run']('{0} -n {1}'.format(sysctl, oid))
if not value.endswith(' value is not available'):
grains[key] = _clean_value(key, value)
elif osdata['kernel'] == 'NetBSD':
sysctl = salt.utils.which('sysctl')
nbsd_hwdata = {
'biosversion': 'machdep.dmi.board-version',
'manufacturer': 'machdep.dmi.system-vendor',
'serialnumber': 'machdep.dmi.system-serial',
'productname': 'machdep.dmi.system-product',
'biosreleasedate': 'machdep.dmi.bios-date',
'uuid': 'machdep.dmi.system-uuid',
}
for key, oid in six.iteritems(nbsd_hwdata):
result = __salt__['cmd.run_all']('{0} -n {1}'.format(sysctl, oid))
if result['retcode'] == 0:
grains[key] = _clean_value(key, result['stdout'])
elif osdata['kernel'] == 'Darwin':
grains['manufacturer'] = 'Apple Inc.'
sysctl = salt.utils.which('sysctl')
hwdata = {'productname': 'hw.model'}
for key, oid in hwdata.items():
value = __salt__['cmd.run']('{0} -b {1}'.format(sysctl, oid))
if not value.endswith(' is invalid'):
grains[key] = _clean_value(key, value)
elif osdata['kernel'] == 'SunOS' and osdata['cpuarch'].startswith('sparc'):
# Depending on the hardware model, commands can report different bits
# of information. With that said, consolidate the output from various
# commands and attempt various lookups.
data = ""
for (cmd, args) in (('/usr/sbin/prtdiag', '-v'), ('/usr/sbin/prtconf', '-vp'), ('/usr/sbin/virtinfo', '-a')):
if salt.utils.which(cmd): # Also verifies that cmd is executable
data += __salt__['cmd.run']('{0} {1}'.format(cmd, args))
data += '\n'

sn_regexes = [
re.compile(r) for r in [
r'(?im)^\s*Chassis\s+Serial\s+Number\n-+\n(\S+)', # prtdiag
r'(?im)^\s*chassis-sn:\s*(\S+)', # prtconf
r'(?im)^\s*Chassis\s+Serial#:\s*(\S+)', # virtinfo
]
]

obp_regexes = [
re.compile(r) for r in [
r'(?im)^\s*System\s+PROM\s+revisions.*\nVersion\n-+\nOBP\s+(\S+)\s+(\S+)', # prtdiag
r'(?im)^\s*version:\s*\'OBP\s+(\S+)\s+(\S+)', # prtconf
]
]

fw_regexes = [
re.compile(r) for r in [
r'(?im)^\s*Sun\s+System\s+Firmware\s+(\S+)\s+(\S+)', # prtdiag
]
]

uuid_regexes = [
re.compile(r) for r in [
r'(?im)^\s*Domain\s+UUID:\s*(\S+)', # virtinfo
]
]

manufacture_regexes = [
re.compile(r) for r in [
r'(?im)^\s*System\s+Configuration:\s*(.*)(?=sun)', # prtdiag
]
]

product_regexes = [
re.compile(r) for r in [
r'(?im)^\s*System\s+Configuration:\s*.*?sun\d\S+\s(.*)', # prtdiag
r'(?im)^\s*banner-name:\s*(.*)', # prtconf
r'(?im)^\s*product-name:\s*(.*)', # prtconf
]
]

sn_regexes = [
re.compile(r) for r in [
r'(?im)Chassis\s+Serial\s+Number\n-+\n(\S+)', # prtdiag
r'(?i)Chassis\s+Serial#:\s*(\S+)', # virtinfo
r'(?i)chassis-sn:\s*(\S+)', # prtconf
]
]

obp_regexes = [
re.compile(r) for r in [
r'(?im)System\s+PROM\s+revisions.*\nVersion\n-+\nOBP\s+(\S+)\s+(\S+)', # prtdiag
r'(?im)version:\s*\'OBP\s+(\S+)\s+(\S+)', # prtconf
]
]

fw_regexes = [
re.compile(r) for r in [
r'(?i)Sun\s+System\s+Firmware\s+(\S+)\s+(\S+)', # prtdiag
]
]

uuid_regexes = [
re.compile(r) for r in [
r'(?i)Domain\s+UUID:\s+(\S+)', # virtinfo
]
]

for regex in sn_regexes:
res = regex.search(data)
if res and len(res.groups()) >= 1:
grains['serialnumber'] = res.group(1).strip().replace("'", "")
break

for regex in obp_regexes:
res = regex.search(data)
if res and len(res.groups()) >= 1:
obp_rev, obp_date = res.groups()[0:2] # Limit the number in case we found the data in multiple places
grains['biosversion'] = obp_rev.strip().replace("'", "")
grains['biosreleasedate'] = obp_date.strip().replace("'", "")

for regex in fw_regexes:
res = regex.search(data)
if res and len(res.groups()) >= 1:
fw_rev, fw_date = res.groups()[0:2]
grains['systemfirmware'] = fw_rev.strip().replace("'", "")
grains['systemfirmwaredate'] = fw_date.strip().replace("'", "")
break

for regex in uuid_regexes:
res = regex.search(data)
if res and len(res.groups()) >= 1:
grains['uuid'] = res.group(1).strip().replace("'", "")
break

for regex in manufacture_regexes:
res = regex.search(data)
if res and len(res.groups()) >= 1:
grains['manufacture'] = res.group(1).strip().replace("'", "")
break

for regex in product_regexes:
res = regex.search(data)
if res and len(res.groups()) >= 1:
grains['product'] = res.group(1).strip().replace("'", "")
break

return grains


def _smartos_computenode_data():
'''
Return useful information from a SmartOS compute node
'''
# Provides:
# vms_total
# vms_running
# vms_stopped
# sdc_version
# vm_capable
# vm_hw_virt

if salt.utils.is_proxy():
return {}

grains = {}

# *_vms grains
grains['computenode_vms_total'] = len(__salt__['cmd.run']('vmadm list -p').split("\n"))
grains['computenode_vms_running'] = len(__salt__['cmd.run']('vmadm list -p state=running').split("\n"))
grains['computenode_vms_stopped'] = len(__salt__['cmd.run']('vmadm list -p state=stopped').split("\n"))

# sysinfo derived grains
sysinfo = json.loads(__salt__['cmd.run']('sysinfo'))
grains['computenode_sdc_version'] = sysinfo['SDC Version']
grains['computenode_vm_capable'] = sysinfo['VM Capable']
if sysinfo['VM Capable']:
grains['computenode_vm_hw_virt'] = sysinfo['CPU Virtualization']

# sysinfo derived smbios grains
grains['manufacturer'] = sysinfo['Manufacturer']
grains['productname'] = sysinfo['Product']
grains['uuid'] = sysinfo['UUID']

return grains


def _smartos_zone_data():
'''
Return useful information from a SmartOS zone
'''
# Provides:
# pkgsrcversion
# imageversion
# pkgsrcpath
# zonename
# zoneid
# hypervisor_uuid
# datacenter

if salt.utils.is_proxy():
return {}

grains = {}

pkgsrcversion = re.compile('^release:\\s(.+)')
imageversion = re.compile('Image:\\s(.+)')
pkgsrcpath = re.compile('PKG_PATH=(.+)')
if os.path.isfile('/etc/pkgsrc_version'):
with salt.utils.fopen('/etc/pkgsrc_version', 'r') as fp_:
for line in fp_:
match = pkgsrcversion.match(line)
if match:
grains['pkgsrcversion'] = match.group(1)
if os.path.isfile('/etc/product'):
with salt.utils.fopen('/etc/product', 'r') as fp_:
for line in fp_:
match = imageversion.match(line)
if match:
grains['imageversion'] = match.group(1)
if os.path.isfile('/opt/local/etc/pkg_install.conf'):
with salt.utils.fopen('/opt/local/etc/pkg_install.conf', 'r') as fp_:
for line in fp_:
match = pkgsrcpath.match(line)
if match:
grains['pkgsrcpath'] = match.group(1)
if 'pkgsrcversion' not in grains:
grains['pkgsrcversion'] = 'Unknown'
if 'imageversion' not in grains:
grains['imageversion'] = 'Unknown'
if 'pkgsrcpath' not in grains:
grains['pkgsrcpath'] = 'Unknown'

grains['zonename'] = __salt__['cmd.run']('zonename')
grains['zoneid'] = __salt__['cmd.run']('zoneadm list -p | awk -F: \'{ print $1 }\'', python_shell=True)

return grains


def _zpool_data(grains):
'''
Provide grains about zpools
'''
# quickly return if windows or proxy
if salt.utils.is_windows() or 'proxyminion' in __opts__:
return {}

# quickly return if no zpool and zfs command
if not salt.utils.which('zpool'):
return {}

# collect zpool data
zpool_grains = {}
for zpool in __salt__['cmd.run']('zpool list -H -o name,size').splitlines():
zpool = zpool.split()
zpool_grains[zpool[0]] = zpool[1]

# return grain data
if len(zpool_grains.keys()) < 1:
return {}
return {'zpool': zpool_grains}


def get_server_id():
'''
Provides an integer based on the FQDN of a machine.
Useful as server-id in MySQL replication or anywhere else you'll need an ID
like this.
'''
# Provides:
# server_id

if salt.utils.is_proxy():
return {}
return {'server_id': abs(hash(__opts__.get('id', '')) % (2 ** 31))}


def get_master():
'''
Provides the minion with the name of its master.
This is useful in states to target other services running on the master.
'''
# Provides:
# master
return {'master': __opts__.get('master', '')}

# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
相關文章
相關標籤/搜索