# Copyright (c) 2010-2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, e
ither express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import unittest
from webob import Request
from swift.common.middleware import recon
from unittest import TestCase
from contextlib import contextmanager
from posix import stat_result, statvfs_result
import os
import swift.common.constraints


class FakeApp(object):
    def __call__(self, env, start_response):
        return "FAKE APP"

def st
art_response(*args):
    pass

class OpenAndReadTester(object):

    def __init__(self, output_iter):
        self.index = 0
        self.out_len = len(output_iter) - 1
        self.data = output_iter
        self.output_iter = iter(output_iter)
        self.read_calls = []
        self.open_calls = []

    def __iter__(self):
        return self

    def next(self):
        if self.index == self.out_len:
            raise StopIteration
        else:
            line = self.data[self.index]
            self.index += 1
        return line

    def read(self, *args, **kwargs):
        self.read_calls.append((args, kwargs))
        try:
            return self.output_iter.next()
        except StopIteration:
            return ''

    @contextmanager
    def open(self, *args, **kwargs):
        self.open_calls.append((args, kwargs))
        yield self

class MockOS(object):

    def __init__(self, ls_out=None, pe_out=None, statvfs_out=None,
                 lstat_out=(1, 1, 5, 4, 5, 5, 55, 55, 55, 55)):
        self.ls_output = ls_out
        self.path_exists_output = pe_out
        self.statvfs_output = statvfs_out
        self.lstat_output_tuple = lstat_out
        self.listdir_calls = []
        self.statvfs_calls = []
        self.path_exists_calls = []
        self.lstat_calls = []

    def fake_listdir(self, *args, **kwargs):
        self.listdir_calls.append((args, kwargs))
        return self.ls_output

    def fake_path_exists(self, *args, **kwargs):
        self.path_exists_calls.append((args, kwargs))
        return self.path_exists_output

    def fake_statvfs(self, *args, **kwargs):
        self.statvfs_calls.append((args, kwargs))
        return statvfs_result(self.statvfs_output)

    def fake_lstat(self, *args, **kwargs):
        self.lstat_calls.append((args, kwargs))
        return stat_result(self.lstat_output_tuple)



class TestReconSuccess(TestCase):

    def setUp(self):
        self.app = recon.ReconMiddleware(FakeApp(), {})
        self.mockos = MockOS()
        self.real_listdir = os.listdir
        self.real_path_exists = os.path.exists
        self.real_lstat = os.lstat
        self.real_statvfs = os.statvfs
        os.listdir = self.mockos.fake_listdir
        os.path.exists = self.mockos.fake_path_exists
        os.lstat = self.mockos.fake_lstat
        os.statvfs = self.mockos.fake_statvfs

    def tearDown(self):
        os.listdir = self.real_listdir
        os.path.exists = self.real_path_exists
        os.lstat = self.real_lstat
        os.statvfs = self.real_statvfs
        del self.mockos

    def test_get_mounted(self):
        mounts_content = ['rootfs / rootfs rw 0 0',
                          'none /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0',
                          'none /proc proc rw,nosuid,nodev,noexec,relatime 0 0',
                          'none /dev devtmpfs rw,relatime,size=248404k,nr_inodes=62101,mode=755 0 0',
                          'none /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0',
                          '/dev/disk/by-uuid/e5b143bd-9f31-49a7-b018-5e037dc59252 / ext4 rw,relatime,errors=remount-ro,barrier=1,data=ordered 0 0',
                          'none /sys/fs/fuse/connections fusectl rw,relatime 0 0',
                          'none /sys/kernel/debug debugfs rw,relatime 0 0',
                          'none /sys/kernel/security securityfs rw,relatime 0 0',
                          'none /dev/shm tmpfs rw,nosuid,nodev,relatime 0 0',
                          'none /var/run tmpfs rw,nosuid,relatime,mode=755 0 0',
                          'none /var/lock tmpfs rw,nosuid,nodev,noexec,relatime 0 0',
                          'none /lib/init/rw tmpfs rw,nosuid,relatime,mode=755 0 0',
                          '/dev/loop0 /mnt/sdb1 xfs rw,noatime,nodiratime,attr2,nobarrier,logbufs=8,noquota 0 0',
                          'rpc_pipefs /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0',
                          'nfsd /proc/fs/nfsd nfsd rw,relatime 0 0',
                          'none /proc/fs/vmblock/mountPoint vmblock rw,relatime 0 0',
                          '']
        mounted_resp = [{'device': 'rootfs', 'path': '/'},
                        {'device': 'none', 'path': '/sys'},
                        {'device': 'none', 'path': '/proc'},
                        {'device': 'none', 'path': '/dev'},
                        {'device': 'none', 'path': '/dev/pts'},
                        {'device': '/dev/disk/by-uuid/e5b143bd-9f31-49a7-b018-5e037dc59252', 'path': '/'},
                        {'device': 'none', 'path': '/sys/fs/fuse/connections'},
                        {'device': 'none', 'path': '/sys/kernel/debug'},
                        {'device': 'none', 'path': '/sys/kernel/security'},
                        {'device': 'none', 'path': '/dev/shm'},
                        {'device': 'none', 'path': '/var/run'},
                        {'device': 'none', 'path': '/var/lock'},
                        {'device': 'none', 'path': '/lib/init/rw'},
                        {'device': '/dev/loop0', 'path': '/mnt/sdb1'},
                        {'device': 'rpc_pipefs', 'path': '/var/lib/nfs/rpc_pipefs'},
                        {'device': 'nfsd', 'path': '/proc/fs/nfsd'},
                        {'device': 'none', 'path': '/proc/fs/vmblock/mountPoint'}]
        oart = OpenAndReadTester(mounts_content)
        rv = self.app.get_mounted(openr=oart.open)
        self.assertEquals(oart.open_calls, [(('/proc/mounts', 'r'), {})])
        self.assertEquals(rv, mounted_resp)

    def test_get_load(self):
        oart = OpenAndReadTester(['0.03 0.03 0.00 1/220 16306'])
        rv = self.app.get_load(openr=oart.open)
        self.assertEquals(oart.read_calls, [((), {})])
        self.assertEquals(oart.open_calls, [(('/proc/loadavg', 'r'), {})])
        self.assertEquals(rv, {'5m': 0.029999999999999999, '15m': 0.0,
                               'processes': 16306, 'tasks': '1/220',
                               '1m': 0.029999999999999999})

    def test_get_mem(self):
        meminfo_content = ['MemTotal:         505840 kB',
                           'MemFree:           26588 kB',
                           'Buffers:           44948 kB',
                           'Cached:           146376 kB',
                           'SwapCached:        14736 kB',
                           'Active:           194900 kB',
                           'Inactive:         193412 kB',
                           'Active(anon):      94208 kB',
                           'Inactive(anon):   102848 kB',
                           'Active(file):     100692 kB',
                           'Inactive(file):    90564 kB',
                           'Unevictable:           0 kB',
                           'Mlocked:               0 kB',
                           'SwapTotal:        407544 kB',
                           'SwapFree:         313436 kB',
                           'Dirty:               104 kB',
                           'Writeback:             0 kB',
                           'AnonPages:        185268 kB',
                           'Mapped:             9592 kB',
                           'Shmem:                68 kB',
                        