文章目录

  • 项目实战:CMDB自动化资产扫描和自动化运维
    • 1、项目介绍
    • 2、项目技术分析
      • 运维自动化难点和痛点
      • 项目技术难点
      • 整体工程设计
    • 3、项目环境搭建
      • 项目环境要求
      • 项目环境的搭建
        • 项目目录的配置
        • 远程服务器虚拟环境的配置
      • MySQL数据库配置
        • (远程)用户登录配置
        • Django数据库配置
        • 数据库报错处理
    • 4、第一个DevOPS工程
      • 项目功能
      • 项目开发步骤
        • 项目开发(一) 信息采集接口的实现
        • 项目开发(二)信息获取接口的实现
      • Django项目日志管理
      • Django项目邮件告警管理
    • 5、第二个DevOPS工程
      • Django工程多配置文件
      • Django工程应用与模块加载
      • 资产管理
        • 为什么优先实现资产管理?
      • 资产管理探测流程
      • 主机存活探测协议
      • 主机存活探测模块和工具
        • Nmap探测工具
        • Nmap的Python操作接口:python-nmap
      • SSH端口存活扫描
      • 扫描探测小结
      • 主机登录探测
        • 什么是主机登录探测?
        • 主机SSH登录验证方式
        • Python的SSH登录模块pexpect
          • pexpect模块的缺陷:
      • Python的SSH登录模块paramiko
      • 系统信息获取
      • Django数据库模型设计
      • 配置文件配置
      • 视图函数
      • 路由配置
      • 后台Admin管理
      • 测试

项目实战:CMDB自动化资产扫描和自动化运维

1、项目介绍

本项目基于Linux系统搭建系统集群,使用Ansible实现Linux集群下的批量部署和自动化管理,实现Web形式的自动化运维系统,集中批量控制服务器,最终实现能支撑1000台实例的环境提供管理和自动化任务,提高运维工程师的工作效率和质量。项目基于HTTP实现自动化任务接受和响应接口设计,基于MySQL用作的关系型数据存取,基于Redis的任务锁机制和消息队列, 基于MongoDB的事件日志记录, 最终实现邮件通知功能、敏感数据加密功能、日志事件记录功能。

主要目标是实现自动化资产扫描, 扫描指定网段的服务器资产信息,后续功能会进一步完善。

2、项目技术分析

运维自动化难点和痛点

项目技术难点

  • 基本技能

    • DevOPS构建之路
    • Python基础语法
    • Django框架
  • 自动化资产扫描发现
    • 资产扫描的作用
    • nmap的作用
    • telnetlib端口扫描
    • pexpect登录探测
    • paramiko登录探测
    • Docker容器扫描
    • KVM虚拟机扫描
    • snmp网络设备扫描
    • SDK调用扫描ESXI资产信息
  • Ansible自动化任务
    • Ansible的安装与配置
    • Python与Ansible的操作
    • Ansible adhoc
    • Ansible playbook
    • 核心类调用
    • API 接口的封装
    • 方法的改写
    • Redis消息存储
    • Mongo事件日志

整体工程设计

3、项目环境搭建

项目环境要求

项目环境的搭建

项目目录的配置

  • 创建Django项目devops

如果有云服务器时执行下面的操作,没有云服务器的操作在这个之后进行说明

  • 连接并配置远程服务器
    [tools]>[Deployment]

    • 配置本地目录和远程服务器目录的映射(Mapping)


    上述操作完成,修改本地文件,远程服务器文件也同时被修改。

远程服务器虚拟环境的配置

  • 连接远程服务器命令行bash
  • 创建虚拟环境并激活虚拟环境
cd /data/www/devops
virtualenv
-p /usr/bin/python3 env
source env/bin/active
pip install Django==2.2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 出现的报错及处理方式
# sqlite版本问题
django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).
  • 1
  • 2

解决方式:不使用sqlite存储数据,使用mysql

# CMDB/settings.py
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'devopsProject','USER': 'devops','PASSWORD': '','HOST': '127.0.0.1','PORT': '3306',}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 远程服务器解释器和目录映射的配置

  • 在远程服务器上测试Django项目是否运行成功
python manage.py runserver
  • 1

如果没有云服务器直接进行虚拟环境的创建
执行下面命令:

witch python3  # /usr/bin/python3
virtualenv -p /usr/bin/python3 env     #创建虚拟环境
source env/bin/active       #切换到虚拟环境
pip install Django==2.2
  • 1
  • 2
  • 3
  • 4

MySQL数据库配置

(远程)用户登录配置

  • 管理数据库服务
[root@foundation0 ~]# systemctl start mariadb
[root@foundation0 ~]# systemctl enable mariadb
  • 1
  • 2
  • 用户授权
[root@foundation0 ~]# mysql -uroot -pServer version: 5.5.52-MariaDB MariaDB Server
  • 1
# 创建数据库
MariaDB [(none)]> create database if not exists devopsProject default charset
utf8;
Query OK, 1 row affected (0.01 sec)
# 新建用户
MariaDB [(none)]> create user devops@'%' identified by 'westos';
Query OK, 0 rows affected (0.03 sec)
# 用户授权
MariaDB [(none)]> grant all on devopsProject.* to devops@'%';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> Bye
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 测试用户授权是否成功
[root@foundation0 ~]# mysql -udevops -pwestos -hIP
Server version: 5.5.52-MariaDB MariaDB Server
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| devopsProject
|
+--------------------+
2 rows in set (0.01 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Django数据库配置

# CMDB/settings.py
ALLOWED_HOSTS = ['*']
#
配置数据库: 使用mysql数据库,而不是默认的sqlite数据库。
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'devopsProject','USER': 'devops','PASSWORD': 'devops','HOST': '47.92.255.98','PORT': '3306',}
}
# 语言和时区的设置
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 生成数据库表
python manage.py makemigrations  # 生成迁移脚本
python manage.py migrate
# 写入数据库, 创建关于用户和用户组等数据表信息
python manage.py createsuperuser # 创建超级用户
# 启动项目, 访问网址http://IP:8000/admin
python manage.py runserver 0.0.0.0:8000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 测试数据表是否创建?数据信息是否写入?
    连接mariadb数据库

配置数据库信息

如果是本地数据库,Host填写127.0.0.1即可

访问数据库表和数据内容

数据库报错处理

运行项目时,出现报错如下,是因为缺少mysqlclient安装包.

解决方法:

pip install mysqlclient

安装失败,报错如下是缺少开发包的缘故。

解决方法

yum install mariab-devel -y
yum install python-devel -y
  • 1
  • 2

4、第一个DevOPS工程

项目功能

记录HTTP访问的IP及用户UA信息

项目开发步骤

$ python manage.py startapp scanhosts
# settings.py
# 1). 将新建的APP加入到项目中
INSTALLED_APPS = [
......省略部分'django.contrib.staticfiles','scanhosts',
]
# 2). 配置数据库: 使用mysql数据库,而不是默认的sqlite数据库。
# DATABASES = {
#     'default': {
#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
#     }
# }
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'devopsProject',
# 数据库名称       'USER': 'devops', # 用户名'PASSWORD': 'westos', # 用户密码'HOST': '127.0.0.1', # 数据库服务器所在主机名'PORT': '3306', # 数据库端口}
}
# 3). 语言和时区的设置(根据自己项目的需求, 选择修改)
# LANGUAGE_CODE = 'en-us'
# TIME_ZONE = 'UTC'
LANGUAGE_CODE = 'zh-hans' # 语言选择中文
TIME_ZONE = 'Asia/Shanghai' # 时区选择亚洲/上海

安装数据库开发软件

$ yum install mariadb-devel -y

安装数据库连接模块(必须先安装mariadb-devel, 否则会报错)

$ pip install mysqlclient

编辑数据库模型文件:

# models.py
"""
- 一个类对应一个数据库表;
- 类的一个属性对应数据库表的一个表头;- max_length: 字符串最大长度, 对应数据库的varchar类型- default: 指定默认值- verbose_name: 指定Django后台显示的列头信息- auto_now: 每次修改记录时自动更新为当前时间
- Meta类的设置
- verbose_name: 指定Django后台显示的表名称单数
- verbose_name_plural: 指定Django后台显示的表名称复数
- db_table: 指定数据库表的名称, 默认是APP名称_类名称.
"""
class UserIPInfo(models.Model):ip = models.CharField(max_length=150, default='', verbose_name='IP地址')time = models.DateTimeField(verbose_name='更新时间', auto_now=True)class Meta:verbose_name = '用户访问地址信息表'verbose_name_plural = verbose_name       db_table = 'user_IP_info'
class BrowseInfo(models.Model):# null=True: 是针对数据库而言,True表示数据库的该字段可以为空。user_agent = models.CharField(max_length=100, default='',verbose_name='用户浏览器信息', null=True)disk_id = models.CharField(max_length=256, default='', verbose_name='唯
一设备ID')"""ForeignKey是一种关联字段,将两张表进行关联的方式on_delete: 是否级联删除, Django1.x默认级联删除, Django2.x必须手动指定on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值CASCADE:此值设置,是级联删除。PROTECT:此值设置,是会报完整性错误。SET_NULL:此值设置,会把外键设置为null,前提是允许为null。SET_DEFAULT:此值设置,会把设置为外键的默认值。SET():此值设置,会调用外面的值,可以是一个函数。"""user_ip = models.ForeignKey('UserIPInfo', on_delete=models.DO_NOTHING)class Meta:verbose_name = '用户浏览器信息表'verbose_name_plural = verbose_namedb_table = 'browse_info'

根据ORM(对象关系映射)将面向对象形式的模型进行迁移, 生成中间代码

$ python manage.py
makemigrations
# 代码执行效果, 生成迁移文件,所在位置: scanhosts/migrations
Migrations for 'scanhosts':
scanhosts/migrations/0001_initial.py- Create model UserIPInfo

将生成的迁移文件转成SQL语句并执行SQL语句, 创建对应的数据库及数据库表

$ python manage.py migrate
$ python manage.py
createsuperuser
Username (leave blank to use 'kiosk'): admin
Email address: admin@qq.com
Password:
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

启动Django项目,默认开启的端口是8000

$ python manage.py runserver
Django version 2.2.5, using settings 'first_devops.settings'
Starting development server at http://127.0.0.1:8000/

访问项目后台管理界面, 输入超级用户名称和密码即可进入后台界面.

项目开发(一) 信息采集接口的实现

  • url设计
    配置URL, 当用户访问http://127.0.0.1:8000/sendinfos这个网址时, 将用户请求交给user_info视图函数处理。
# first_devops/urls.py
urlpatterns = [path('admin/', admin.site.urls),url(r'^sendinfos/$', user_infos),
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 视图函数的实现
# scanhosts/views.py
def user_info(request):# request.META 是一个Python字典,包含了所有本次HTTP请求的Header信息,比如用户IP
地址和用户Agent(通常是浏览器的名称和版本号)ip = request.META.get('REMOTE_ADDR')user_agent = request.META.get('HTTP_USER_AGENT')# 使用filter()方法对数据进行过滤, 返回的是列表, 列表元素是符合条件的对象。user_obj = UserIPInfo.objects.filter(ip=ip)# 如果没有找到,则新建UserIPInfo对象,并获取对象编号(为了和BrowseInfo表关联)if not user_obj:res = UserIPInfo.objects.create(ip=ip)user_ip_id = res.idelse:user_ip_id = user_obj[0].id# 新建BrowseInfo对象BrowseInfo.objects.create(user_agent=user_agent, user_ip_id=user_ip_id)# 字典封装返回的数据信息result = {'STATUS': 'success','INFO': 'User Info','IP': ip,'User-Agent': user_agent}# 以json的方式封装返回, 下面的两种方式任选一种.# return
HttpResponse(json.dumps(result),
content_type='application/json')return JsonResponse(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 浏览器访问效果图

    浏览器访问结束后, 访问MySQL数据库, 看是否将数据信息采集成功并通过ORM的方式写入数据库中。
[root@foundation0 ~]# mysql -udevops -p
Welcome to the MariaDB monitor.
Commands end with ; or \g.
MariaDB [(none)]> use devopsProject;
MariaDB [devopsProject]> select * from user_IP_info;
MariaDB [devopsProject]> select * from browse_info;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

项目开发(二)信息获取接口的实现

  • url设计
    配置URL, 当用户访问http://127.0.0.1:8000/getinfos这个网址时, 将用户请求交给user_history视图函数处理。
# first_devops/urls.py
urlpatterns = [path('admin/', admin.site.urls),url(r'^sendinfos/$', user_info),url(r'^getinfos/$', user_history),
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 视图函数的实现
# scanhosts/views.py
def user_history(request):# 获取UserIPInfo表的所有对象信息;ip_lists = UserIPInfo.objects.all()infos = {}# 获取每个IP访问网站浏览器的信息, 格式如下:"""infos = {'127.0.0.1' : ['UA-1', 'ua-2'],'172.25.254.1' : ['UA-1', 'ua-2'],}"""for item in ip_lists:infos[item.ip] = [b_obj.user_agent for b_obj in
BrowseInfo.objects.filter(user_ip_id=item.id)]result = {'STATUS': 'success','INFO': infos}return JsonResponse(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Django项目日志管理

在编写程序过程中,很难免的会出现一些问题,程序并非按照我们预想的那样运行,这个时候我们通常会对程序进行调试,来看看到底是哪边出了问题。而程序日志是来帮助我们记录程序运行过程的帮手,善用日志的程序员也就能很快找出自己程序的问题所在从而快速解决问题。

在服务器级别的组件中都有对应的日志文件,例如MySQL、Redis、nginx、uWSGI都会在运行过程中将一些信息写到日志文件中。

Django使用python的内置模块logging来管理自己的日志, 包含四大组件: 日志记录器Loggers、日志处理器Handlers、日志过滤器Filters和日志格式化工具Formatters。

Django项目日志管理详情查看官方文档:https://docs.djangoproject.com/en/2.2/topics/logging/

# first_devops/settings.py
# 日志管理的配置
LOGGING = {'version': 1,# disable_existing_loggers是否禁用已经存在的logger实例。默认值是True.'disable_existing_loggers': False,# formatters: 定义输出的日志格式。'formatters': {'verbose': {# 格式化属性查看资料:
https://docs.python.org/3/library/logging.html#logrecord-attributes'format': '{levelname} {asctime} {module} : {lineno} {message}',# 变量的风格'style': '{',# 日期显示格式'datefmt': '%Y-%m-%d %H:%M:%S',},
},# handlers定义处理器。'handlers': {'file': {# 日志处理级别'level': 'INFO',# 日志处理类, 详细的请查看网站:
https://docs.python.org/3/library/logging.handlers.html'class': 'logging.FileHandler',# 日志处理后输出格式'formatter': 'verbose',# 记录日志的文件名, 存储在当前项目目录下的devops.log文件'filename': os.path.join(BASE_DIR, 'devops.log')},
},   # loggers定义logger实例。'loggers': {'django': {# 对应的handles对象列表'handlers': ['file'],# logger实例输出的日志级别'level': 'INFO',# 日志是否向上级传递。True 向上级传,False 不向上级传。'propagate': True,},
}
}

Django项目邮件告警管理

在web应用中,服务器对客户发送邮件来通知用户一些信息,可以使用邮件来实现。Django中提供了邮件接口,使我们可以快捷的建设一个邮件发送系统。通常用于发送自定义消息或者通知告警等信息(当然
也可以通过短信接口或者微信接口, 便于维护人员快速响应)。

# first_devops/settings.py
# 邮件配置
EMAIL_HOST = 'smtp.qq.com'
EMAIL_HOST_USER = 'QQ邮箱'
EMAIL_HOST_PASSWORD = '登录授权码(注意: 不是登录密码)'
EMAIL_PORT = 465
EMAIL_SUBJECT_PREFIX = 'Python开发社区'
EMAIL_USE_SSL = True
$ python manage.py
shell
>>> from django.core.mail import send_mail
>>> help(send_mail)
>>> send_mail(subject="Django邮件发送测试代码", message='邮件发送测试成功',
from_email='发送人的邮箱地址', recipient_list=['接收人的邮箱地址1', '接收人的邮箱地址2'])
1

发送邮件在很多业务场景都会适用, 为了方便操作, 将邮件发送的内容封装成一个工具, 减少开发过程中的重复操作, 提高效率。 操作如下:

# first_devops/scanhosts/utils/tools.py
import logging
from django.core.mail import send_mail
from datetime import datetime
from first_devops import settings
class SendMail(object):"""发送邮件的封装类"""def __init__(self, subject, message, recipient_list, ):# 给每个邮件的标题加上当前时间, 时间格式为年月日_小时分钟秒_传入的邮件标题subject_time = datetime.now().strftime('%Y%m%d_%H%M%S_')self.recipient_list = recipient_listself.subject = subject_time + subjectself.message = messagedef send(self):try:send_mail(subject=self.subject,message=self.message,from_email=settings.EMAIL_HOST_USER,recipient_list=self.recipient_list,fail_silently=False)return Trueexcept Exception as e:logging.error(str(e))return False
$ python manage.py
shell
>>> from scanhosts.utils.tools import SendMail
>>> mail = SendMail('Django 测试标题', '邮件正文内容', ['976131979@qq.com'])
>>> mail.send()
True

5、第二个DevOPS工程

Django工程多配置文件

from .base import  *
DEBUG = True
  • 1
  • 2
  • production.py文件: 生产环境的配置文件
from .base import  *
# 开发环境一定要关闭调试模式
DEBUG = False
# 允许所有主机访问
ALLOWED_HOSTS = ['*']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 修改manage.py文件, 默认寻找的设置文件是当前项目中的settings文件, 如果是开发环境, 修改如下:
def main():os.environ.setdefault('DJANGO_SETTINGS_MODULE',
'devops.settings.development')
# ......此处省略代码
if __name__ == '__main__':main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果项目将来需要上线, 修改启动项目访问的配置文件为生产环境的配置文件即可, 如下:

def main():os.environ.setdefault('DJANGO_SETTINGS_MODULE',
'devops.settings.production')
# ......此处省略代码
if __name__ == '__main__':main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 启动项目
$ python manage.py runserver
Django version 2.2.5, using settings 'devops.settings.development'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
  • 1
  • 2
  • 3
  • 4

Django工程应用与模块加载

为了方便在一个大的Django项目中,管理实现不同的业务功能, 我们会在项目中创建多个APP实现功能。为了更加方便管理APP, 项目结构更加清晰。可以专门创建apps目录存储项目应用, 专门创建extra_apps存储项目第三方APP, 项目结构如下所示:

但项目运行时, 不会自动寻找apps和extra_apps子目录中创建的APP, 需要手动在配置文件中配置,修改devops/settings/base.py文件, 添加内容如下:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 将apps目录和extra_apps添加到python的搜索模块的路径集中
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
sys.path.insert(0, os.path.join(BASE_DIR, 'extra_apps'))

添加成功后, 进入Django工程的交互式环境, 查看sys.path变量的值, 判断是否添加成功?

$ python manage.py   shell
In [1]: import sys                                                              In [2]: sys.path                                                                Out[2]:
['/home/kiosk/PycharmProjects/devops/devops/extra_apps',
'/home/kiosk/PycharmProjects/devops/devops/apps',
'/home/kiosk/PycharmProjects/devops',
# .......此处为了美观, 省略部分路径........
]

资产管理

为什么优先实现资产管理?

  • 资产管理是运维的基本工作;
  • 资产管理是DevOPS系统的基础;
    资产管理是自动化运维平台构建的基础。

资产管理探测流程

主机存活探测协议

主机存活探测模块和工具

Nmap探测工具

Nmap,也就是Network Mapper,最早是Linux下的网络扫描和嗅探工具包。是一款用于网络发现和安全审计的网络安全工具。

  • 主机发现 - 识别网络上的主机。例如,列出响应TCP和/或ICMP请求或打开特定端口的主机。
# 使用-sP开关(Arp ping)执行PING命令,与windows / linux ping命令类似。
$ ping -c1 -w1 172.25.254.197
# 探测主机IP是否存活,
&>/dev/null并将所有的输入重定向到垃圾箱
# && 如果前面的指令执行成功, 做什么操作(echo ok)
# || 如果前面的指令执行失败, 做什么操作(echo fail)
$ ping -c1 -w1 172.25.254.250   &>/dev/null && echo ok || echo fail
# 使用nmap命令, 如果报错-bash: nmap: command not found, 则yum 安装nmap安装包
$ nmap -n -sP 172.25.254.197
$ nmap -n -sP 172.25.254.0/24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 端口扫描 - 枚举目标主机上的开放端口。
# Nmap默认端口的扫描范围1-10000
$ nmap -n -p 172.25.254.197
# 具体指定要扫描的端口为50-80
$ nmap -n -p50-80 172.25.254.197
# 具体指定要扫描的端口为22和80
$ nmap -n -p22,80 172.25.254.197
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 版本检测 - 询问远程设备上的网络服务以确定应用程序名称和版本号。
  • OS检测 - 确定网络设备的操作系统和硬件特性。
# -O是检测操作系统交换机
$ nmap -O 172.25.254.197
  • 1
  • 2
  • 可与脚本进行脚本交互 - 使用Nmap脚本引擎(NSE)和Lua编程语言。

查看172.25.254.197这台 主机是否开启?
查看172.25.254.0/24局域网内存活的主机信息及存活主机个数。

Nmap的Python操作接口:python-nmap

python-nmap是一个使用nmap进行端口扫描的python库,它可以很轻易的生成nmap扫描报告,并且可以帮助系统管理员进行自动化扫描任务和生成报告。同时,它也持nmap脚本输出。

# 安装nmap的第三方模块
$ pip install python-nmap
  • 1
  • 2

具体的代码调用如下:

import nmap
# 实例化对象, portScanner()类用于实现对指定主机进行端口扫描
nm = nmap.PortScanner()
# 以指定方式扫描指定主机或网段的指定端口
result = nm.scan(hosts='172.25.254.0/24', arguments='-n -sP')
print("扫描结果: ", result)
# 返回的扫描具体的nmap命令行
print("nmap命令行: ", nm.command_line())
# 返回nmap扫描的主机清单,格式为列表类型
print("主机清单: ", nm.all_hosts())
# 查看指定主机信息
print('172.25.254.197的主机信息: ', nm['172.25.254.197'])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

代码执行效果如下图所示:

扫描结果:
{'nmap': {'command_line': 'nmap -oX - -n -sP 172.25.254.0/24', 'scaninfo': {},
'scanstats': {'timestr': 'Wed Dec 25 16:14:47 2019', 'elapsed': '6.06',
'uphosts': '2', 'downhosts': '254', 'totalhosts': '256'}}, 'scan':
{'172.25.254.197': {'hostnames': [{'name': '', 'type': ''}], 'addresses':
{'ipv4': '172.25.254.197'}, 'vendor': {}, 'status': {'state': 'up', 'reason':
'syn-ack'}}, '172.25.254.250': {'hostnames': [{'name': '', 'type': ''}],
'addresses': {'ipv4': '172.25.254.250'}, 'vendor': {}, 'status': {'state': 'up',
'reason': 'syn-ack'}}}}
nmap命令行:
主机清单:
nmap -oX - -n -sP 172.25.254.0/24
['172.25.254.197', '172.25.254.250']
172.25.254.197的主机信息:
{'hostnames': [{'name': '', 'type': ''}], 'addresses': {'ipv4':
'172.25.254.197'}, 'vendor': {}, 'status': {'state': 'up', 'reason': 'syn-ack'}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

SSH端口存活扫描

$ telnet 172.25.254.34 22

# 实例化对象
tn = telnetlib.Telnet(host='172.25.254.34', port=22)
# read_until读取直到遇到了换行符或超时秒数。默认返回bytes类型,通过decode方法解码为字符串。
result = tn.read_until(b'\n', timeout=5).decode('utf-8')
# 通过正则匹配且忽略大小写, 寻找是否ssh服务开启。
searchObj = re.search('ssh', result, re.I)
# 如果能匹配到内容, 说明ssh服务开启, 是Linux服务器.
if searchObj:print("ssh服务是开启的,且是Linux操作系统")
else:print('ssh服务未开启或者不是Linux服务器')

扫描探测小结

主机登录探测

什么是主机登录探测?

用一系列的验证方式循环进行SSH登录, 得到争取的登录方式。

主机SSH登录验证方式

SSH常用来远程登录到远程机器,有两种常用的方法

  • 第一种便是账号密码登录。
  • 第二种就是公钥私钥无密码登录。(如何实现无密码登录?)

Python的SSH登录模块pexpect

Pexpect 用来实现与 ssh、ftp 、telnet 等程序的自动交互。是 Expect 语言的一个 Python 实现,是一个用来启动子程序,并使用正则表达式对程序输出做出特定响应,以此实现与其自动交互的 Python模块。

pexpect的核心类和函数。

  • 直接进程运行run()函数, 返回结果和状态。
import pexpect
cmd_result, exitstatus = pexpect.run('hostname', withexitstatus=True)
print("命令执行结果: ", cmd_result.decode('utf-8'))
print("命令执行的状态码: ", exitstatus)
  • 1
  • 2
  • 3
  • 4

执行结果如下:

命令执行结果:foundation0.ilt.example.com
命令执行的状态码:  0
  • 1
  • 2
  • pexpect指令执行的两种方式—无交互和交互式
import pexpect# 1.通过pexpect执行指令(无交互)
# 执行命令并返回命令执行结果和状态码(0代表成功,其他-执行失败)
(command_output, exitstatus) = pexpect.run('hostname', withexitstatus=1)
command_output = command_output.decode('utf-8')
if exitstatus == 0:print("命令执行成功:", command_output)
else:print("命令执行失败:", command_output)def login_ssh_password(user, host, password, port=22):# 2. 通过pexpect执行指令(有交互)command = 'ssh -p22 root@172.25.254.34'# command = 'ssh -p%s %s@%s' %(port, user, host)# spawn开启一个子进程处理交互式操作ssh = pexpect.spawn(command=command, timeout=3)# 匹配交互信息,返回的是匹配到的信息的索引match_index = ssh.expect(['Are you sure you want to continue connecting (yes/no)? ', 'password:'])print(match_index)# 如果索引为0代表第一次连接,如果索引为1代表非第一次连接if match_index == 0:print("第一次连接")ssh.sendline('yes')ssh.expect(['password:'])ssh.sendline(password)elif match_index == 1:print('非第一次连接')ssh.sendline(password)login_index = ssh.expect(['Last login: ', pexpect.EOF, pexpect.TIMEOUT])print(login_index)if login_index == 0:print("用户登录成功")# 进入元成服务器的命令行ssh.interact()elif login_index == 1:print("登录失败:Logout")elif login_index == 2:print('登录超时')if __name__ == '__main__':login_ssh_password(user='root',host='172.25.254.34', password='Asimov',port=22)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

pexpect模块的缺陷:

  • 依赖终端命令的方式
  • 不同的ssh登陆环境兼容性差

Python的SSH登录模块paramiko

# 使用豆瓣的镜像源, 安装paramiko模块并指定安装版本为2.6.0.
$ pip install -i https://pypi.douban.com/simple paramiko==2.6.0

paramiko包含两个核心组件:SSHClient和SFTPClient(sftp=ssh file transfer protocol)。

  • SSHClient的作用类似于Linux的ssh命令,是对SSH会话的封装,该类封装了传输(Transport),通道(Channel)及SFTPClient建立的方法(open_sftp),通常用于执行远程命令。

  • SFTPClient的作用类似与Linux的sftp命令,是对SFTP客户端的封装,用以实现远程文件操作,如文件上传、下载、修改文件权限等操作。

  • 项目代码:基于paramiko实现ssh客户端密钥远程登录
    测试之前生成公钥和私钥进行测试:

# 生成公钥和私钥, 默认存储在 ~/.ssh/目录下. id_rsa私钥, id_rsa.pub公钥
ssh-keygen
# 希望我的主机可以无密码连接其他主机(需要将公钥分发给其他主机)
ssh-copy-id -i ~/.ssh/id_rsa.pub user@ip
# 测试无密码连接是否成功
ssh user@ip
ssh -i 私钥位置 user@ip
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
import paramikodef login_ssh_password(hostname, port, username, password, command):# 实例化SSH客户端对象with paramiko.SSHClient() as client:#自动添加当前主机到远程服务器的known_hosts,远程连接不再询问yes/noclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())# 通过用户名和密码连接远程服务器client.connect(hostname=hostname,port=port,username=username,password=password,)# 连接成功后执行的命令stdin,stdout,stderr = client.exec_command(command)# 获取命令执行的正确输出# return stdin, stdout,stderrreturn stdout.read().decode('utf-8')def login_ssh_key(hostname, port, username, keyfile, command):# 实例化SSH客户端对象with paramiko.SSHClient() as client:# 配置私人密钥文件位置private = paramiko.RSAKey.from_private_key_file(keyfile)#自动添加当前主机到远程服务器的known_hosts,远程连接不再询问yes/noclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())# 通过用户名和密码连接远程服务器client.connect(hostname=hostname,port=port,username=username,password=private,)# 连接成功后执行的命令stdin,stdout,stderr = client.exec_command(command)# 获取命令执行的正确输出# return stdin, stdout,stderrreturn stdout.read().decode('utf-8')if __name__ == '__main__':# stdout = login_ssh_password(hostname='172.25.254.34',port=22,username='root',password='Asimov',command='uname')# print(stdout)stdout = login_ssh_key(hostname='172.25.254.34',port=22,username='root',keyfile='/home/kiosk/.ssh/id_rsa',command='uname')print(stdout)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • SFTPClient实战代码: 上传和下载文件
    SFTPCLient作为一个sftp的客户端对象,根据ssh传输协议的sftp会话,实现远程文件操作,如上传、下载、权限、状态。
from_transport(cls,t)
客户端通道
put(localpath, remotepath, callback=None, confirm=True)   将本地文件上传到服务
器 参数confirm:
是否调用stat()方法检查文件状态,返
回ls -l的结果
get(remotepath, localpath, callback=None)
从服务器下载文件到本
地
mkdir() 在服务器上创建目录
remove() 在服务器上删除目录
rename() 在服务器上重命名目录
stat() 查看服务器文件状态
listdir() 列出服务器目录下的文件
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

具体的实例代码如下:

import paramiko
# 获取Transport实例
with
paramiko.Transport(('172.25.254.197', 22)) as tran:# 连接SSH服务端,使用passwordtran.connect(username="root", password='westos')# # 或使用# # 配置私人密钥文件位置# private = paramiko.RSAKey.from_private_key_file('./id_rsa')# # 连接SSH服务端,使用pkey指定私钥# tran.connect(username="root", pkey=private)# 获取SFTP实例sftp = paramiko.SFTPClient.from_transport(tran)# 设置上传的本地/远程文件路径localpath = "/etc/passwd"  remotepath = "/mnt/passwd"# 执行上传动作sftp.put(localpath, remotepath)# # 执行下载动作# sftp.get(remotepath, localpath)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

系统信息获取

命令 作用 举例
主机名 通过名称识别资产的作用、位置等信息 Nginx01、KVM、aliyun01
MAC地址 记录网卡的信息,可以作为主机的唯一标识 6e:40:08:f9:84:00
SN 物理服务器、网络设备有唯一的资产标识 J156R12
系统版本 查看服务器系统和具体的版本 Redhat7.0、Centos7.0、Ubuntu12.04
服务器机型 查看主机或者服务器类型 Dell R610、HP、DL580
  • 为什么要获取这些信息

    • 有利于识别资产设备
    • 是资产的基本信息, 是自动化平台的基础(实现自动化任务执行、定时任务、自动化报表、监控等相关功能)
    • 此处只能实现主机类型信息的探测, 而不是Docker容器的探测
  • 获取信息的Linux命令介绍

    • 获取主机名的命令(选择通用方式): hostname、uname -a、cat /etc/sysconfig/network(主要针对Centos)
    • 获取系统版本: cat /etc/issue(可能为空)、cat /etc/redhat-release、uname、lsb_release
    • 获取MAC地址: cat /sys/class/net/ [^vtlsb] */address、ifconfig ens33
    • 获取服务器硬件机型: dmidecode -s system-manufacturer、dmidecode -s system-product-name

Django数据库模型设计

# apps/scanhost/models.py
class Server(models.Model):"""服务器设备"""sub_asset_type_choice = ((0, 'PC服务器'),(1, '刀片机'),(2, '小型机'),)   created_by_choice = (('auto', '自动添加'),('manual', '手工录入'),)sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice,
default=0, verbose_name="服务器类型")created_by = models.CharField(choices=created_by_choice, max_length=32,
default='auto', verbose_name="添加方式")hosted_on = models.ForeignKey('self', related_name='hosted_on_server',blank=True, null=True, verbose_name="宿主机",
on_delete=models.CASCADE)  # 虚拟机专用字段IP = models.CharField('IP地址', max_length=30, default='')MAC
= models.CharField('Mac地址', max_length=200, default='')model = models.CharField(max_length=128, null=True, blank=True,
verbose_name='服务器型号')hostname = models.CharField(max_length=128, null=True, blank=True,
verbose_name="主机名")os_type = models.CharField('操作系统类型', max_length=64, blank=True,
null=True)os_distribution = models.CharField('发行商', max_length=64, blank=True,
null=True)os_release = models.CharField('操作系统版本', max_length=64, blank=True,
null=True)def __str__(self):return '%s-%s' % (self.id, self.hostname)class Meta:verbose_name = '服务器'verbose_name_plural = "服务器"
python manage.py makemigrations
python manage.py migrate
# 创建超级用户用于后台登录
python manage.py createsuperuser

配置文件配置

# devops\settings\base.py
scanhosts = [# '127.0.0.1','172.25.254.0/24',]# '47.92.255.98']
commands = {'hostname': 'hostname','os_type': 'uname','os_distribution': 'dmidecode  -s  system-manufacturer','os_release': 'cat /etc/redhat-release','MAC': 'cat /sys/class/net/`[^vtlsb]`*/address',
}

视图函数

import re
import telnetlibimport nmap
import paramiko
from django.http import HttpResponse
from django.shortcuts import render# Create your views here.
from CMDB.settings import base
from apps.scanhost.models import Serverdef get_active_hosts(hosts):"""根据提供的网段或者IP返回存活的主机IP"""# 实例化对象, portScanner()类用于实现对指定主机进行端口扫描nm = nmap.PortScanner()# 以指定方式扫描指定主机或网段的指定端口result = nm.scan(hosts=hosts, arguments='-n ')return nm.all_hosts()def is_ssh_up(host, port=22, timeout=5):# 实例化对象tn = telnetlib.Telnet(host=host, port=port)# read_until读取直到遇到了换行符或超时秒数。默认返回bytes类型,通过decode方法解码为字符串。result = tn.read_until(b'\n', timeout=timeout).decode('utf-8')# print(result)     #SSH-2.0-OpenSSH_7.4# 通过正则匹配且忽略大小写, 寻找是否ssh服务开启。searchObj = re.search('ssh', result, re.I)# 如果能匹配到内容, 说明ssh服务开启, 是Linux服务器.if searchObj:return Trueelse:return Falsedef login_ssh_key(hostname, port, username, keyfile, command):# 实例化SSH客户端对象with paramiko.SSHClient() as client:# 配置私人密钥文件位置private = paramiko.RSAKey.from_private_key_file(keyfile)# 自动添加当前主机到远程服务器的known_hosts,远程连接不再询问yes/noclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())# 通过用户名和密码连接远程服务器client.connect(hostname=hostname,port=port,username=username,password=private,)# 连接成功后执行的命令stdin, stdout, stderr = client.exec_command(command)# 获取命令执行的正确输出# return stdin, stdout,stderrreturn stdout.read().decode('utf-8')def scanhost(request):# 访问所有要扫描的网段/IPfor host in base.scanhosts:print("正在扫描%s......" % (host))# 获取所有可以ping通的主机IPactive_hosts = get_active_hosts(hosts=host)# 一次遍历判断ssh服务是否开启for active_host in active_hosts:if is_ssh_up(active_host):server = Server()# 设置IP地址server.IP = active_host# 执行指令for attr, command in base.commands.items():# attr ='hostname' , command = 'hostname'# 存储主机名、操作系统.....指令执行的结果result = login_ssh_key(active_host, 22, 'root', '/home/kiosk/.ssh/id_rsa', command)setattr(server, attr, result)server.save()return HttpResponse('扫描成功')

路由配置

# devops/urls.py
urlpatterns = [path('admin/', admin.site.urls),path('scan/', scanhost)
]

后台Admin管理

# apps/scanhost/admin.py
# 可以在admin后台管理服务器信息
admin.site.register(Server)

测试

运行项目python manage.py runserver 0.0.0.0:8000

Django项目实战:CMDB资产扫描和DevOPS自动化运维相关推荐

  1. 自动化运维-----项目实战: 基于Ansible的云平台自动化运维系统

    文章目录 项目实战: 基于Ansible的云平台自动化运维系统 一.项目介绍 1.项目介绍 2.项目背景 二.项目环境搭建 1.项目目录的配置 2.远程服务器虚拟环境的配置 3.MySQL数据库配置 ...

  2. 技术沙龙|京东云DevOps自动化运维技术实践

    自动化测试体系不完善.缺少自助式的持续交付平台.系统间耦合度高服务拆分难度大.成熟的DevOps工程师稀缺,缺少敏捷文化--这些都是DevOps 在落地过程中,或多或少会碰到的问题,DevOps发展任 ...

  3. 《Linux/UNIX OpenLDAP实战指南》——1.8 自动化运维解决方案

    本节书摘来自异步社区<Linux/UNIX OpenLDAP实战指南>一书中的第1章,第1.8节,作者:郭大勇著,更多章节内容可以访问云栖社区"异步社区"公众号查看 1 ...

  4. 优云CMDB专家实践谈:自动化运维的基石CMDB

    CMDB是什么? 运维百花齐放繁荣景象的同时,也让碎片化问题产生:每个人都想整合运维平台,但是往往事与愿违. CMDB就像一个人的大脑核心,是一个信息协调库,其存储的资料是协调身体完成各种复杂运动的信 ...

  5. DevOps 自动化运维工具Chef

    最近在学习Chef, Chef 就是现在比较热门的自动化运维工具之一,其他的工具还有Ansible, Puppet, SaltStack.  为什么选择Chef,是因为Chef对Window的支持非常 ...

  6. Python+Django+Ansible Playbook自动化运维项目实战(二)

    Python+Django+Ansible Playbook自动化运维项目实战 一.资产管理,自动化发现.扫描 1.服务端资产探测.扫描发现 1)资产管理的资产: 2)抽象与约定: 2.探测协议和模块 ...

  7. Python+Django+Ansible Playbook自动化运维项目实战:资产管理

    Python+Django+Ansible Playbook自动化运维项目实战 一.资产管理,自动化发现.扫描 1.服务端资产探测.扫描发现 1)资产管理的资产: 2)抽象与约定: 2.探测协议和模块 ...

  8. 【Kubernetes 企业项目实战】06、基于 Jenkins+K8s 构建 DevOps 自动化运维管理平台(上)

    目录 一.k8s 助力 DevOps 在企业落地实践 1.1 传统方式部署项目为什么发布慢,效率低? 1.2 上线一个功能,有多少时间被浪费了? 1.3 如何解决发布慢,效率低的问题呢? 1.4 什么 ...

  9. Django项目实战——用户投票系统(三)

    Django项目实战--用户投票系统(三) 承接上文 官方文档链接附上: 编写你的第一个 Django 应用,第 3 部分 | Django 文档 | Django (djangoproject.co ...

最新文章

  1. 程序员拯救乐坛?OpenAI用“逆天”GPT2.0搞了个AI音乐生成器
  2. Keil逻辑分析仪的使用
  3. Zookeeper服务端线程分析(单机)
  4. 浅析TCP之SACK(选择性确认)
  5. linux检测病毒工具,Linux下查杀病毒工具
  6. redis hash数据类型常用命令
  7. 如何定义和建立架构?
  8. UML小结以及基于领域模型的系统设计初步
  9. JavaScript——创建对象
  10. aem 渲染_AEM中的单元测试(大声思考)
  11. 2_RabbitMQ工作模式_Work queues_Publish/Subscribe_Routing_Topics_HeaderRpc
  12. ffmpeg.exe dos下怎么用 放在哪里
  13. javascript布尔值_JavaScript布尔值通过上法庭进行解释
  14. [设计模式]单例模式
  15. 开课吧Java课堂:什么是HashMap类
  16. ps抠图怎么放大图片_ps中在使用抠图工具时如何用快捷键移动放大的原始图片?...
  17. 天地不仁,以万物为刍狗
  18. es进行curl请求时报错:missing authentication token for REST request
  19. 苹果cmsv10怎么添加常见的几种广告代码
  20. CSS3动画实现左右滚动效果

热门文章

  1. 字符设备驱动开发的流程
  2. Linux下静态库与动态库的制作
  3. linux的gdb命令bt,Linux 程序调式命令 GDB 概述
  4. 【PHP学习】表单验证实现注册功能
  5. rtt 在STM32F767上实现虚拟串口的问题
  6. 如何将之前push的撤回_门禁如何接线?一个实例了解清楚
  7. 常用CSS中文字体转Unicode对照表
  8. Knative v0.16.0安装全过程
  9. 计算机系统基础实验-LinkLab实验
  10. 中望3d快捷键命令大全_3dMAX常用快捷键命令大全