下面是Python之禅网络翻译版,在Python命令行下执行 import this查看英文原版。
《Python之禅》, 由蒂姆·彼得斯所著
美丽优于丑陋。
直白优于含蓄。
简单优于复杂。
复杂优于繁琐。
扁平比嵌套好。
稀疏胜于致密。
可读性很重要。
特殊情况不够特殊, 不足以违反规定。
虽然实用性胜过纯洁,
错误永远不应该悄无声息地过去,
除非明确沉默。
面对模棱两可的地方, 拒绝猜测的诱惑,
应该有一个--最好是只有一个--明显的方法来做到这一点。
虽然这种方式一开始可能并不明显, 除非你是荷兰人。
现在总比不好,
虽然从来没有比现在更好的了。
如果实现很难解释, 那是个坏主意。
如果实现很容易解释, 这可能是一个好主意。
命名空间是一个令人尖叫的好主意--让我们做更多这样的事情!
一、博客实例
实现博客的主页,个人的主页。
为了进行博客的实验,现用PyCharm创建一个Django项目,项目名称是michael_06_blog,并创建一个app01,以及静态文件夹static。在settings.py文件中注册app01以及添加静态文件路径。静态文件路径代码如下所示:
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)
下面先来创建一个文章表,并对文章进行分类,以及分文章类型。在app01目录下models.py文件中创建文章表,代码如下所示:
位置:michael_06_blog\app01\models.py
from django.db import models
class Category(models.Model):
caption = models.CharField(max_length=16)
class ArticleType(models.Model):
caption = models.CharField(max_length=16)
class Article(models.Model):
title = models.CharField(max_length=32)
content = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=None)
article_type = models.ForeignKey(ArticleType, on_delete=None)
# 文章分类,创建一个元组,放在内存中,减少表的查询
# type_choice = (
# (0, 'Python'),
# (1, 'OpenStack'),
# (2, 'Linux'),
# )
# article_type_id = models.IntegerField(choices=type_choice)
接下来在项目文件夹所在的命令行,执行下面两命令,创建数据库:
python manage.py makemigrations
python manage.py migrate
成功执行上面两条命令后,数据表已经创建。现在在articletype表中添加5条文章类型信息,文章类型信息信息如下所示:
1Python
2Linux
3大数据
4 架构
5Go
6其他
在category表中添加5条文章分类信息,如下所示:
1Python之路
2从入门到放弃
3从删库到跑路
4OpenStack入门到高级
5shell编程
接下来再在article表中随便添加几篇文章。过程省略。文章添加完成后,下面来写一个主页面,列出所有的文章。首先添加一个URL:
位置:michael_06_blog\urls.py
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
re_path('^article/', views.article), # 本次添加
]
现在在views.py文件中写article() 函数,该函数代码如下所示:
from django.shortcuts import render
from app01 import models
def article(request):
article_type_list = models.ArticleType.objects.all()
category_list = models.Category.objects.all()
result = models.Article.objects.all()
return render(request, "article.html",
{"result": result,'article_type_list': article_type_list,
'category_list': category_list})
在article()函数中对三张表进行了全部查询,并将查询结果返回给前端。写完article()函数,在michael_06_blog\templates 目录下,创建 article.html文件,该文件代码如下所示:
.condition a{
display: inline-block;
padding: 3px 5px;
border: 1px solid #dddddd;
margin: 5px;
}
过滤条件
全部
{% for row in article_type_list %}
{{ row.caption }}
{% endfor %}
全部
{% for row in category_list %}
{{ row.caption }}
{% endfor %}
文章列表
{% for row in result %}
{{ row.id }} - {{ row.title }}{% endfor %}
这段HTML代码中,为了让过滤条件下的条目稍微好看一些,对所有的a标签做了一个简单的样式设置。现在运行 michael_06_blog 项目,在浏览器地址栏访问 http://127.0.0.1:8000/article/,页面上就列出了文章的分类、文章类型、以及文章列表。如下图1-1所示:图1-1 文章列表的简单页面
在这个页面中虽然有过滤条件,但是过滤条件并未起作用。可以通过在URL中设置过滤条件来过滤,比如 http://127.0.0.1:8000/article-0-0.html表示显示全部文章列表,http://127.0.0.1:8000/article-1-0.html 表示分类为1,类型是全部的文章列表,要实现这样的功能,首先需要对URL进行修改,修改后的URL如下所示:
位置:michael_06_blog\urls.py
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
re_path('^article-(?P\d+)-(?P\d+)', views.article),
]
在URL中的参数 article_type_id 和 category_id 不是随便加,是根据在 models.py 中定义的外键关联表的字段改的,在外键关联表的字段后面,在数据库默认会在后面增加 _id。接下来修改 views.py 中的 article() 函数。修改后代码如下所示:
位置:app01\views.py
from django.shortcuts import render
from app01 import models
def article(request, *args, **kwargs):
print(kwargs) # 输出:{'article_type_id': '0', 'category_id': '0'}
article_type_list = models.ArticleType.objects.all()
category_list = models.Category.objects.all()
condition = {}
for k, v in kwargs.items():
if v == '0':
pass
else:
condition[k] = v
result = models.Article.objects.filter(**condition)
return render(request, "article.html",
{"result": result,'article_type_list': article_type_list,
'category_list': category_list})
这次在 article() 函数中传递了另外两个参数 args和kwargs,由于在URL中修改为要传递两个字典参数,这里的kwargs可接收URL传递来的字典参数。取出字典参数的value进行判断,如果是 0 则显示全部的文章列表,如果不为 0 就去数据库中根据URL传递的查询条件进行查询,并将查询结果返回到前端,并在页面上显示。例如下图1-2是访问http://127.0.0.1:8000/article-1-0.html 页面后的显示结果。图1-2 根据URL的查询条件进行查询显示
现在这个页面中的文章分类、文章类型、文章列表都还不能点击,接下来做进一步的优化。
首先对过滤条件进行修改,让过滤条件可以进行点击,并且选择了相应的过滤条件后,文章列表就显示对应过滤条件的文章。要做到这种效果,需要在点击过滤条件的时候,对URL进行构造。
第一种构造URL的方法:在urls.py 文件中的URL中增加一个 name参数,在views.py中的函数根据这个name参数进行构造。例如URL修改如下:
re_path('^article-(?P\d+)-(?P\d+).html', views.article, name='article'),
在views.py中article()函数可使用下面方式构造URL:
from django.urls import reverse
......
print(request.path_info) # 获取当前URL
url = reverse('article', kwargs=kwargs) # 可以构造URL
# url = reverse('article', kwargs={'article_type_id': '4', 'category_id': '3'}) #传递字典进行构造也可以
print(url)
可将构造好的URL传到前端即可。
第二种构造URL的方法:这里主要采用这种构造URL的方式,由于在 article() 函数中,有传递kwargs这个字典参数到函数,这个字典参数就包含了构造URL需要的信息,现在将这个kwargs字典参数传到前端,前端使用这个URL直接进行构造即可。修改后的article()函数代码如下所示:
位置:app01\views.py
def article(request, *args, **kwargs):
# print(request.path_info) # 获取当前URL
# url = reverse('article', kwargs=kwargs) # 可以构造URL
# # url = reverse('article', kwargs={'article_type_id': '4', 'category_id': '3'}) #传递字典进行构造也可以
# print(url)
article_type_list = models.ArticleType.objects.all()
category_list = models.Category.objects.all()
# print(kwargs) # {'article_type_id': '4', 'category_id': '3'}
condition = {}
for k, v in kwargs.items():
kwargs[k] = int(v)
if v == '0':
pass
else:
condition[k] = v
result = models.Article.objects.filter(**condition)
return render(request, "article.html",
{"result": result,'article_type_list': article_type_list,
'category_list': category_list, 'arg_dict': kwargs})
接下来修改 article.html 文件的代码,根据后台传递过来的数据,对URL进行构造,修改后的article.html文件代码如下所示:
位置:templates\article.html
.condition a{
display: inline-block;
padding: 3px 5px;
border: 1px solid #dddddd;
margin: 5px;
}
.condition a.active{
background-color: cornflowerblue;
}
过滤条件
{% if arg_dict.article_type_id == 0 %}
{% else %}
{% endif %}
{% for row in article_type_list %}
{% if row.id == arg_dict.article_type_id %}
{% else %}
{% endif %}
{% endfor %}
{% if arg_dict.category_id == 0 %}
{% else %}
{% endif %}
{% for row in category_list %}
{% if row.id == arg_dict.category_id %}
{% else %}
{% endif %}
{% endfor %}
文章列表
{% for row in result %}
{{ row.id }} - {{ row.title }}{% endfor %}
现在访问 http://127.0.0.1:8000/article-0-0.html 页面,所有的过滤条件均可以点击,并且根据不同的过滤条件,文章列表下面显示不同的分类和类型的文章,这样基本实现了一些简单的功能。如下图1-3所示:图1-3 根据过滤条件显示不同的文章
上面的 article.html 文件代码虽然实现了根据不同的条件在页面上显示不同的文章列表功能,但是代码显得有些乱,也有些长。现在把这个文件中的部分代码用python函数来实现。为此,app01目录下新建templatetags目录,在这个目录新建一个filter.py文件。在这个文件中定义三个函数来实现页面上的文章分类功能。详细代码如下所示:
位置:app01\templatetags\filter.py
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag
def filter_all(arg_dict, k):
if k == 'article_type_id':
n1 = arg_dict[k]
n2 = arg_dict['category_id']
if n1 == 0:
ret = '全部' % n2
else:
ret = '全部' % n2
else:
n1 = arg_dict[k]
n2 = arg_dict['article_type_id']
if n1 == 0:
ret = '全部' % n2
else:
ret = '全部' % n2
return mark_safe(ret)
@register.simple_tag
def filter_article_type(article_type_list, arg_dict):
ret = []
for row in article_type_list:
if row.id == arg_dict['article_type_id']:
temp = '%s' % (
row.id, arg_dict['category_id'], row.caption)
else:
temp = '%s' % (
row.id, arg_dict['category_id'], row.caption)
ret.append(temp)
return mark_safe("".join(ret))
@register.simple_tag
def filter_article_category(category_list, arg_dict):
ret = []
for row in category_list:
if row.id == arg_dict['category_id']:
temp = '%s' % (
arg_dict['article_type_id'], row.id, row.caption)
else:
temp = '%s' % (
arg_dict['article_type_id'], row.id, row.caption)
ret.append(temp)
return mark_safe("".join(ret))
现在修改 article.html文件的代码,修改后的代码如下所示:
位置:templates\article.html
{% load filter %}
Title.condition a{
display: inline-block;
padding: 3px 5px;
border: 1px solid #dddddd;
margin: 5px;
}
.condition a.active{
background-color: cornflowerblue;
}
过滤条件
{% filter_all arg_dict 'article_type_id' %}
{% filter_article_type article_type_list arg_dict %}
{% filter_all arg_dict 'category_id' %}
{% filter_article_category category_list arg_dict %}
文章列表
{% for row in result %}
{{ row.id }} - {{ row.title }}{% endfor %}
现在修改models.py文件,将ArticleType表注销掉,将增加 type_choice 元组,这个元组是放在内存中,不是放在数据库,这样可减少数据库查询,修改后的代码如下所示:
位置:app01\models.py
from django.db import models
class Category(models.Model):
caption = models.CharField(max_length=16)
# class ArticleType(models.Model):
# caption = models.CharField(max_length=16)
class Article(models.Model):
title = models.CharField(max_length=32)
content = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=None)
# article_type = models.ForeignKey(ArticleType, on_delete=None)
# 文章分类,创建一个元组,放在内存中,减少表的查询
type_choice = (
(1, 'Python'),
(2, 'OpenStack'),
(3, 'Linux'),
)
article_type_id = models.IntegerField(choices=type_choice, default=1)
并在命令行下执行下面两条命令:
python manage.py makemigrations
python manage.py migrate
在views.py文件中,只需要修改article()函数中的一行代码即可,将下面这行代码:
article_type_list = models.ArticleType.objects.all()
修改为:
article_type_list = models.Article.type_choice
这时在filter.py文件中的 filter_article_type() 函数中 row.id 和 row.caption 就不能这样获取id 和 cpation 的值,因为 ArticleType 这张表已经不存在了。由于现在的 article_type_list 是元组内嵌套元组,filter_article_type() 函数中是循环的元组,要获取内部元组的值,可通过索引获取,如 row[0]获取id值,row[1]获取cpation值。修改后的filter_article_type() 函数代码如下所示:
位置:app01\templatetags\filter.py 文件中的 filter_article_type 函数:
@register.simple_tag
def filter_article_type(article_type_list, arg_dict):
ret = []
for row in article_type_list:
if row[0] == arg_dict['article_type_id']:
temp = '%s' % (
row[0], arg_dict['category_id'], row[1])
else:
temp = '%s' % (
row[0], arg_dict['category_id'], row[1])
ret.append(temp)
return mark_safe("".join(ret))
现在重新刷新 http://127.0.0.1:8000/article-0-0.html 页面,页面同样能正常显示,并且点击不同的分类可以看到不同的文章列表。如图1-4所示。图1-4 删除ArticleType表后的文章列表
二、 JSONP跨域请求本质
JSONP非常重要。JSONP是一种请求方式。在Python中要请求一个网站,通常是 requests.get(URL) 的方式请求,为了说明这个过程,先创建一个app02,在命令行下执行下面的命令创建 app02:
python manage.py startapp app02
创建完app02后,在michael_06_blog\urls.py中添加一个URL:
from app02 import views as a2
path('req/', a2.jsonp),
接着在 app02\views.py 中定义一个 jsonp() 函数,代码如下所示:
位置:app02\views.py
from django.shortcuts import render
import requests
def jsonp(request):
# response 是一个对象
response = requests.get('http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301')
print(response.content) # 字节类型
response.encoding = 'utf-8' # 先进行编码
# print(response.text) # 字符串
return render(request, 'req.html', {'result': response.text})
再在 templates 目录下创建一个 req.html 文件,在这个文件中就一行代码 {{ result }},这时访问 http://127.0.0.1:8000/req/ 页面, 页面上显示的内容是 jsonp()函数中 requests.get() 所请求的URL页面内容。这个请求的顺序是: 用户端浏览器发出请求,URL将请求发送到服务器端对应的views.py中的函数,由函数的requests.get() 去请求指定的网站,再将网站的内容返回给用户端浏览器显示。
这个请求顺序中,服务器端做的是中间转接作用。那用户端可以直接去请求指定的URL不呢?先来修改 req.html文件的代码,通过JS直接发送请求,修改后代码如下所示:
位置: templates\req.html
后台获取的结果
{{ result }}
JavaScrip直接获取结果
function getContent() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301');
// 回调函数 xhr.onreadystatechange = function(){
console.log(xhr.responseText);
};
xhr.send();
}
现在刷新页面 http://127.0.0.1:8000/req/,点击页面上的 获取数据后,在Console中输出错误提示,大致意思是浏览器阻止了请求,实际上请求已经发送过去了。如图1-5所示。图1-5 浏览器阻止了JS的请求
这个问题是由于浏览器具有同源(源就是指的域名)策略,浏览器要阻止Ajax请求,但无法阻止
function getContent() {
var tag = document.createElement("script"); // 创建一个 script 标签 tag.src = 'http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301';
document.head.appendChild(tag); // 添加到 head标签中 }
现在访问 http://127.0.0.1:8000/req/ 页面,点击页面下方的获取数据,在Elements 选项卡下的源码中的 head 标签中就能看添加的 script标签。如图1-6所示。图1-6 使用JS来添加script标签
现在虽然能够正常发送请求,但是好像并没有什么用。为了说明它的用处,这里在urls.py文件再增加一个URL,该URL代码如下所示:
位置:michael_06_blog\urls.py
re_path('^jsonpexample.html$', a2.jsonpexample),
添加完URL后,先将req.html文件中的JavaScript的代码进行修改,修改后的代码如下所示:
位置:templates\req.html 中的JavaScript代码:
function getContent() {
var tag = document.createElement("script"); // 创建一个 script 标签 tag.src = 'http://127.0.0.1:8000/jsonpexample.html?callback=foo&k1=python&k2=linux';
document.head.appendChild(tag); // 添加到 head标签中 document.head.removeChild(tag); // 移除标签 }
function foo(args1, arg2) {
console.log(args1, arg2);
alert(args1, arg2);
}
在这次的JS代码中,src属性指向的这是我这次新增URL,URL后面是发送GET请求时传递的参数。这里传递的参数是:callback=foo&k1=python&k2=linux,后台可通过 request.GET.get('callback') 的方式获取到参数的值。这里假设callback参数传递的是函数名,k1、k2传递的是函数需要的参数。callback传递的是什么函数名,在下面定义的函数 function foo() 的函数名就是什么。
现在在app02下的views.py文件中增加一个 jsonpexample() 函数,这个函数的代码如下所示:
from django.shortcuts import HttpResponse
def jsonpexample(request):
func = request.GET.get('callback')
k1 = request.GET.get('k1')
k2 = request.GET.get('k2')
content = '%s("%s", "%s")' % (func, k1, k2)
print(request.GET)
return HttpResponse(content)
jsonpexample()函数的代码中获取GET请求中参数的值,并将这个获取到值拼接成一个函数字符串,有参数的话还可以传上参数。现在刷新页面 http://127.0.0.1:8000/req/,点击 获取数据,在Console上可以看到输出,并且页面上也有一个弹出框。这样就说明了成功获取到script标签的src属性指向的网站的内容。在JS的代码中可以对获取到的值做各种各样的处理。这就是JSONP跨域请求。
JSONP跨域的巧妙之处:
首先创建script标签;
其次让标签的src属性等于 远程地址,远程地址的返回的内容就能拿到;
返回的数据必须是符合JS格式;
JSONP只能发GET请求,不能发POST请求。
三、 JSONP跨域请求jQuery方式
function getContent() {
$.ajax({
url: 'http://www.jxntv.cn/data/jmd-jxtv2.html?_=1454376870403',
type: 'GET',
datatype: 'jsonp',
jsonp: 'callback', // 这样写上后相当于在URL中加 callback=list jsonpCallback: 'list', // 回调函数,这时可以去掉URL中的 callback=list })
}
function list(args) {
console.log(args);
}
这时刷新 http://127.0.0.1:8000/req/ 页面,点击 获取数据,在Console下的输出可以看到成功获取到指定网站的数据。
四、 XSS过滤
在开发博客系统时,博客内容在编辑的时候,可以有各种样式设置,还可以在内容中写入HTML标签,这时提交到后台的内容是字符串,在页面上显示的也是字符串。但是在编辑博客内容时,如果切换到HTML源代码模式,在源码中写入HTML标签,这时提交到后台,后台对提交过来的内容就要做一些标签过滤,如果不对标签过滤,用户提交过来的HTML代码含有恶意代码,恶意代码也会发布到博客中。下面的代码是对标签进行过滤的一个简单示例:
from bs4 import BeautifulSoup
content = """
hello world michael
linux
"""tags = {'p': ['class'], 'strong': ['id']} # 白名单
soup = BeautifulSoup(content, 'html.parser')
for tag in soup.find_all():
if tag.name in tags: # 在白名单中的标签,不做处理
pass
else:
tag.hidden = True # 不在白名单中的标签,可以隐藏,也可以清除
tag.clear()
continue
input_attrs = tag.attrs # 获取到的是字典:{'class': 'c1', 'id': 'i1'}
valid_attrs = tags[tag.name] # ['class']
for k in list(input_attrs.keys()): # 不能直接删除字典的键,先将字典的键转换成列表,循环列表来删除字典的键值
if k in valid_attrs:
pass
else:
del tag.attrs[k]
content = soup.decode()
print(content)
这里对标签的过滤,采用了beautifulsoup4模块。
为了进一步说明过滤方法,先来看一个类的单实例模式。
1、 类的单例模式
现在考虑下面这段代码:
class Foo(object):
instance = None
def __init__(self):
self.name = 'michael'
@classmethod
def get_instance(cls):
if Foo.instance:
return Foo.instance
else:
Foo.instance = Foo()
return Foo.instance
def process(self):
return '123'
obj1 = Foo() # 创建实例 obj1
obj2 = Foo() # 创建实例 obj2
print(id(obj1),id(obj2)) # 我这里输出的是:1273894384864 1273894384920
print(obj1 is obj2) # 输出False
obj3 = Foo.get_instance() # 创建实例 obj3
obj4 = Foo.get_instance() # 创建实例 obj4
print(id(obj3),id(obj4)) # 我这里输出的是:1273894385032 1273894385032
print(obj3 is obj4) # 输出 True
这段代码中,定义了一个类Foo,在这个类中的 get_instance() 函数使用了 @classmethod 进行包装,从后面的创建实例obj1,obj2和obj3,obj4可以看出,obj1与obj2是两个不同的实例,他们的ID号也不一样。但在创建obj3和obj4实例时,调用的是Foo类的get_instance()方法创建,创建出来的实例是同一个,他们ID号也是相同的。这就是类的单实例模式。但是这种创建类的单实例模式的方法显得有些不方便,在类中还有一个 __new__()可以完成这件事情。例如下面代码所示:
class Foo(object):
instance = None
def __init__(self):
self.name = 'michael'
def __new__(cls, *args, **kwargs):
if Foo.instance:
return Foo.instance
else:
Foo.instance = object.__new__(cls, *args, **kwargs)
return Foo.instance
obj1 = Foo()
obj2 = Foo()
print(id(obj1),id(obj2)) # 输出:2805915207608 2805915207608
print(obj1 is obj2) # 输出:True
在这次的Foo类中,定义了__new__()方法,这个方法要先于__init__()方法执行,这次创建的两个实例obj1和obj2是同一个实例,创建实例的方法也同平时创建实例的方法一样。这就是类的单例模式。
2、 XSS过滤实例
理解上面说的类的单例模式后,现在假设有下面这个XSSFilter类,文件名暂命名为xss.py,该文件可以放在项目文件夹下,例如可创建一个utils(工具)文件夹,将xss.py文件放在这里面。代码如下所示:
from bs4 import BeautifulSoup
class XSSFilter(object):
__instance = None
def __init__(self):
# XSS白名单
self.valid_tags = {
"font": ['color', 'size', 'face', 'style'],
'b': [],
'div': [],
"span": [],
"table": ['border', 'cellspacing', 'cellpadding'],
'th': ['colspan', 'rowspan'],
'td': ['colspan', 'rowspan'],
"a": ['href', 'target', 'name'],
"img": ['src', 'alt', 'title'],
'p': ['align'],
"pre": ['class'],
"hr": ['class'],
'strong': []
}
def __new__(cls, *args, **kwargs):
"""单例模式:param cls::param args::param kwargs::return:"""
if not cls.__instance:
obj = object.__new__(cls, *args, **kwargs)
cls.__instance = obj
return cls.__instance
def process(self, content):
soup = BeautifulSoup(content, 'html.parser')
# 遍历所有HTML标签
for tag in soup.find_all(recursive=True):
# 判断标签名是否在白名单中
if tag.name not in self.valid_tags:
tag.hidden = True
if tag.name not in ['html', 'body']:
tag.hidden = True
tag.clear()
continue
# 当前标签的所有属性白名单
attr_rules = self.valid_tags[tag.name]
keys = list(tag.attrs.keys())
for key in keys:
if key not in attr_rules:
del tag[key]
return soup.decode()
现在在app目录下的views.py文件中就可导入这个XSSFilter类,并调用其process方法对标签进行过滤。
from utils.xss import XSSFilter
content = XSSFilter().process(content)
content参数就是用户提交过来的文章内容。这样当有多个用户同时提交文章时,这里只创建一个实例对文章进行过滤,避免同时创建很实例的情况。
博客实验:
1、给每条博客增加评论功能;
2、给每条博客增加点赞功能;
五、CMDB简介
了解ITIL:TIL即IT基础架构库(Information Technology Infrastructure Library, ITIL,信息技术基础架构库)由英国政府部门CCTA(Central Computing and Telecommunications Agency)在20世纪80年代末制订,现由英国商务部OGC(Office of Government Commerce)负责管理,主要适用于IT服务管理(ITSM)。ITIL为企业的IT服务管理实践提供了一个客观、严谨、可量化的标准和规范。主要分以下5个管理项目:
事件管理(Incident Management)、问题管理(Problem Management)、配置管理(Configuration Management)、变更管理(Change Management)、发布管理(Release Management)。
CMDB --Configuration Management Database 配置管理数据库, CMDB存储与管理企业IT架构中设备的各种配置信息,它与所有服务支持和服务交付流程都紧密相联,支持这些流程的运转、发挥配置信息的价值,同时依赖于相关流程保证数据的准确性。
在实际的项目中,CMDB常常被认为是构建其它ITIL流程的基础而优先考虑,ITIL项目的成败与是否成功建立CMDB有非常大的关系。
CMDB初期要实现以下功能:
- 资产自动收集
- API URL
- 可视化管理
资产自动收集有下面4种方式可完成。
1、可使用paramiko模块来实现大致流程如下图1-7所示:图1-7 资产自动收集流程
通过API(CMDB中控机)获取主机名,利用paramiko链接服务器获取数据,解析成字典通过API入库。
优点是:无任何依赖。缺点是:慢。公司服务器少的可以这样做。
2、使用saltstack来实现,通过API获取主机名,利用salt api 链接服务器获取数据,解析成字典通过API入库。
优点是:无依赖。缺点是:有点慢。
3、使用puppet来实现:
(1)、知识点:
master
slave: certname(每个文件中都有这一项,唯一标识)
slave: certname
配置文件:有默认值,默认30分钟 master 与 slave 进行一次连接
(2)、report报表功能
每次交互时,在master上要生成一个文件。是配置文件的 report 中配置在指定目录生成一个ymal文件。
report: cmdb # 每30分钟交互时,会执行指定目录下的 cmdb.rb 文件中的 process 函数。放置在
【/usr/lib/ruby/site_ruby/2.2/puppet/reports/】路径下
(3)、自定义factor
4、Agent:基于shell命令来实现:
缺点:每台有 agent
优点:快
v = subprocess.getoutput('hostname') # 在本地执行命令 hostname ,将结果给v变量。
这4种方式本质上都是执行shell命令,获取结果。