summaryrefslogtreecommitdiffstats
path: root/extras/prot_filter.py
blob: 7dccacf155ee7b5a0f85db25a280adc810fbe076 (plain)
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
#!/usr/bin/python

"""
  Copyright (c) 2013 Red Hat, Inc. <http://www.redhat.com>
  This file is part of GlusterFS.

  This file is licensed to you under your choice of the GNU Lesser
  General Public License, version 3 or any later version (LGPLv3 or
  later), or the GNU General Public License, version 2 (GPLv2), in all
  cases as published by the Free Software Foundation.
"""

"""
  INSTRUCTIONS
  Put this in /usr/lib64/glusterfs/$version/filter to have it run automatically,
  or else you'll have to run it by hand every time you change the volume
  configuration.  Give it a list of volume names on which to enable the
  protection functionality; it will deliberately ignore client volfiles for
  other volumes, and all server volfiles.  It *will* include internal client
  volfiles such as those used for NFS or rebalance/self-heal; this is a
  deliberate choice so that it will catch deletions from those sources as well.
"""

volume_list = [ "jdtest" ]

import copy
import string
import sys
import types

class Translator:
    def __init__ (self, name):
        self.name = name
        self.xl_type = ""
        self.opts = {}
        self.subvols = []
        self.dumped = False
    def __repr__ (self):
        return "<Translator %s>" % self.name

def load (path):
    # If it's a string, open it; otherwise, assume it's already a
    # file-like object (most notably from urllib*).
    if type(path) in types.StringTypes:
        fp = file(path,"r")
    else:
        fp = path
    all_xlators = {}
    xlator = None
    last_xlator = None
    while True:
        text = fp.readline()
        if text == "":
            break
        text = text.split()
        if not len(text):
            continue
        if text[0] == "volume":
            if xlator:
                raise RuntimeError, "nested volume definition"
            xlator = Translator(text[1])
            continue
        if not xlator:
            raise RuntimeError, "text outside volume definition"
        if text[0] == "type":
            xlator.xl_type = text[1]
            continue
        if text[0] == "option":
            xlator.opts[text[1]] = string.join(text[2:])
            continue
        if text[0] == "subvolumes":
            for sv in text[1:]:
                xlator.subvols.append(all_xlators[sv])
            continue
        if text[0] == "end-volume":
            all_xlators[xlator.name] = xlator
            last_xlator = xlator
            xlator = None
            continue
        raise RuntimeError, "unrecognized keyword %s" % text[0]
    if xlator:
        raise RuntimeError, "unclosed volume definition"
    return all_xlators, last_xlator

def generate (graph, last, stream=sys.stdout):
    for sv in last.subvols:
        if not sv.dumped:
            generate(graph,sv,stream)
            print >> stream, ""
            sv.dumped = True
    print >> stream, "volume %s" % last.name
    print >> stream, "    type %s" % last.xl_type
    for k, v in last.opts.iteritems():
        print >> stream, "    option %s %s" % (k, v)
    if last.subvols:
        print >> stream, "    subvolumes %s" % string.join(
            [ sv.name for sv in last.subvols ])
    print >> stream, "end-volume"

def push_filter (graph, old_xl, filt_type, opts={}):
    new_type = "-" + filt_type.split("/")[1]
    old_type = "-" + old_xl.xl_type.split("/")[1]
    pos = old_xl.name.find(old_type)
    if pos >= 0:
        new_name = old_xl.name
        old_name = new_name[:pos] + new_type + new_name[len(old_type)+pos:]
    else:
        new_name = old_xl.name + old_type
        old_name = old_xl.name + new_type
    new_xl = Translator(new_name)
    new_xl.xl_type = old_xl.xl_type
    new_xl.opts = old_xl.opts
    new_xl.subvols = old_xl.subvols
    graph[new_xl.name] = new_xl
    old_xl.name = old_name
    old_xl.xl_type = filt_type
    old_xl.opts = opts
    old_xl.subvols = [new_xl]
    graph[old_xl.name] = old_xl

if __name__ == "__main__":
    path = sys.argv[1]
    # Alow an override for debugging.
    for extra in sys.argv[2:]:
        volume_list.append(extra)
    graph, last = load(path)
    for v in volume_list:
        if graph.has_key(v):
            break
    else:
        print "No configured volumes found - aborting."
        sys.exit(0)
    for v in graph.values():
        if v.xl_type == "cluster/distribute":
            push_filter(graph,v,"features/prot_dht")
        elif v.xl_type == "protocol/client":
            push_filter(graph,v,"features/prot_client")
    # We push debug/trace so that every fop gets a real frame, because DHT
    # gets confused if STACK_WIND_TAIL causes certain fops to be invoked
    # from anything other than a direct child.
    for v in graph.values():
        if v.xl_type == "features/prot_client":
            push_filter(graph,v,"debug/trace")
    generate(graph,last,stream=open(path,"w"))