Source code for insights.parsers.nginx_conf

"""
NginxConf - file ``/etc/nginx/nginx.conf`` and other Nginx configuration files
==============================================================================

NginxConfPEG - file ``/etc/nginx/nginx.conf`` and other Nginx configuration files
=================================================================================
"""
import string

from insights.contrib.nginxparser import create_parser, UnspacedList
from insights.core import ConfigParser, LegacyItemAccess, Parser
from insights.core.plugins import parser
from insights.parsers import ParseException, get_active_lines
from insights.parsr import (EOF, EmptyQuotedString, Forward, LeftCurly, Lift, LineEnd, RightCurly,
                            Many, Number, OneLineComment, PosMarker, SemiColon, QuotedString,
                            skip_none, String, WS, WSChar)
from insights.parsr.query import Directive, Entry, Section
from insights.specs import Specs
from insights.util import deprecated


[docs]@parser(Specs.nginx_conf) class NginxConf(Parser, LegacyItemAccess): """ .. warning:: This parser is deprecated, please import :py:class:`insights.combiners.nginx_conf.NginxConfTree` instead. Parse the keyword-and-value of a Nginx configuration file. Generally, each line is split on the first space into key and value, leading and trailing space being ignored. Example nginx.conf file:: user root worker_processes 5; error_log logs/error.log; pid logs/nginx.pid; worker_rlimit_nofile 8192; events { worker_connections 4096; } mail { server_name mail.example.com; auth_http localhost:9000/cgi-bin/auth; server { listen 143; protocol imap; } } http { include /etc/nginx/conf.d/*.conf index index.html index.htm index.php; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] $status ' '"$request" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; sendfile on; tcp_nopush on; server_names_hash_bucket_size 128; server { # php/fastcgi listen 80; server_name domain1.com www.domain1.com; access_log logs/domain1.access.log main; root html; location ~ \.php$ { fastcgi_pass 127.0.0.1:1025; } } server { # simple reverse-proxy listen 80; server_name domain2.com www.domain2.com; access_log logs/domain2.access.log main; location ~ ^/(images|javascript|js|css|flash|media|static)/ { root /var/www/virtual/big.server.com/htdocs; expires 30d; } location / { proxy_pass http://127.0.0.1:8080; } } map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream websocket { server 10.66.208.205:8010; } upstream big_server_com { server 127.0.0.3:8000 weight=5; server 127.0.0.3:8001 weight=5; server 192.168.0.1:8000; server 192.168.0.1:8001; } server { # simple load balancing listen 80; server_name big.server.com; access_log logs/big.server.access.log main; location / { proxy_pass http://big_server_com; } } } Examples: >>> type(nginxconf) <class 'insights.parsers.nginx_conf.NginxConf'> >>> nginxconf['user'] 'root' >>> nginxconf['events']['worker_connections'] # Values are all kept as strings. '4096' >>> nginxconf['mail']['server'][0]['listen'] '143' >>> nginxconf['http']['access_log'] 'logs/access.log main' >>> nginxconf['http']['server'][0]['location'][0]['fastcgi_pass'] '127.0.0.1:1025' """ def __init__(self, *args, **kwargs): deprecated(NginxConf, "Import NginxConfTree from insights.combiners.nginx_conf instead") super(NginxConf, self).__init__(*args, **kwargs)
[docs] def parse_content(self, content): list_result = UnspacedList(create_parser().parseString("\n".join(get_active_lines(content))).asList()) self.data = self._convert_nginx_list_to_dict(list_result)
def _convert_nginx_list_to_dict(self, li): """ After parsed by create_parser(), the result is a list, it is better to convert to dict for convenience. """ def _list_depth_to_dict(self, li): """ Function to convert list whose depth is tree to dict. Generally, the section name would be a dict key, and content embraced by brace would be value. In some sections, the first item is like ['location', '/'], in this case, the convert rule is that 'location' would be the key, and add {"name":'/'} key-value to the dict value. """ dict_ret = {} if self._depth(li) == 1: return self._handle_key_value(dict_ret, li[0], li[1]) else: for sub_item in li[1]: tmp_dict = _list_depth_to_dict(self, sub_item) tmp_key = list(tmp_dict.keys())[0] tmp_val = tmp_dict[tmp_key] tmp_val = tmp_val[0] if isinstance(tmp_val, list) else tmp_val dict_ret.update(self._handle_key_value(dict_ret, tmp_key, tmp_val)) if len(li[0]) > 1: dict_ret["name"] = ' '.join(li[0][1:]) return {li[0][0]: dict_ret} dict_all = {} for item in li: dict_all.update(_list_depth_to_dict(self, item)) return dict_all def _handle_key_value(self, t_dict, key, value): """ Function to handle dict key has multi value, and return the values as list. """ # As it is possible that key "server", "location", "include" # and "upstream" have multi value, set the value of dict as list. if key in ("server", "location", "include", "upstream"): if key in t_dict: val = t_dict[key] val.append(value) return {key: val} return {key: [value]} else: return {key: value} def _depth(self, l): """ Function to count the depth of a list """ if isinstance(l, list) and len(l) > 0: return 1 + max(self._depth(item) for item in l) else: return 0
[docs]@parser(Specs.nginx_conf) class NginxConfPEG(ConfigParser): """ Parse the keyword-and-value of a Nginx configuration file. Example nginx.conf file:: user root worker_processes 5; error_log logs/error.log; pid logs/nginx.pid; worker_rlimit_nofile 8192; events { worker_connections 4096; } mail { server_name mail.example.com; auth_http localhost:9000/cgi-bin/auth; server { listen 143; protocol imap; } } http { include /etc/nginx/conf.d/*.conf index index.html index.htm index.php; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] $status ' '"$request" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; sendfile on; tcp_nopush on; server_names_hash_bucket_size 128; server { # php/fastcgi listen 80; server_name domain1.com www.domain1.com; access_log logs/domain1.access.log main; root html; location ~ \.php$ { fastcgi_pass 127.0.0.1:1025; } } server { # simple reverse-proxy listen 80; server_name domain2.com www.domain2.com; access_log logs/domain2.access.log main; location ~ ^/(images|javascript|js|css|flash|media|static)/ { root /var/www/virtual/big.server.com/htdocs; expires 30d; } location / { proxy_pass http://127.0.0.1:8080; } } map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream websocket { server 10.66.208.205:8010; } upstream big_server_com { server 127.0.0.3:8000 weight=5; server 127.0.0.3:8001 weight=5; server 192.168.0.1:8000; server 192.168.0.1:8001; } server { # simple load balancing listen 80; server_name big.server.com; access_log logs/big.server.access.log main; location / { proxy_pass http://big_server_com; } } } Examples: >>> type(nginxconfpeg) <class 'insights.parsers.nginx_conf.NginxConfPEG'> >>> nginxconfpeg['user'][-1].value 'root' >>> nginxconfpeg['events']['worker_connections'][-1].value 4096 >>> nginxconfpeg['mail']['server'][0]['listen'][-1].value 143 >>> nginxconfpeg['http']['access_log'][-1].value 'logs/access.log main' >>> nginxconfpeg['http']['server'][0]['location'][0]['fastcgi_pass'][-1].value '127.0.0.1:1025' """ def __init__(self, *args, **kwargs): def to_entry(name, attrs, body): if body == ";": return Directive(name=name.value, attrs=attrs, lineno=name.lineno, src=self) return Section(name=name.value, attrs=attrs, children=body, lineno=name.lineno, src=self) name_chars = string.ascii_letters + "_/" Stmt = Forward() Num = Number & (WSChar | LineEnd | SemiColon) Comment = OneLineComment("#").map(lambda x: None) BeginBlock = WS >> LeftCurly << WS EndBlock = WS >> RightCurly << WS Bare = String(set(string.printable) - (set(string.whitespace) | set("#;{}'\""))) Name = WS >> PosMarker(String(name_chars) | EmptyQuotedString(name_chars)) << WS Attr = WS >> (Num | Bare | QuotedString) << WS Attrs = Many(Attr) Block = BeginBlock >> Many(Stmt).map(skip_none) << EndBlock Stanza = (Lift(to_entry) * Name * Attrs * (Block | SemiColon)) | Comment Stmt <= WS >> Stanza << WS Doc = Many(Stmt).map(skip_none) self.Top = Doc + EOF super(NginxConfPEG, self).__init__(*args, **kwargs) def parse_doc(self, content): try: return Entry(children=self.Top("\n".join(content))[0], src=self) except Exception: raise ParseException("There was an exception when parsing the config file.")