driver.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  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. stateFolder := filepath.Join(root, "plugins", "docker-iscsi")
  46. fi, err = os.Lstat(stateFolder)
  47. if os.IsNotExist(err) {
  48. if err := os.MkdirAll(stateFolder, 0755); err != nil {
  49. return nil, err
  50. }
  51. } else if err != nil {
  52. return nil, err
  53. }
  54. if fi != nil && !fi.IsDir() {
  55. return nil, util.LogError("%v already exist and it's not a directory", stateFolder)
  56. }
  57. d := &iscsiDriver{
  58. root: filepath.Join(root, "volumes"),
  59. statePath: filepath.Join(stateFolder, "iscsi-state.json"),
  60. volumes: map[string]*iscsiVolume{},
  61. }
  62. data, err := ioutil.ReadFile(d.statePath)
  63. if err != nil {
  64. if os.IsNotExist(err) {
  65. logrus.WithField("statePath", d.statePath).Debug("no state found")
  66. } else {
  67. return nil, err
  68. }
  69. } else {
  70. if err := json.Unmarshal(data, &d.volumes); err != nil {
  71. return nil, err
  72. }
  73. }
  74. return d, nil
  75. }
  76. // Docker API :- Create
  77. func (d *iscsiDriver) Create(r *volume.CreateRequest) error {
  78. logrus.WithField("method", "create").Debugf("%#v", r)
  79. d.Lock()
  80. defer d.Unlock()
  81. v := &iscsiVolume{}
  82. for key, val := range r.Options {
  83. switch key {
  84. case "targetiqn":
  85. v.TargetIQN = val
  86. case "host":
  87. v.Host = val
  88. default:
  89. if val != "" {
  90. v.Options = append(v.Options, key+"="+val)
  91. } else {
  92. v.Options = append(v.Options, key)
  93. }
  94. }
  95. }
  96. if v.Host == "" {
  97. return util.LogError("'host' option required")
  98. }
  99. if v.TargetIQN == "" {
  100. return util.LogError("'targetiqn' option required")
  101. }
  102. v.Mountpoint = filepath.Join(d.root, fmt.Sprintf("%x", md5.Sum([]byte(v.TargetIQN))))
  103. d.volumes[r.Name] = v
  104. d.saveState()
  105. return nil
  106. }
  107. // Docker API :- Remove
  108. func (d *iscsiDriver) Remove(r *volume.RemoveRequest) error {
  109. logrus.WithField("method", "remove").Debugf("%#v", r)
  110. d.Lock()
  111. defer d.Unlock()
  112. v, ok := d.volumes[r.Name]
  113. if !ok {
  114. return util.LogError("volume %s not found", r.Name)
  115. }
  116. if v.connections != 0 {
  117. return util.LogError("volume %s is currently used by a container", r.Name)
  118. }
  119. if err := os.RemoveAll(v.Mountpoint); err != nil {
  120. return util.LogError(err.Error())
  121. }
  122. delete(d.volumes, r.Name)
  123. d.saveState()
  124. return nil
  125. }
  126. // Docker API :- Mount
  127. func (d *iscsiDriver) Mount(r *volume.MountRequest) (*volume.MountResponse, error) {
  128. logrus.WithField("method", "mount").Debugf("%#v", r)
  129. d.Lock()
  130. defer d.Unlock()
  131. v, ok := d.volumes[r.Name]
  132. if !ok {
  133. return &volume.MountResponse{}, util.LogError("volume %s not found", r.Name)
  134. }
  135. if v.connections == 0 {
  136. fi, err := os.Lstat(v.Mountpoint)
  137. if os.IsNotExist(err) {
  138. if err := os.MkdirAll(v.Mountpoint, 0755); err != nil {
  139. return &volume.MountResponse{}, util.LogError(err.Error())
  140. }
  141. } else if err != nil {
  142. return &volume.MountResponse{}, util.LogError(err.Error())
  143. }
  144. if fi != nil && !fi.IsDir() {
  145. return &volume.MountResponse{}, util.LogError("%v already exist and it's not a directory", v.Mountpoint)
  146. }
  147. if err := d.mountVolume(v); err != nil {
  148. return &volume.MountResponse{}, util.LogError(err.Error())
  149. }
  150. }
  151. v.connections++
  152. return &volume.MountResponse{Mountpoint: v.Mountpoint}, nil
  153. }
  154. // Docker API :- Path
  155. func (d *iscsiDriver) Path(r *volume.PathRequest) (*volume.PathResponse, error) {
  156. logrus.WithField("method", "path").Debugf("%#v", r)
  157. d.RLock()
  158. defer d.RUnlock()
  159. v, ok := d.volumes[r.Name]
  160. if !ok {
  161. return &volume.PathResponse{}, util.LogError("volume %s not found", r.Name)
  162. }
  163. return &volume.PathResponse{Mountpoint: v.Mountpoint}, nil
  164. }
  165. // Docker API :- Unmount
  166. func (d *iscsiDriver) Unmount(r *volume.UnmountRequest) error {
  167. logrus.WithField("method", "unmount").Debugf("%#v", r)
  168. d.Lock()
  169. defer d.Unlock()
  170. v, ok := d.volumes[r.Name]
  171. if !ok {
  172. return util.LogError("volume %s not found", r.Name)
  173. }
  174. v.connections--
  175. if v.connections <= 0 {
  176. if err := d.unmountVolume(v); err != nil {
  177. return util.LogError(err.Error())
  178. }
  179. v.connections = 0
  180. }
  181. return nil
  182. }
  183. // Docker API :- Get
  184. func (d *iscsiDriver) Get(r *volume.GetRequest) (*volume.GetResponse, error) {
  185. logrus.WithField("method", "get").Debugf("%#v", r)
  186. d.Lock()
  187. defer d.Unlock()
  188. v, ok := d.volumes[r.Name]
  189. if !ok {
  190. return &volume.GetResponse{}, util.LogError("volume %s not found", r.Name)
  191. }
  192. return &volume.GetResponse{Volume: &volume.Volume{Name: r.Name, Mountpoint: v.Mountpoint}}, nil
  193. }
  194. // Docker API :- List
  195. func (d *iscsiDriver) List() (*volume.ListResponse, error) {
  196. logrus.WithField("method", "list").Debugf("")
  197. d.Lock()
  198. defer d.Unlock()
  199. var vols []*volume.Volume
  200. for name, v := range d.volumes {
  201. vols = append(vols, &volume.Volume{Name: name, Mountpoint: v.Mountpoint})
  202. }
  203. return &volume.ListResponse{Volumes: vols}, nil
  204. }
  205. // Docker API :- Capabilities
  206. func (d *iscsiDriver) Capabilities() *volume.CapabilitiesResponse {
  207. logrus.WithField("method", "capabilities").Debugf("")
  208. return &volume.CapabilitiesResponse{Capabilities: volume.Capability{Scope: "local"}}
  209. }
  210. // Local Functions
  211. func (d *iscsiDriver) mountVolume(v *iscsiVolume) error {
  212. // Discover iSCSI LUNs
  213. err := iSCSI.DiscoverLUNs(v.Host)
  214. if err != nil {
  215. return err
  216. }
  217. // Login to iSCSI Target
  218. err = iSCSI.LoginTarget(v.TargetIQN, v.Host)
  219. if err != nil {
  220. return err
  221. }
  222. // Wait for Physical Volume to appear
  223. time.Sleep(500 * time.Millisecond)
  224. out := ""
  225. errMsg := ""
  226. diskPathFolder := "/dev/disk/by-path"
  227. fi, err := os.Lstat(diskPathFolder)
  228. if os.IsNotExist(err) {
  229. // Disk By Path doesn't exist
  230. iscsiPathFolder := "/host/tmp/iscsi"
  231. fi, err = os.Lstat(iscsiPathFolder)
  232. if os.IsNotExist(err) {
  233. // iSCSI Administration folder doesn't exist
  234. return err
  235. } else {
  236. // iSCSI Administration folder exists, seen on Synology systems
  237. // Get target device
  238. cmd := "ls /host/tmp/iscsi | grep \"" + v.TargetIQN + "\" | head -n 1"
  239. out, errMsg = util.ExecuteCommandString(cmd)
  240. if len(out) > 0 {
  241. logrus.Debug(out)
  242. }
  243. if len(errMsg) > 0 {
  244. err := fmt.Errorf("Unable to find device: %s", errMsg)
  245. return err
  246. }
  247. cmd = "readlink -nf /host/tmp/iscsi/" + out
  248. out, errMsg = util.ExecuteCommandString(cmd)
  249. if len(out) > 0 {
  250. logrus.Debug(out)
  251. }
  252. if len(errMsg) > 0 {
  253. err := fmt.Errorf("Unable to locate device: %s", errMsg)
  254. return err
  255. }
  256. }
  257. } else {
  258. // Disk By Path exists
  259. // Get target device
  260. cmd := "ls /dev/disk/by-path | grep \"" + v.TargetIQN + "\" | head -n 1"
  261. out, errMsg = util.ExecuteCommandString(cmd)
  262. if len(out) > 0 {
  263. logrus.Debug(out)
  264. }
  265. if len(errMsg) > 0 {
  266. err := fmt.Errorf("Unable to find device: %s", errMsg)
  267. return err
  268. }
  269. cmd = "readlink -nf /dev/disk/by-path/" + out
  270. out, errMsg = util.ExecuteCommandString(cmd)
  271. if len(out) > 0 {
  272. logrus.Debug(out)
  273. }
  274. if len(errMsg) > 0 {
  275. err := fmt.Errorf("Unable to locate device: %s", errMsg)
  276. return err
  277. }
  278. }
  279. _ = fi
  280. // Mount
  281. cmd := "mount " + out + " " + v.Mountpoint
  282. out, errMsg = util.ExecuteCommandString(cmd)
  283. if len(out) > 0 {
  284. logrus.Debug(out)
  285. }
  286. if len(errMsg) > 0 {
  287. err := fmt.Errorf("Unable to Mount Volume: %s", errMsg)
  288. return err
  289. }
  290. return nil
  291. }
  292. func (d *iscsiDriver) unmountVolume(v *iscsiVolume) error {
  293. // Unmount
  294. cmd := "umount " + v.Mountpoint
  295. out, errMsg := util.ExecuteCommandString(cmd)
  296. if len(out) > 0 {
  297. logrus.Debug(out)
  298. }
  299. if len(errMsg) > 0 {
  300. err := fmt.Errorf("Unable to Unmount Volume: %s", errMsg)
  301. return err
  302. }
  303. // Logout from iSCSI Target
  304. err := iSCSI.LogoutTarget(v.TargetIQN, v.Host)
  305. if err != nil {
  306. return err
  307. }
  308. return nil
  309. }
  310. func (d *iscsiDriver) saveState() {
  311. data, err := json.Marshal(d.volumes)
  312. if err != nil {
  313. logrus.WithField("statePath", d.statePath).Error(err)
  314. return
  315. }
  316. if err := ioutil.WriteFile(d.statePath, data, 0644); err != nil {
  317. logrus.WithField("savestate", d.statePath).Error(err)
  318. }
  319. }