diff --git a/netshare/drivers/ceph.go b/netshare/drivers/ceph.go new file mode 100644 index 0000000..62b187d --- /dev/null +++ b/netshare/drivers/ceph.go @@ -0,0 +1,148 @@ +package drivers + +import ( + "fmt" + log "github.com/Sirupsen/logrus" + "github.com/docker/go-plugins-helpers/volume" + "os" + "strings" +) + +const ( + CephOptions = "cephopts" +) + +type cephDriver struct { + volumeDriver + username string + password string + context string + cephmount string + cephport string + localmount string + cephopts map[string]string +} + +//var ( +// EmptyMap = map[string]string{} +//) + +func NewCephDriver(root string, username string, password string, context string, cephmount string, cephport string, localmount string, cephopts string) cephDriver { + d := cephDriver{ + volumeDriver: newVolumeDriver(root), + username: username, + password: password, + context: context, + cephmount: cephmount, + cephport: cephport, + localmount: localmount, + cephopts: map[string]string{}, + } + if len(cephopts) > 0 { + d.cephopts[CephOptions] = cephopts + } + + return d +} + +func (n cephDriver) Mount(r volume.Request) volume.Response { + log.Debugf("Entering Mount: %v", r) + n.m.Lock() + defer n.m.Unlock() + hostdir := mountpoint(n.root, r.Name) + source := n.fixSource(r) + if n.mountm.HasMount(r.Name) && n.mountm.Count(r.Name) > 0 { + log.Infof("Using existing CEPH volume mount: %s", hostdir) + n.mountm.Increment(r.Name) + return volume.Response{Mountpoint: hostdir} + } + + log.Infof("Mounting CEPH volume %s on %s", source, hostdir) + if err := createDest(hostdir); err != nil { + return volume.Response{Err: err.Error()} + } + + if err := n.mountVolume(source, hostdir); err != nil { + return volume.Response{Err: err.Error()} + } + n.mountm.Add(r.Name, hostdir) + return volume.Response{Mountpoint: hostdir} +} + +func (n cephDriver) Unmount(r volume.Request) volume.Response { + log.Debugf("Entering Unmount: %v", r) + + n.m.Lock() + defer n.m.Unlock() + hostdir := mountpoint(n.root, r.Name) + + if n.mountm.HasMount(r.Name) { + if n.mountm.Count(r.Name) > 1 { + log.Printf("Skipping unmount for %s - in use by other containers", r.Name) + n.mountm.Decrement(r.Name) + return volume.Response{} + } + n.mountm.Decrement(r.Name) + } + + log.Infof("Unmounting volume name %s from %s", r.Name, hostdir) + + if err := run(fmt.Sprintf("umount %s", hostdir)); err != nil { + return volume.Response{Err: err.Error()} + } + + n.mountm.DeleteIfNotManaged(r.Name) + + if err := os.RemoveAll(hostdir); err != nil { + return volume.Response{Err: err.Error()} + } + + return volume.Response{} +} + +func (n cephDriver) fixSource(r volume.Request) string { + if n.mountm.HasOption(r.Name, ShareOpt) { + return n.mountm.GetOption(r.Name, ShareOpt) + } + source := strings.Split(r.Name, "/") + source[0] = source[0] + ":" + n.cephport + ":" + return strings.Join(source, "/") +} + +func (n cephDriver) mountVolume(source, dest string) error { + var cmd string + + options := n.mountOptions(n.mountm.GetOptions(dest)) + opts := "" + if val, ok := options[CephOptions]; ok { + fmt.Println("opts = ", val) + opts = "-o " + val + } + + mountCmd := "mount" + + if log.GetLevel() == log.DebugLevel { + mountCmd = mountCmd + " -t ceph" + } + + //cmd = fmt.Sprintf("%s -t ceph %s:%s:/ -o %s,%s,%s %s %s", mountCmd, n.cephmount, n.cephport, n.context, n.username, n.password, opts, dest) + cmd = fmt.Sprintf("%s -t ceph %s -o %s,%s,%s %s %s", mountCmd, source, n.context, n.username, n.password, opts, dest) + + log.Debugf("exec: %s\n", cmd) + return run(cmd) +} + +func (n cephDriver) mountOptions(src map[string]string) map[string]string { + if len(n.cephopts) == 0 && len(src) == 0 { + return EmptyMap + } + + dst := map[string]string{} + for k, v := range n.cephopts { + dst[k] = v + } + for k, v := range src { + dst[k] = v + } + return dst +} diff --git a/netshare/drivers/driver_types.go b/netshare/drivers/driver_types.go index b5549c1..8b81968 100644 --- a/netshare/drivers/driver_types.go +++ b/netshare/drivers/driver_types.go @@ -6,12 +6,14 @@ const ( CIFS DriverType = iota NFS EFS + CEPH ) var driverTypes = []string{ "cifs", "nfs", "efs", + "ceph", } func (dt DriverType) String() string { diff --git a/netshare/drivers/nfs.go b/netshare/drivers/nfs.go index 324a3a5..c7888a9 100644 --- a/netshare/drivers/nfs.go +++ b/netshare/drivers/nfs.go @@ -123,7 +123,6 @@ func (n nfsDriver) mountVolume(source, dest string, version int) error { if len(opts) < 1 { opts = DefaultNfsV3 } - cmd = fmt.Sprintf("%s -o %s %s %s", mountCmd, opts, source, dest) default: log.Debugf("Mounting with NFSv4 - src: %s, dest: %s", source, dest) if len(opts) > 0 { diff --git a/netshare/netshare.go b/netshare/netshare.go index 694b9f2..d36a552 100644 --- a/netshare/netshare.go +++ b/netshare/netshare.go @@ -26,6 +26,13 @@ const ( TCPFlag = "tcp" PortFlag = "port" NameServerFlag = "nameserver" + NameFlag = "name" + SecretFlag = "secret" + ContextFlag = "context" + CephMount = "sorcemount" + CephPort = "port" + CephOpts = "options" + ServerMount = "servermount" EnvSambaUser = "NETSHARE_CIFS_USERNAME" EnvSambaPass = "NETSHARE_CIFS_PASSWORD" EnvSambaWG = "NETSHARE_CIFS_DOMAIN" @@ -70,6 +77,12 @@ var ( Run: execEFS, } + cephCmd = &cobra.Command{ + Use: "ceph", + Short: "run plugin in Ceph mode", + Run: execCEPH, + } + versionCmd = &cobra.Command{ Use: "version", Short: "Display current version and build date", @@ -85,7 +98,7 @@ var ( func Execute() { setupFlags() rootCmd.Long = fmt.Sprintf(NetshareHelp, Version, BuildDate) - rootCmd.AddCommand(versionCmd, cifsCmd, nfsCmd, efsCmd) + rootCmd.AddCommand(versionCmd, cifsCmd, nfsCmd, efsCmd, cephCmd) rootCmd.Execute() } @@ -108,6 +121,14 @@ func setupFlags() { efsCmd.Flags().String(AvailZoneFlag, "", "AWS Availability zone [default: \"\", looks up via metadata]") efsCmd.Flags().String(NameServerFlag, "", "Custom DNS nameserver. [default \"\", uses /etc/resolv.conf]") efsCmd.Flags().Bool(NoResolveFlag, false, "Indicates EFS mount sources are IP Addresses vs File System ID") + + cephCmd.Flags().StringP(NameFlag, "n", "admin", "Username to use for ceph mount.") + cephCmd.Flags().StringP(SecretFlag, "s", "NoneProvided", "Password to use for Ceph Mount.") + cephCmd.Flags().StringP(ContextFlag, "c", "system_u:object_r:tmp_t:s0", "SELinux Context of Ceph Mount.") + cephCmd.Flags().StringP(CephMount, "m", "10.0.0.1", "Address of Ceph source mount.") + cephCmd.Flags().StringP(CephPort, "p", "6789", "Port to use for ceph mount.") + cephCmd.Flags().StringP(ServerMount, "S", "/mnt/ceph", "Directory to use as ceph local mount.") + cephCmd.Flags().StringP(OptionsFlag, "o", "", "Options passed to Ceph mounts ") } func setupLogger(cmd *cobra.Command, args []string) { @@ -118,6 +139,28 @@ func setupLogger(cmd *cobra.Command, args []string) { } } +func execCEPH(cmd *cobra.Command, args []string) { + username, _ := cmd.Flags().GetString(NameFlag) + password, _ := cmd.Flags().GetString(SecretFlag) + context, _ := cmd.Flags().GetString(ContextFlag) + cephmount, _ := cmd.Flags().GetString(CephMount) + cephport, _ := cmd.Flags().GetString(CephPort) + servermount, _ := cmd.Flags().GetString(ServerMount) + cephopts, _ := cmd.Flags().GetString(CephOpts) + + if len(username) > 0 { + username = "name=" + username + } + if len(password) > 0 { + password = "secret=" + password + } + if len(context) > 0 { + context = "context=" + "\"" + context + "\"" + } + d := drivers.NewCephDriver(rootForType(drivers.CEPH), username, password, context, cephmount, cephport, servermount, cephopts) + start(drivers.CEPH, d) +} + func execNFS(cmd *cobra.Command, args []string) { version, _ := cmd.Flags().GetInt(VersionFlag) if os.Getenv(EnvNfsVers) != "" {