1
0
mirror of https://github.com/gluster/glusterd2.git synced 2026-02-06 15:46:00 +01:00
Files
glusterd2/utils/utils.go
2016-12-15 14:38:14 +05:30

366 lines
9.0 KiB
Go

package utils
// #include "limits.h"
import "C"
import (
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"reflect"
"runtime"
"strings"
"syscall"
"golang.org/x/sys/unix"
log "github.com/Sirupsen/logrus"
"github.com/gluster/glusterd2/errors"
"github.com/pborman/uuid"
)
const (
testXattr = "trusted.glusterfs.test"
volumeIDXattr = "trusted.glusterfs.volume-id"
gfidXattr = "trusted.gfid"
)
var (
// PathMax calls unix.PathMax
PathMax = unix.PathMax
// Removexattr calls unix.Removexattr
Removexattr = unix.Removexattr
// Setxattr calls unix.Setxattr
Setxattr = unix.Setxattr
// Getxattr calls unix.Getxattr
Getxattr = unix.Getxattr
)
//PosixPathMax represents C's POSIX_PATH_MAX
const PosixPathMax = C._POSIX_PATH_MAX
// IsLocalAddress checks whether a given host/IP is local
// Does lookup only after string matching IP addresses
func IsLocalAddress(address string) (bool, error) {
var host string
host, _, _ = net.SplitHostPort(address)
if host == "" {
host = address
}
localNames := []string{"127.0.0.1", "localhost", "::1"}
for _, name := range localNames {
if host == name {
return true, nil
}
}
laddrs, e := net.InterfaceAddrs()
if e != nil {
return false, e
}
var lips []net.IP
for _, laddr := range laddrs {
lipa := laddr.(*net.IPNet)
lips = append(lips, lipa.IP)
}
for _, ip := range lips {
if host == ip.String() {
return true, nil
}
}
rips, e := net.LookupIP(host)
if e != nil {
return false, e
}
for _, rip := range rips {
for _, lip := range lips {
if lip.Equal(rip) {
return true, nil
}
}
}
return false, nil
}
// ParseHostAndBrickPath parses the host & brick path out of req.Bricks list
func ParseHostAndBrickPath(brickPath string) (string, string, error) {
i := strings.LastIndex(brickPath, ":")
if i == -1 {
log.WithField("brick", brickPath).Error(errors.ErrInvalidBrickPath.Error())
return "", "", errors.ErrInvalidBrickPath
}
hostname := brickPath[0:i]
path := brickPath[i+1 : len(brickPath)]
return hostname, path, nil
}
//ValidateBrickPathLength validates the length of the brick path
func ValidateBrickPathLength(brickPath string) error {
//TODO : Check whether PATH_MAX is compatible across all distros
if len(filepath.Clean(brickPath)) >= PathMax {
log.WithField("brick", brickPath).Error(errors.ErrBrickPathTooLong.Error())
return errors.ErrBrickPathTooLong
}
return nil
}
//ValidateBrickSubDirLength validates the length of each sub directories under
//the brick path
func ValidateBrickSubDirLength(brickPath string) error {
subdirs := strings.Split(brickPath, string(os.PathSeparator))
// Iterate over the sub directories and validate that they don't breach
// _POSIX_PATH_MAX validation
for _, subdir := range subdirs {
if len(subdir) >= PosixPathMax {
log.WithField("subdir", subdir).Error("sub directory path is too long")
return errors.ErrSubDirPathTooLong
}
}
return nil
}
//GetDeviceID fetches the device id of the device containing the file/directory
func GetDeviceID(f os.FileInfo) (int, error) {
s := f.Sys()
switch s := s.(type) {
//TODO : Need to change syscall to unix, using unix.Stat_t fails in one
//of the test
case *syscall.Stat_t:
return int(s.Dev), nil
}
return -1, errors.ErrDeviceIDNotFound
}
//ValidateBrickPathStats checks whether the brick directory can be created with
//certain validations like directory checks, whether directory is part of mount
//point etc
func ValidateBrickPathStats(brickPath string, host string, force bool) error {
var created bool
var rootStat, brickStat, parentStat os.FileInfo
err := os.MkdirAll(brickPath, os.ModeDir|os.ModePerm)
if err != nil {
if !os.IsExist(err) {
log.WithFields(log.Fields{
"host": host,
"brick": brickPath,
}).Error("Failed to create brick - ", err.Error())
return err
}
} else {
created = true
}
brickStat, err = os.Lstat(brickPath)
if err != nil {
log.WithFields(log.Fields{
"host": host,
"brick": brickPath,
}).Error("Failed to stat on brick path - ", err.Error())
return err
}
if !created && !brickStat.IsDir() {
log.WithFields(log.Fields{
"host": host,
"brick": brickPath,
}).Error("brick path which is already present is not a directory")
return errors.ErrBrickNotDirectory
}
rootStat, err = os.Lstat("/")
if err != nil {
log.Error("Failed to stat on / -", err.Error())
return err
}
parentBrick := path.Dir(brickPath)
parentStat, err = os.Lstat(parentBrick)
if err != nil {
log.WithFields(log.Fields{
"host": host,
"brick": brickPath,
"parentBrick": parentBrick,
}).Error("Failed to stat on parent of the brick path")
return err
}
if !force {
var parentDeviceID, rootDeviceID, brickDeviceID int
var e error
parentDeviceID, e = GetDeviceID(parentStat)
if e != nil {
log.WithFields(log.Fields{
"host": host,
"brick": brickPath,
}).Error("Failed to find the device id for parent of brick path")
return err
}
rootDeviceID, e = GetDeviceID(rootStat)
if e != nil {
log.Error("Failed to find the device id of '/'")
return err
}
brickDeviceID, e = GetDeviceID(brickStat)
if e != nil {
log.WithFields(log.Fields{
"host": host,
"brick": brickPath,
}).Error("Failed to find the device id of the brick")
return err
}
if brickDeviceID != parentDeviceID {
log.WithFields(log.Fields{
"host": host,
"brick": brickPath,
}).Error(errors.ErrBrickIsMountPoint.Error())
return errors.ErrBrickIsMountPoint
} else if parentDeviceID == rootDeviceID {
log.WithFields(log.Fields{
"host": host,
"brick": brickPath,
}).Error(errors.ErrBrickUnderRootPartition.Error())
return errors.ErrBrickUnderRootPartition
}
}
return nil
}
//ValidateXattrSupport checks whether the underlying file system has extended
//attribute support and it also sets some internal xattrs to mark the brick in
//use
func ValidateXattrSupport(brickPath string, host string, volid uuid.UUID, force bool) error {
var err error
err = Setxattr(brickPath, "trusted.glusterfs.test", []byte("working"), 0)
if err != nil {
log.WithFields(log.Fields{"error": err.Error(),
"brickPath": brickPath,
"host": host,
"xattr": testXattr}).Error("setxattr failed")
return err
}
err = Removexattr(brickPath, "trusted.glusterfs.test")
if err != nil {
log.WithFields(log.Fields{"error": err.Error(),
"brickPath": brickPath,
"host": host,
"xattr": testXattr}).Fatal("removexattr failed")
return err
}
if !force {
if isBrickPathAlreadyInUse(brickPath) {
log.WithFields(log.Fields{
"brickPath": brickPath,
"host": host}).Error(errors.ErrBrickPathAlreadyInUse.Error())
return errors.ErrBrickPathAlreadyInUse
}
}
err = Setxattr(brickPath, volumeIDXattr, []byte(volid), 0)
if err != nil {
log.WithFields(log.Fields{"error": err.Error(),
"brickPath": brickPath,
"host": host,
"xattr": volumeIDXattr}).Error("setxattr failed")
return err
}
return nil
}
func isBrickPathAlreadyInUse(brickPath string) bool {
keys := []string{gfidXattr, volumeIDXattr}
var p string
var buf []byte
p = brickPath
for ; p != "/"; p = path.Dir(p) {
for _, key := range keys {
size, err := Getxattr(p, key, buf)
if err != nil {
return false
} else if size > 0 {
return true
} else {
return false
}
}
}
return false
}
// InitDir checks if the input directory is present, a direcotry and is accessible.
// @ If the directory is not present, it will create directory.
// @ If it is not a directory, initDir panics.
// @ If the directory is not accessible, initDir panics.
func InitDir(dir string) {
di, err := os.Stat(dir)
if err != nil {
switch {
case os.IsNotExist(err):
if err = os.MkdirAll(dir, os.ModeDir|os.ModePerm); err != nil {
log.WithFields(log.Fields{
"err": err,
"path": dir,
}).Fatal("failed to create directory")
}
return
case os.IsPermission(err):
log.WithFields(log.Fields{
"err": err,
"path": dir,
}).Fatal("failed to access directory")
}
}
if !di.IsDir() {
log.WithFields(log.Fields{
"err": syscall.ENOTDIR,
"path": dir,
}).Fatal("directory path is not a directory")
}
// Check if you can create entries in the input directory
t, err := ioutil.TempFile(dir, "")
if err != nil {
log.WithFields(log.Fields{
"err": err,
"path": dir,
}).Fatal("directory path is not a writable")
}
// defer happens in LIFO
defer syscall.Unlink(t.Name())
defer t.Close()
}
// GetLocalIP will give local IP address of this node
func GetLocalIP() (string, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return "", err
}
for _, address := range addrs {
// check the address type and if it is not a loopback then return it
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String(), nil
}
}
}
return "", errors.ErrIPAddressNotFound
}
// GetFuncName returns the name of the passed function pointer
func GetFuncName(fn interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
}