Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

"""Temporary module to mock libiscsi""" 

 

import re 

 

from . import util 

from . import f_exceptions 

 

 

def exn_on_failure(cmd, message): 

    """Executes via util.doexec the command specified. If the return code is 

    non-zero, raises an ISCSIError with the given message""" 

 

    # _lock = None 

    # if os.path.basename(cmd[0]) == 'iscsiadm': 

    #     _lock = lock.Lock(LOCK_TYPE_RUNNING, 'iscsiadm') 

    #     _lock.acquire() 

 

    (ret, stdout, stderr) = util.doexec(cmd) 

    # if _lock <> None and _lock.held(): 

    #     _lock.release() 

    if ret == 0: 

        return (stdout, stderr) 

    else: 

        msg = 'rc: %d, stdout: %s, stderr: %s' % (ret, stdout, stderr) 

        raise f_exceptions.XenError('SMGeneral', message+' '+msg) 

 

 

def discovery(target, port, chapuser, chappass, target_iqn="any", 

              interface_array=("default",)): 

    """Run iscsiadm in discovery mode to obtain a list of the 

    TargetIQNs available on the specified target and port. Returns 

    a list of triples - the portal (ip:port), the tpgt (target portal 

    group tag) and the target name""" 

 

    # Save configuration of root LUN nodes and restore after discovery 

    # otherwise when we do a discovery on the same filer as is hosting 

    # our root disk we'll reset the config of the root LUNs 

#    save_rootdisk_nodes() 

 

41    if ':' in target: 

        targetstring = "[%s]:%s" % (target, str(port)) 

    else: 

        targetstring = "%s:%s" % (target, str(port)) 

    cmd_base = ["-t", "st", "-p", targetstring] 

    for interface in interface_array: 

        cmd_base.append("-I") 

        cmd_base.append(interface) 

    cmd_disc = ["iscsiadm", "-m", "discovery"] + cmd_base 

    cmd_discdb = ["iscsiadm", "-m", "discoverydb"] + cmd_base 

    auth_args =  ["-n", "discovery.sendtargets.auth.authmethod", "-v", "CHAP", 

                  "-n", "discovery.sendtargets.auth.username", "-v", chapuser, 

                  "-n", "discovery.sendtargets.auth.password", "-v", chappass] 

    fail_msg = "Discovery failed. Check target settings and " \ 

               "username/password (if applicable)" 

    try: 

        if chapuser!="" and chappass!="": 

            exn_on_failure(cmd_discdb + ["-o", "new"], fail_msg) 

            exn_on_failure(cmd_discdb + ["-o", "update"] + auth_args, fail_msg) 

            cmd = cmd_discdb + ["--discover"] 

        else: 

            cmd = cmd_disc 

        (stdout,stderr) = exn_on_failure(cmd, fail_msg) 

    except Exception as exc: 

#        restore_rootdisk_nodes() 

        raise f_exceptions.XenError('ISCSILogin', message=exc.message) 

#    else: 

#        restore_rootdisk_nodes() 

 

    return parse_node_output(stdout, target_iqn) 

 

 

def set_chap_settings (portal, targetIQN, username, password): 

    """Sets the username and password on the session identified by the  

    portal/targetIQN combination""" 

 

    failuremessage = "Failed to set CHAP settings" 

    cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

           "update", "-n", "node.session.auth.authmethod","-v", "CHAP"] 

    (stdout,stderr) = exn_on_failure(cmd, failuremessage) 

 

    cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

           "update", "-n", "node.session.auth.username","-v", 

           username] 

    (stdout,stderr) = exn_on_failure(cmd, failuremessage) 

 

    cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

           "update", "-n", "node.session.auth.password","-v", 

           password] 

    (stdout,stderr) = exn_on_failure(cmd, failuremessage) 

 

 

def match_target_iqn(tgt_iqn, tgt_string): 

    """doc placeholder""" 

    if not len(tgt_string): 

        return False 

100    if tgt_iqn == "any": 

        return True 

    # Extract IQN from iscsiadm -m session 

    # Ex: tcp: [17] 10.220.98.9:3260,1 iqn.2009-01.xenrt.test:iscsi4181a93e 

    siqn = tgt_string.split(",")[1].split()[1] 

    return siqn == tgt_iqn 

 

 

def parse_node_output(text, target_iqn): 

    """helper function - parses the output of iscsiadm for discovery and 

    get_node_records""" 

    def dotrans(x): 

        (rec, iqn) = x.split() 

        (portal, tpgt) = rec.split(',') 

        return (portal, tpgt, iqn) 

    unfiltered_map = [dotrans(x) for x in text.split('\n') if 

                      match_target_iqn(target_iqn, x)] 

 

    # We need to filter duplicates orignating from doing the discovery using 

    # multiple interfaces 

    filtered_map = [] 

    for input_value in unfiltered_map: 

        if input_value not in filtered_map: 

            filtered_map.append(input_value) 

    return filtered_map 

 

 

def get_portal_target_ref(portal, target): 

    return '{}#{}'.format(portal, target) 

 

 

def login(portal, target, username, password, uuid, rescan=False): 

    """This is a slightly revised version of iscsilib.login in SM. 

 

    We are trying to stick as close as possible to the original one but fixing 

    or improving whatever can be improved, in order to later fix it to the 

    original one and start using that one both for the transport code and 

    the old SM code. 

 

    For example, 'uuid' is a new addition in order to be able to automatically 

    refcount in here, instead of relying on the caller to do that. 

    """ 

 

140    if username != "" and password != "": 

        set_chap_settings(portal, target, username, password) 

 

 

    refcount = 0 

    legacy_refcount = 0 

 

    portal_target_ref = get_portal_target_ref(portal, target) 

 

    # Increment portal/target ref count 

    refcount = util._incr_iscsiSR_refcount(portal_target_ref, uuid) 

 

    if refcount == 1: 

        cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", target, "-l"] 

        try: 

            failuremessage = "Failed to login to target." 

            (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

            # Increment legacy refcounter 

170            if uuid: 

                legacy_refcount = util._incr_iscsiSR_refcount(target, uuid) 

        except Exception as exc: 

            util.SMlog("Failed: {}".format(" ".join(cmd)), 

                       ident="Transport") 

            # Rollback refcount if needed 

            if uuid: 

                # This should be in its own try/block and be chained 

                # with main exception below 

                util._decr_iscsiSR_refcount(portal_target_ref, uuid) 

            raise f_exceptions.XenError('ISCSILogin', message=exc.message) 

 

    # Rescan if requested and more than one refcount of either form 

    if rescan and (refcount > 1 or legacy_refcount > 1): 

        util.SMlog("Session already logged in for {}, rescan requested". 

                   format(target)) 

        rescan_target(portal, target) 

 

def logout(portal, target, uuid): 

    """Modified version of iscsilib.py:logout to handle refcounting 

 

    Given the nature of this refcounting, it is not possible to specifically 

    logout from one ip/iqn pair without breaking the refcounting. 

    For this reason, this version does not accept a specific ip and the 

    parameter 'all'. 

    """ 

    util.SMlog("decrease logout refcount of {} ({})". 

               format(target, uuid), 

               ident="Transport") 

 

    portal_target_ref = get_portal_target_ref(portal, target) 

 

    if util._decr_iscsiSR_refcount(portal_target_ref, uuid): 

        # still logged in 

        return 

 

    # Decrement the legacy refcount 

    if not util._decr_iscsiSR_refcount(target, uuid): 

        try: 

            cmd = ["iscsiadm", "-m", "node", "-T", target, "-u"] 

            failuremessage = "Failed to log out of target" 

            util.SMlog("logging out {} for {}". 

                       format(target, uuid), 

                       ident="Transport") 

            (_, _) = exn_on_failure(cmd, failuremessage) 

        except Exception as exc: 

            raise f_exceptions.XenError('ISCSILogout', message=exc.message) 

 

def rescan_target(portal, target): 

    """ 

    Rescan the given iscsi portal and target 

    """ 

    sessions_cmd = ["iscsiadm", "-m", "session"] 

    failure_message = "Failed to read iscsi sessions" 

    (stdout, _) = exn_on_failure(sessions_cmd, failure_message) 

 

    session_re = r"^[^\[]*\[(\d+)\] " + portal + r",\d+ " + target 

 

    for line in stdout.splitlines(): 

        session_match = re.match(session_re, line) 

215        if session_match: 

            session_id = session_match.group(1) 

            rescan_cmd = ["iscsiadm", "-m", "session", '-r', session_id, '--rescan'] 

            (_, _) = exn_on_failure(rescan_cmd, "Failed to rescan session") 

 

 

def get_iscsi_interfaces(): 

    result = [] 

    try: 

        # Get all configured iscsiadm interfaces 

        cmd = ["iscsiadm", "-m", "iface"] 

        (stdout, stderr) = exn_on_failure(cmd, 

                            "Failure occured querying iscsi daemon") 

        # Get the interface (first column) from a line such as default 

        # tcp,<empty>,<empty>,<empty>,<empty> 

        for line in stdout.split("\n"): 

            line_element = line.split(" ") 

            interface_name = line_element[0] 

            # ignore interfaces which aren't marked as starting with 

            # c_. 

            if len(line_element) == 2 and interface_name[:2] == "c_": 

                result.append(interface_name) 

    except: 

        # Ignore exception from exn on failure, still return the default 

        # interface 

        pass 

    # In case there are no configured interfaces, still add the default 

    # interface 

    if len(result) == 0: 

        result.append("default") 

    return result