#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

'''
USAGE: gp_df [-m]
platform independent df command
where -m will display output in megabytes
'''

import os, re, sys, time, subprocess

try:
    from optparse import Option, OptionParser 
    from gppylib.gpparseopts import OptParser, OptChecker
    from gppylib.commands.unix import SYSTEM
except ImportError, e:    
    sys.exit('Cannot import modules.  Please check that you have sourced greenplum_path.sh.  Detail: ' + str(e))

class BytesFormatter:
    BYTES = 1
    KILOBYTES = 2
    MEGABYTES = 3


class FileSystem:
    def __init__(self, device, dir, type):
        self.device = device
        self.dir = dir
        self.type = type
        self.total_bytes = 0
        self.used_bytes = 0
        self.available_bytes = 0

    @staticmethod
    def PrintHeader(formatter):
        if formatter == BytesFormatter.BYTES:
            used = "USED_BYTES"
            available = "AVAILABLE_BYTES"
        elif formatter == BytesFormatter.KILOBYTES:
            used = "USED_KB"
            available = "AVAILABLE_KB"
        elif formatter == BytesFormatter.MEGABYTES:
            used = "USED_MB"
            available = "AVAILABLE_MB"
        else:
            raise Exception("unknown bytes formatter")
            
        print "%s %s %s %s %s" % ("DEVICE", "DIR", "TYPE", used, available)

    def Print(self, formatter):
        try:
            if formatter == BytesFormatter.BYTES:
                used = self.used_bytes
                available = self.available_bytes
            elif formatter == BytesFormatter.KILOBYTES:
                used = int(self.used_bytes/1024)
                available = int(self.available_bytes/1024)
            elif formatter == BytesFormatter.MEGABYTES:
                used = int(self.used_bytes/1024/1024)
                available = int(self.available_bytes/1024/1024)
            else:
                raise Exception("unknown bytes formatter")
        except Exception, e:
            print >> sys.stderr, e.__str__()
            print >> sys.stderr, "Error converting disk usage to numerical form"
            sys.exit(1)
        except:
            print >> sys.stderr, "Error converting disk usage to numerical form"
            sys.exit(1)
 
        print "%s %s %s %d %d" % (self.device, self.dir, self.type, used, available)

def getLinuxDirectoryList():

    interestedFSTypes = set(['xfs', 'ext3', 'ext4'])

    fsList = []
    try:
        fd = open('/proc/mounts')
        buffer = fd.read()
        fd.close()
        for line in buffer.splitlines():
            fields = line.split()
            if len(fields) != 6:
                raise Exception("Wrong number of fields in /proc/mounts entry: %s" % line.strip())
            if fields[2] not in interestedFSTypes:
                continue
            fsList.append(FileSystem(device=fields[0], dir=fields[1], type=fields[2]))
    except Exception, e:
        print >> sys.stderr, e.__str__()
        print >> sys.stderr, "Error reading list of mounts"
        sys.exit(1)
    except:
        print >> sys.stderr, "Error reading list of mounts"
        sys.exit(1)

    return fsList


def getSolarisDirectoryList():

    cmd = "zfs list -H -t filesystem -o name,mountpoint"
    p = subprocess.Popen(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    result = p.communicate()
    errormsg = result[1].strip()
    output = result[0]
    if p.returncode:
        print >> sys.stderr, errormsg
        print >> sys.stderr, "error getting zpool information from host"
        sys.exit(1)

    fsList = []
    for line in output.splitlines():
        fields = line.split()
        if len(fields) != 2:
            print >> sys.stderr, "bad output line from zfs list: %s" % line
            continue

        # we want only zfs pools not filesystems that are a subset of a zfs pool
        fsname = fields[0].strip()
        mount = fields[1].strip()
        if re.search("/", fsname):
            continue
        fsList.append(FileSystem(device=fsname, dir=mount, type='zfs'))

    return fsList


def getDirectoryList():

    if SYSTEM.getName() == "linux":
        return getLinuxDirectoryList()
    elif SYSTEM.getName() == "sunos":
        return getSolarisDirectoryList()
    else:
        print >> sys.stderr, "Unsupported platform"
        sys.exit(1)


###### main()
if __name__ == '__main__':

    gphome = os.environ.get('GPHOME')
    if not gphome:
        print "GPHOME not set"
        sys.exit(1)

    parser = OptParser(option_class=OptChecker)
    parser.remove_option('-h')
    parser.add_option('-h', '-?', '--help', action='store_true')
    parser.add_option('-m', '--megabytes', action='store_true')
    (options, args) = parser.parse_args()

    if options.help:
        print __doc__
        sys.exit(1)

    fsList = getDirectoryList()

    for fs in fsList:
        stats = os.statvfs(fs.dir)
        fs.total_bytes =  stats.f_frsize * stats.f_blocks 
        fs.available_bytes = stats.f_frsize * stats.f_bfree
        fs.used_bytes = fs.total_bytes - fs.available_bytes

        if fs.used_bytes < 0:
            print >> sys.stderr, "statvfs output for device=%s dir=%s has more free bytes than total bytes" % (fs.device, fs.dir)
            print >> sys.stderr, stats
            sys.exit(1)

    if options.megabytes:
        formatter = BytesFormatter.MEGABYTES
    else:
        formatter = BytesFormatter.BYTES

    FileSystem.PrintHeader(formatter)
    for fs in fsList:
        fs.Print(formatter)


