天天看點

Saltstack Automatic grouping

一、要點:

知道key驗證存放的目錄

key在驗證是手動還自動

對不線上的主機的處理 

minion_id的命名規範

二、使用的技術棧

saltstack 相關的庫:salt.config,salt.client,salt.runner

使用redis 存放兩個資料庫,第一個為存為字典,用于存放minion_id與實體IP的對應,另一個存為集合,用于項目_業務命名的方式包含相應的主機

使用watchdog對目錄變更監控,事件觸發機制

三、完整代碼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

<code># coding:utf8</code>

<code># Created by IntelliJ IDEA.</code>

<code># User: King.gp</code>

<code># Date: 2016/10/9</code>

<code># Time: 10:55</code>

<code># To change this template use File | Settings | File Templates.</code>

<code>import</code> <code>time</code>

<code>import</code> <code>re</code>

<code>import</code> <code>sys</code>

<code>import</code> <code>redis</code>

<code>import</code> <code>logging</code>

<code>import</code> <code>salt.config as saltc</code>

<code>import</code> <code>salt.runner as saltr</code>

<code>import</code> <code>salt.client as SaltC</code>

<code>from</code> <code>watchdog.observers </code><code>import</code> <code>Observer</code>

<code>from</code> <code>watchdog.events </code><code>import</code> <code>FileSystemEventHandler</code>

<code>logging.basicConfig(level</code><code>=</code><code>logging.DEBUG,</code>

<code>                    </code><code>format</code><code>=</code><code>'%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s'</code><code>,</code>

<code>                    </code><code>datefmt</code><code>=</code><code>'%a, %d %b %Y %H:%M:%S'</code><code>,</code>

<code>                    </code><code>filename</code><code>=</code><code>'myapp.log'</code><code>, )</code>

<code>#################################################################################################</code>

<code># 定義一個StreamHandler,将INFO級别或更高的日志資訊列印到标準錯誤,并将其添加到目前的日志處理對象#</code>

<code># console = logging.StreamHandler()</code>

<code># console.setLevel(logging.INFO)</code>

<code># formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')</code>

<code># console.setFormatter(formatter)</code>

<code># logging.getLogger('').addHandler(console)</code>

<code>################################################################################################</code>

<code>class</code> <code>MonitorHandler(FileSystemEventHandler):</code>

<code>    </code><code># time</code>

<code>    </code><code>localtime </code><code>=</code> <code>time.asctime(time.localtime(time.time()))</code>

<code>    </code><code># 檔案或目錄被移動</code>

<code>    </code><code>def</code> <code>on_moved(</code><code>self</code><code>, event):</code>

<code>        </code><code>logging.info(MonitorHandler.localtime </code><code>+</code> <code>" - Event :檔案/目錄: {0} 被移動!"</code><code>.</code><code>format</code><code>(event.src_path))</code>

<code>        </code><code>call_func()</code>

<code>    </code><code>#  檔案或目錄被建立</code>

<code>    </code><code>def</code> <code>on_created(</code><code>self</code><code>, event):</code>

<code>        </code><code>logging.info(MonitorHandler.localtime </code><code>+</code> <code>" - Event: 檔案/目錄: {0} 被建立"</code><code>.</code><code>format</code><code>(event.src_path))</code>

<code>    </code><code># 檔案或目錄被删除</code>

<code>    </code><code>def</code> <code>on_deleted(</code><code>self</code><code>, event):</code>

<code>        </code><code>logging.info(MonitorHandler.localtime </code><code>+</code> <code>" - Event :檔案/目錄: {0} 被删除!"</code><code>.</code><code>format</code><code>(event.src_path))</code>

<code>    </code><code># 檔案或目錄被修改</code>

<code>    </code><code>def</code> <code>on_modified(</code><code>self</code><code>, event):</code>

<code>        </code><code>logging.info(MonitorHandler.localtime </code><code>+</code> <code>" - Event :檔案/目錄: {0} 被修改!"</code><code>.</code><code>format</code><code>(event.src_path))</code>

<code>def</code> <code>client():</code>

<code>    </code><code>slc </code><code>=</code> <code>SaltC.LocalClient()</code>

<code>    </code><code>return</code> <code>slc</code>

<code>class</code> <code>NodeGroup(</code><code>object</code><code>):</code>

<code>    </code><code>def</code> <code>__init__(</code><code>self</code><code>):</code>

<code>        </code><code>self</code><code>.member </code><code>=</code> <code>redis.StrictRedis(host</code><code>=</code><code>'127.0.0.1'</code><code>,</code>

<code>                                        </code><code>port</code><code>=</code><code>6379</code><code>,</code>

<code>                                        </code><code>db</code><code>=</code><code>6</code><code>, )  </code><code># 字典資料</code>

<code>        </code><code>self</code><code>.project </code><code>=</code> <code>redis.StrictRedis(host</code><code>=</code><code>'127.0.0.1'</code><code>,</code>

<code>                                         </code><code>port</code><code>=</code><code>6379</code><code>,</code>

<code>                                         </code><code>db</code><code>=</code><code>7</code><code>, )  </code><code># 集合資料</code>

<code>    </code><code>def</code> <code>update_info(</code><code>self</code><code>):</code>

<code>        </code><code>self</code><code>.member.flushdb()</code>

<code>        </code><code>self</code><code>.project.flushdb()</code>

<code>        </code><code>with </code><code>open</code><code>(</code><code>'host'</code><code>, </code><code>'rb'</code><code>) as fd:</code>

<code>            </code><code>lines </code><code>=</code> <code>fd.readlines()</code>

<code>        </code><code>pattern </code><code>=</code> <code>re.</code><code>compile</code><code>(r</code><code>'[a-z]+'</code><code>)</code>

<code>        </code><code>for</code> <code>line </code><code>in</code> <code>lines:</code>

<code>            </code><code>line </code><code>=</code> <code>line.strip().split()  </code><code># 1.2.3.4 web01.crms.com</code>

<code>            </code><code>self</code><code>.member.</code><code>set</code><code>(line[</code><code>1</code><code>], line[</code><code>0</code><code>])  </code><code># web01.crms.com</code>

<code>            </code><code>profession_number </code><code>=</code> <code>line[</code><code>1</code><code>].split(</code><code>"."</code><code>)[</code><code>0</code><code>]  </code><code># web01</code>

<code>            </code><code>m </code><code>=</code> <code>re.match(pattern, profession_number)</code>

<code>            </code><code>if</code> <code>m:</code>

<code>                </code><code>profession </code><code>=</code> <code>m.group()</code>

<code>                </code><code>project_suffix </code><code>=</code> <code>line[</code><code>1</code><code>].split(</code><code>'.'</code><code>)[</code><code>-</code><code>2</code><code>:]</code>

<code>                </code><code>project </code><code>=</code> <code>'.'</code><code>.join(project_suffix)  </code><code># crms.com</code>

<code>                </code><code>self</code><code>.project.sadd(</code><code>'_'</code><code>.join([project, profession]), '</code><code>')  # crms.com_web: '</code><code>'</code>

<code>        </code><code>member_list </code><code>=</code> <code>[]</code>

<code>        </code><code>match_dict </code><code>=</code> <code>{}</code>

<code>        </code><code>for</code> <code>category </code><code>in</code> <code>self</code><code>.project.keys():</code>

<code>            </code><code>category_pattern </code><code>=</code> <code>re.</code><code>compile</code><code>(category.split(</code><code>"_"</code><code>)[</code><code>1</code><code>])  </code><code># vm, logstash, elasticsearch</code>

<code>            </code><code>pattern_member_list </code><code>=</code> <code>[]</code>

<code>            </code><code>for</code> <code>key </code><code>in</code> <code>self</code><code>.member.keys():</code>

<code>                </code><code># ['test.logstash.yinker.com', 'test.elastic.yinker.com', 'test.vm.yinker.com']</code>

<code>                </code><code>category_match </code><code>=</code> <code>category_pattern.search(key)  </code><code># web we01.crms.com</code>

<code>                </code><code>if</code> <code>category_match:</code>

<code>                    </code><code>pattern_member_list.append(key)</code>

<code>                    </code><code>match_dict[category_pattern.pattern] </code><code>=</code> <code>pattern_member_list</code>

<code>                    </code><code>if</code> <code>match_dict </code><code>not</code> <code>in</code> <code>member_list:</code>

<code>                        </code><code>member_list.append(match_dict)</code>

<code>        </code><code># members_dict = {}</code>

<code>        </code><code>for</code> <code>p </code><code>in</code> <code>self</code><code>.project.keys():</code>

<code>            </code><code>pattern </code><code>=</code> <code>re.</code><code>compile</code><code>(p.split(</code><code>"_"</code><code>)[</code><code>0</code><code>])</code>

<code>            </code><code>for</code> <code>k, values </code><code>in</code> <code>member_list[</code><code>0</code><code>].items():</code>

<code>                </code><code>members_list </code><code>=</code> <code>[]</code>

<code>                </code><code>for</code> <code>v </code><code>in</code> <code>values:</code>

<code>                    </code><code>match </code><code>=</code> <code>pattern.search(v)  </code><code># suffix ,</code>

<code>                    </code><code>if</code> <code>match:</code>

<code>                        </code><code>members_list.append(match.string)</code>

<code>                    </code><code>else</code><code>:</code>

<code>                        </code><code>continue</code>

<code>                        </code><code># members_dict['_'.join([pattern.pattern, k])] = members_list</code>

<code>                </code><code># print members_list</code>

<code>                </code><code>self</code><code>.project.sadd(</code><code>'_'</code><code>.join([pattern.pattern, k]), members_list)</code>

<code>                </code><code>self</code><code>.project.srem(</code><code>'_'</code><code>.join([pattern.pattern, k]), '')</code>

<code>    </code><code>def</code> <code>append_config(</code><code>self</code><code>):</code>

<code>        </code><code>with </code><code>open</code><code>(</code><code>"/etc/salt/master.d/nodegroups.conf"</code><code>, </code><code>"w"</code><code>) as fd:</code>

<code>            </code><code>fd.write(</code><code>"nodegroups:\n"</code><code>)</code>

<code>        </code><code>with </code><code>open</code><code>(</code><code>"/etc/salt/master.d/nodegroups.conf"</code><code>, </code><code>"a+"</code><code>)as f:</code>

<code>            </code><code>for</code> <code>mem </code><code>in</code> <code>self</code><code>.project.keys():</code>

<code>                </code><code># zookeeperg0: '[email protected]</code>

<code>                </code><code># ["['idp01.crms.com','idp02.crms.com']"]</code>

<code>                </code><code>f.write(</code><code>"  "</code> <code>+</code> <code>mem </code><code>+</code> <code>": "</code> <code>+</code> <code>"'" + "L@" + (''.join(</code>

<code>                    </code><code>[i.replace("['", '').replace("']", '').replace("'"</code><code>, '') </code><code>for</code> <code>i </code><code>in</code>

<code>                     </code><code>self</code><code>.project.smembers(mem)]) </code><code>+</code> <code>"'"</code> <code>+</code> <code>'\n</code><code>').replace('</code> <code>', '</code><code>'))</code>

<code>def</code> <code>control_hosts():</code>

<code>    </code><code>priv_opts </code><code>=</code> <code>saltc.master_config(</code><code>'/etc/salt/master'</code><code>)</code>

<code>    </code><code>priv_runner </code><code>=</code> <code>saltr.RunnerClient(priv_opts)</code>

<code>    </code><code># r = priv_runner()</code>

<code>    </code><code>host_down </code><code>=</code> <code>priv_runner.cmd(</code><code>"manage.down"</code><code>, [</code><code>"removekeys=True"</code><code>])  </code><code># 移除DOWN狀态的minion</code>

<code>    </code><code>if</code> <code>len</code><code>(host_down) &gt; </code><code>0</code><code>:</code>

<code>        </code><code>logging.info(MonitorHandler.localtime </code><code>+</code> <code>"minion_id: {0} 己經下線 "</code><code>.</code><code>format</code><code>(</code><code>' '</code><code>.join(host_down)))</code>

<code>    </code><code>local </code><code>=</code> <code>client()</code>

<code>    </code><code>host_items </code><code>=</code> <code>local.cmd(</code><code>"*"</code><code>, </code><code>"grains.item"</code><code>, [</code><code>'ip4_interfaces'</code><code>, </code><code>'id'</code><code>])</code>

<code>    </code><code>time.sleep(</code><code>3</code><code>)</code>

<code>    </code><code>if</code> <code>host_items:</code>

<code>        </code><code>raw </code><code>=</code> <code>''</code>

<code>        </code><code>for</code> <code>v </code><code>in</code> <code>host_items.values():</code>

<code>            </code><code>raw </code><code>=</code> <code>'{0}{1}\n'</code><code>.</code><code>format</code><code>(raw, </code><code>' '</code><code>.join([v[</code><code>'ip4_interfaces'</code><code>][</code><code>'eth0'</code><code>][</code><code>0</code><code>], v[</code><code>'id'</code><code>]]))</code>

<code>        </code><code>with </code><code>open</code><code>(</code><code>"host"</code><code>, </code><code>"w+"</code><code>) as f:</code>

<code>            </code><code>f.write(raw)</code>

<code>        </code><code>return</code> <code>True</code>

<code>    </code><code>else</code><code>:</code>

<code>        </code><code>logging.error(MonitorHandler.localtime </code><code>+</code> <code>"拉取grains資訊失敗"</code><code>)</code>

<code>        </code><code>return</code> <code>False</code>

<code>def</code> <code>call_func():</code>

<code>    </code><code>status </code><code>=</code> <code>control_hosts()</code>

<code>    </code><code>if</code> <code>status:</code>

<code>        </code><code>group_agg </code><code>=</code> <code>NodeGroup()</code>

<code>        </code><code>group_agg.update_info()</code>

<code>        </code><code>group_agg.append_config()</code>

<code>        </code><code>sys.exit(status</code><code>=</code><code>127</code><code>)</code>

<code>if</code> <code>__name__ </code><code>=</code><code>=</code> <code>"__main__"</code><code>:</code>

<code>    </code><code>Monitor_Dir </code><code>=</code> <code>'/etc/salt/pki/master/minions'</code>

<code>    </code><code>event_handler </code><code>=</code> <code>MonitorHandler()</code>

<code>    </code><code>observer </code><code>=</code> <code>Observer()</code>

<code>    </code><code>NodeGroup_File </code><code>=</code> <code>'/etc/salt/master.d/nodegroups.conf'</code>

<code>    </code><code>observer.schedule(event_handler, path</code><code>=</code><code>Monitor_Dir, recursive</code><code>=</code><code>True</code><code>)</code>

<code>    </code><code>observer.start()</code>

<code>    </code><code>try</code><code>:</code>

<code>        </code><code>while</code> <code>True</code><code>:</code>

<code>            </code><code>time.sleep(</code><code>10</code><code>)</code>

<code>    </code><code>except</code> <code>KeyboardInterrupt:</code>

<code>        </code><code>observer.stop()</code>

<code>    </code><code>observer.join()</code>

四、關于缺陷

關于怎麼判斷minion不線上的界定,是因為網絡抖動,還是真的下線;

如果出來這樣的問題是移除,還是不處理,如何取舍,因為這涉及一個問題,利用分組無法準确的執行任務,因為分組依賴于salt的允許的key對應的主機;

啟動時會提示遞歸溢出,所有移除的主機都會輸出到console上;

請自行添加到supervisor中。

本文轉自 jinlinger 51CTO部落格,原文連結:http://blog.51cto.com/essun/1959970,如需轉載請自行聯系原作者

繼續閱讀