driver.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. package main
  2. import (
  3. "crypto/md5"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "sync"
  10. "time"
  11. // Vendors
  12. "iscsi"
  13. "util"
  14. "github.com/Sirupsen/logrus"
  15. "github.com/docker/go-plugins-helpers/volume"
  16. )
  17. var iSCSI iscsi.ISCSIPlugin
  18. type iscsiVolume struct {
  19. Host string
  20. TargetIQN string
  21. Options []string
  22. Mountpoint string
  23. connections int
  24. }
  25. type iscsiDriver struct {
  26. sync.RWMutex
  27. root string
  28. statePath string
  29. volumes map[string]*iscsiVolume
  30. }
  31. // Constructor
  32. func newiscsiDriver(root string) (*iscsiDriver, error) {
  33. logrus.WithField("method", "new driver").Debug(root)
  34. fi, err := os.Lstat(root)
  35. if os.IsNotExist(err) {
  36. if err := os.MkdirAll(root, 0755); err != nil {
  37. return nil, err
  38. }
  39. } else if err != nil {
  40. return nil, err
  41. }
  42. if fi != nil && !fi.IsDir() {
  43. return nil, util.LogError("%v already exist and it's not a directory", root)
  44. }
  45. d := &iscsiDriver{
  46. root: filepath.Join(root, "volumes"),
  47. statePath: filepath.Join(root, "plugins", "docker-iscsi", "iscsi-state.json"),
  48. volumes: map[string]*iscsiVolume{},
  49. }
  50. data, err := ioutil.ReadFile(d.statePath)
  51. if err != nil {
  52. if os.IsNotExist(err) {
  53. logrus.WithField("statePath", d.statePath).Debug("no state found")
  54. } else {
  55. return nil, err
  56. }
  57. } else {
  58. if err := json.Unmarshal(data, &d.volumes); err != nil {
  59. return nil, err
  60. }
  61. }
  62. return d, nil
  63. }
  64. // Docker API :- Create
  65. func (d *iscsiDriver) Create(r *volume.CreateRequest) error {
  66. logrus.WithField("method", "create").Debugf("%#v", r)
  67. d.Lock()
  68. defer d.Unlock()
  69. v := &iscsiVolume{}
  70. for key, val := range r.Options {
  71. switch key {
  72. case "targetiqn":
  73. v.TargetIQN = val
  74. case "host":
  75. v.Host = val
  76. default:
  77. if val != "" {
  78. v.Options = append(v.Options, key+"="+val)
  79. } else {
  80. v.Options = append(v.Options, key)
  81. }
  82. }
  83. }
  84. if v.Host == "" {
  85. return util.LogError("'host' option required")
  86. }
  87. if v.TargetIQN == "" {
  88. return util.LogError("'targetiqn' option required")
  89. }
  90. v.Mountpoint = filepath.Join(d.root, fmt.Sprintf("%x", md5.Sum([]byte(v.TargetIQN))))
  91. d.volumes[r.Name] = v
  92. d.saveState()
  93. return nil
  94. }
  95. // Docker API :- Remove
  96. func (d *iscsiDriver) Remove(r *volume.RemoveRequest) error {
  97. logrus.WithField("method", "remove").Debugf("%#v", r)
  98. d.Lock()
  99. defer d.Unlock()
  100. v, ok := d.volumes[r.Name]
  101. if !ok {
  102. return util.LogError("volume %s not found", r.Name)
  103. }
  104. if v.connections != 0 {
  105. return util.LogError("volume %s is currently used by a container", r.Name)
  106. }
  107. if err := os.RemoveAll(v.Mountpoint); err != nil {
  108. return util.LogError(err.Error())
  109. }
  110. delete(d.volumes, r.Name)
  111. d.saveState()
  112. return nil
  113. }
  114. // Docker API :- Mount
  115. func (d *iscsiDriver) Mount(r *volume.MountRequest) (*volume.MountResponse, error) {
  116. logrus.WithField("method", "mount").Debugf("%#v", r)
  117. d.Lock()
  118. defer d.Unlock()
  119. v, ok := d.volumes[r.Name]
  120. if !ok {
  121. return &volume.MountResponse{}, util.LogError("volume %s not found", r.Name)
  122. }
  123. if v.connections == 0 {
  124. fi, err := os.Lstat(v.Mountpoint)
  125. if os.IsNotExist(err) {
  126. if err := os.MkdirAll(v.Mountpoint, 0755); err != nil {
  127. return &volume.MountResponse{}, util.LogError(err.Error())
  128. }
  129. } else if err != nil {
  130. return &volume.MountResponse{}, util.LogError(err.Error())
  131. }
  132. if fi != nil && !fi.IsDir() {
  133. return &volume.MountResponse{}, util.LogError("%v already exist and it's not a directory", v.Mountpoint)
  134. }
  135. if err := d.mountVolume(v); err != nil {
  136. return &volume.MountResponse{}, util.LogError(err.Error())
  137. }
  138. }
  139. v.connections++
  140. return &volume.MountResponse{Mountpoint: v.Mountpoint}, nil
  141. }
  142. // Docker API :- Path
  143. func (d *iscsiDriver) Path(r *volume.PathRequest) (*volume.PathResponse, error) {
  144. logrus.WithField("method", "path").Debugf("%#v", r)
  145. d.RLock()
  146. defer d.RUnlock()
  147. v, ok := d.volumes[r.Name]
  148. if !ok {
  149. return &volume.PathResponse{}, util.LogError("volume %s not found", r.Name)
  150. }
  151. return &volume.PathResponse{Mountpoint: v.Mountpoint}, nil
  152. }
  153. // Docker API :- Unmount
  154. func (d *iscsiDriver) Unmount(r *volume.UnmountRequest) error {
  155. logrus.WithField("method", "unmount").Debugf("%#v", r)
  156. d.Lock()
  157. defer d.Unlock()
  158. v, ok := d.volumes[r.Name]
  159. if !ok {
  160. return util.LogError("volume %s not found", r.Name)
  161. }
  162. v.connections--
  163. if v.connections <= 0 {
  164. if err := d.unmountVolume(v); err != nil {
  165. return util.LogError(err.Error())
  166. }
  167. v.connections = 0
  168. }
  169. return nil
  170. }
  171. // Docker API :- Get
  172. func (d *iscsiDriver) Get(r *volume.GetRequest) (*volume.GetResponse, error) {
  173. logrus.WithField("method", "get").Debugf("%#v", r)
  174. d.Lock()
  175. defer d.Unlock()
  176. v, ok := d.volumes[r.Name]
  177. if !ok {
  178. return &volume.GetResponse{}, util.LogError("volume %s not found", r.Name)
  179. }
  180. return &volume.GetResponse{Volume: &volume.Volume{Name: r.Name, Mountpoint: v.Mountpoint}}, nil
  181. }
  182. // Docker API :- List
  183. func (d *iscsiDriver) List() (*volume.ListResponse, error) {
  184. logrus.WithField("method", "list").Debugf("")
  185. d.Lock()
  186. defer d.Unlock()
  187. var vols []*volume.Volume
  188. for name, v := range d.volumes {
  189. vols = append(vols, &volume.Volume{Name: name, Mountpoint: v.Mountpoint})
  190. }
  191. return &volume.ListResponse{Volumes: vols}, nil
  192. }
  193. // Docker API :- Capabilities
  194. func (d *iscsiDriver) Capabilities() *volume.CapabilitiesResponse {
  195. logrus.WithField("method", "capabilities").Debugf("")
  196. return &volume.CapabilitiesResponse{Capabilities: volume.Capability{Scope: "local"}}
  197. }
  198. // Local Functions
  199. func (d *iscsiDriver) mountVolume(v *iscsiVolume) error {
  200. // Discover iSCSI LUNs
  201. err := iSCSI.DiscoverLUNs(v.Host)
  202. if err != nil {
  203. return err
  204. }
  205. // Login to iSCSI Target
  206. err = iSCSI.LoginTarget(v.TargetIQN, v.Host)
  207. if err != nil {
  208. return err
  209. }
  210. // Wait for Physical Volume to appear
  211. time.Sleep(500 * time.Millisecond)
  212. // Get target device
  213. cmd := "ls /dev/disk/by-path | grep \"" + v.TargetIQN + "\" | head -n 1"
  214. out, errMsg := util.ExecuteCommandString(cmd)
  215. if len(out) > 0 {
  216. logrus.Debug(out)
  217. }
  218. if len(errMsg) > 0 {
  219. err := fmt.Errorf("Unable to find device: %s", errMsg)
  220. return err
  221. }
  222. cmd = "readlink -nf /dev/disk/by-path/" + out
  223. out, errMsg = util.ExecuteCommandString(cmd)
  224. if len(out) > 0 {
  225. logrus.Debug(out)
  226. }
  227. if len(errMsg) > 0 {
  228. err := fmt.Errorf("Unable to locate device: %s", errMsg)
  229. return err
  230. }
  231. // Mount
  232. cmd = "mount " + out + " " + v.Mountpoint
  233. out, errMsg = util.ExecuteCommandString(cmd)
  234. if len(out) > 0 {
  235. logrus.Debug(out)
  236. }
  237. if len(errMsg) > 0 {
  238. err := fmt.Errorf("Unable to Mount Volume: %s", errMsg)
  239. return err
  240. }
  241. return nil
  242. }
  243. func (d *iscsiDriver) unmountVolume(v *iscsiVolume) error {
  244. // Unmount
  245. cmd := "umount " + v.Mountpoint
  246. out, errMsg := util.ExecuteCommandString(cmd)
  247. if len(out) > 0 {
  248. logrus.Debug(out)
  249. }
  250. if len(errMsg) > 0 {
  251. err := fmt.Errorf("Unable to Unmount Volume: %s", errMsg)
  252. return err
  253. }
  254. // Logout from iSCSI Target
  255. err := iSCSI.LogoutTarget(v.TargetIQN, v.Host)
  256. if err != nil {
  257. return err
  258. }
  259. return nil
  260. }
  261. func (d *iscsiDriver) saveState() {
  262. data, err := json.Marshal(d.volumes)
  263. if err != nil {
  264. logrus.WithField("statePath", d.statePath).Error(err)
  265. return
  266. }
  267. if err := ioutil.WriteFile(d.statePath, data, 0644); err != nil {
  268. logrus.WithField("savestate", d.statePath).Error(err)
  269. }
  270. }