1import os, string, logging, re, random, shutil
2from autotest_lib.client.bin import test, os_dep, utils
3from autotest_lib.client.common_lib import error
4
5
6def find_mnt_pt(path):
7    """
8    Find on which mount point a given path is mounted.
9
10    @param path: Path we want to figure its mount point.
11    """
12    pth = os.path.abspath(path)
13    while not os.path.ismount(pth):
14        pth = os.path.dirname(pth)
15    return pth
16
17
18class ffsb(test.test):
19    """
20    This class wraps FFSB (Flexible File System Benchmark) execution
21    under autotest.
22
23    @author Onkar N Mahajan (onkar.n.mahajan@linux.vnet.ibm.com)
24    """
25    version = 1
26    params = {}
27    tempdirs = []
28    bytes = {'K':1024 , 'k':1024,
29             'M':1048576, 'm':1048576,
30             'G':1073741824, 'g':1073741824,
31             'T':1099511627776 , 't':1099511627776}
32
33
34    def initialize(self):
35        self.job.require_gcc()
36        self.results = []
37        self.nfail = 0
38
39
40    def set_ffsb_params(self, usrfl):
41        """
42        This function checks for the user supplied FFSB profile file
43        and validates it against the availble resources on the
44        guest - currently only disk space validation is supported
45        but adjusting the number of threads according to the vcpus
46        exported by the qemu-kvm also needs to be added.
47
48        @param usrfl: Path to the user profile file.
49        """
50        d = {}
51        fr = open(usrfl,'r')
52        for line in fr.read().split('\n'):
53            p = re.compile(r'\s*\t*\[{1}filesystem(\d+)\]{1}')
54            m = p.match(line)
55            if m:
56                fsno = int(line[m.start(1):m.end(1)])
57                d[fsno] = []
58            p = re.compile(r'(\s*\t*location)\=(.*)')
59            m = p.match(line)
60            if m:
61                path = line[m.start(2):m.end(2)]
62                mntpt = find_mnt_pt(path)
63                f = os.statvfs(mntpt)
64                avl_dsk_spc = f.f_bfree * f.f_bsize
65                avl_dsk_spc *= 0.95
66                d[fsno].append(mntpt)
67                d[fsno].append(int(avl_dsk_spc))
68            p = re.compile(r'(\s*\t*num_files)\=(\d+)')
69
70            m = p.match(line)
71            if m:
72                usrnumfl = int(line[m.start(2):m.end(2)])
73                d[fsno].append(usrnumfl)
74            p = re.compile(r'(\s*\t*max_filesize)\=(\d+[kKMmGgTt]?)')
75            m = p.match(line)
76            if m:
77                usrmaxflsz = line[m.start(2):m.end(2)]
78                usrmaxflsz = int(usrmaxflsz[0:-1]) * self.bytes[usrmaxflsz[-1]]
79                d[fsno].append(usrmaxflsz)
80        for k in d.keys():
81            while d[k][2]*d[k][3] >= d[k][1]:
82                d[k][2] -= 1
83            if d[k][2] == 0:
84                d[k][2] = 1
85                d[k][3] = d[k][1]
86            # If the ffsb mount point is on the same file system
87            # then use the available disk space after the previous
88            # tests
89            for k1 in d.keys():
90                if d[k1][0] == d[k][0]:
91                    d[k1][1] -= (d[k][2]*d[k][3])
92        fr.close()
93        return d
94
95
96    def dup_ffsb_profilefl(self):
97        """
98        Validates the path from the FFSB configuration file, the
99        disk space available for the test, warn the user and
100        change the file sizes and/or number of files to be used for
101        generating the workload according to the available disk space
102        on the guest.
103        """
104        self.usrfl = '%s/%s' % (os.path.split(self.srcdir)[0],'profile.cfg')
105        self.sysfl = '%s/%s' % (self.srcdir,'profile.cfg')
106
107        params = self.set_ffsb_params(self.usrfl)
108
109        fsno = 0
110        fr = open(self.usrfl,'r')
111        fw = open(self.sysfl,'w')
112        for line in fr.read().split('\n'):
113            p = re.compile(r'\s*\t*\[{1}filesystem(\d+)\]{1}')
114            m = p.match(line)
115            if m:
116                fsno = int(line[m.start(1):m.end(1)])
117            p = re.compile(r'(\s*\t*location)\=(.*)')
118            m = p.match(line)
119            if m:
120                while True:
121                    dirnm = ''.join(random.choice(string.letters) for i in xrange(9))
122                    if line[m.end(2) - 1] == '/':
123                        newline = '%s%s' % (line[0:m.end(2)], dirnm)
124                        ffsbdir = '%s%s' % (line[m.start(2):m.end(2)], dirnm)
125                    else:
126                        newline = '%s/%s' % (line[0:m.end(2)], dirnm)
127                        ffsbdir = '%s/%s' % (line[m.start(2):m.end(2)], dirnm)
128                    self.tempdirs.append(ffsbdir)
129                    if os.path.exists(ffsbdir):
130                        continue
131                    else:
132                        os.makedirs(ffsbdir)
133                        break
134                fw.write(newline+'\n')
135                continue
136            p = re.compile(r'(\s*\t*num_files)\=(.*)')
137            m = p.match(line)
138            if m:
139                newline = '%s=%s' % (line[0:m.end(1)], str(params[fsno][2]))
140                fw.write(newline+'\n')
141                continue
142            p = re.compile(r'(\s*\t*max_filesize)\=(\d+[kKMmGgTt]?)')
143            m = p.match(line)
144            if m:
145                newline = '%s%s' % (line[0:m.start(2)], str(params[fsno][3]))
146                fw.write(newline+'\n')
147                continue
148            fw.write(line+'\n')
149        fr.close()
150        fw.close()
151
152
153    def setup(self, tarball='ffsb-6.0-rc2.tar.bz2'):
154        """
155        Uncompress the FFSB tarball and compiles it.
156
157        @param tarball: FFSB tarball. Could be either a path relative to
158                self.srcdir or a URL.
159        """
160        tarball = utils.unmap_url(self.bindir, tarball, self.tmpdir)
161        utils.extract_tarball_to_dir(tarball, self.srcdir)
162        os.chdir(self.srcdir)
163        os_dep.command('gcc')
164        utils.configure()
165        utils.make()
166
167
168    def run_once(self):
169        """
170        Runs a single iteration of the FFSB.
171        """
172        self.dup_ffsb_profilefl()
173        # Run FFSB using abspath
174        cmd = '%s/ffsb %s/profile.cfg' % (self.srcdir, self.srcdir)
175        logging.info("FFSB command: %s", cmd)
176        self.results_path = os.path.join(self.resultsdir,
177                                         'raw_output_%s' % self.iteration)
178        try:
179            self.results = utils.system_output(cmd, retain_output=True)
180            logging.info(self.results)
181            utils.open_write_close(self.results_path, self.results)
182        except error.CmdError, e:
183            self.nfail += 1
184            logging.error('Failed to execute FFSB : %s', e)
185
186
187    def postprocess(self):
188        """
189        Do test postprocessing. Fail the test or clean up results.
190        """
191        if self.nfail != 0:
192            raise error.TestError('FFSB test failed.')
193        else:
194            logging.info('FFSB test passed')
195            logging.info('Cleaning up test data...')
196            for l in self.tempdirs:
197                shutil.rmtree(l)
198