Hide keyboard shortcuts

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#!/usr/local/bin/python 

2# encoding: utf-8 

3""" 

4*a database object that can setup up a ssh tunnel (optional) and a database connection* 

5 

6:Author: 

7 David Young 

8 

9:Date Created: 

10 November 22, 2017 

11""" 

12################# GLOBAL IMPORTS #################### 

13from builtins import object 

14import sys 

15import os 

16os.environ['TERM'] = 'vt100' 

17import readline 

18import glob 

19import pickle 

20import time 

21from subprocess import Popen, PIPE, STDOUT 

22import pymysql as ms 

23from docopt import docopt 

24from fundamentals.mysql import readquery 

25 

26 

27class database(object): 

28 """ 

29 *a database object that can setup up a ssh tunnel (optional) and a database connection* 

30 

31 **Key Arguments:** 

32 - ``log`` -- logger 

33 - ``dbSettings`` -- a dictionary of database settings 

34 

35 **Return:** 

36 - ``dbConns`` -- a database connection 

37 

38 **Usage:** 

39 

40 Given a python dictionary that looks like this: 

41 

42 .. code-block:: python  

43 

44 dbSettings = { 

45 'host': '127.0.0.1',  

46 'loginPath': 'atlasMovers',  

47 'user': 'monster',  

48 'tunnel': { 

49 'remote ip': 'psweb.mp.qub.ac.uk',  

50 'remote datbase host': 'dormammu',  

51 'remote user': 'monster',  

52 'port': 9006 

53 },  

54 'password': 'myPass',  

55 'db': 'atlas_moving_objects' 

56 } 

57 

58 ``loginPath`` and ``tunnel`` are optional, to setup the a database connection, run the following: 

59 

60 .. code-block:: python  

61 

62 # SETUP ALL DATABASE CONNECTIONS 

63 from fundamentals.mysql import database 

64 dbConn = database( 

65 log=log, 

66 dbSettings=dbSettings 

67 ).connect() 

68 """ 

69 # INITIALISATION 

70 

71 def __init__( 

72 self, 

73 log, 

74 dbSettings=False, 

75 autocommit=True 

76 

77 ): 

78 self.log = log 

79 log.debug("instansiating a new '_database' object") 

80 self.dbSettings = dbSettings 

81 self.autocommit = autocommit 

82 

83 return None 

84 

85 def connect(self): 

86 """connect to the database 

87 

88 **Return:** 

89 - ``dbConn`` -- the database connection 

90 

91 See the class docstring for usage 

92 """ 

93 self.log.debug('starting the ``connect`` method') 

94 

95 dbSettings = self.dbSettings 

96 

97 port = False 

98 if "tunnel" in dbSettings and dbSettings["tunnel"]: 

99 port = self._setup_tunnel( 

100 tunnelParameters=dbSettings["tunnel"] 

101 ) 

102 

103 # SETUP A DATABASE CONNECTION 

104 host = dbSettings["host"] 

105 user = dbSettings["user"] 

106 passwd = dbSettings["password"] 

107 dbName = dbSettings["db"] 

108 dbConn = ms.connect( 

109 host=host, 

110 user=user, 

111 passwd=passwd, 

112 db=dbName, 

113 port=port, 

114 use_unicode=True, 

115 charset='utf8mb4', 

116 local_infile=1, 

117 client_flag=ms.constants.CLIENT.MULTI_STATEMENTS, 

118 connect_timeout=36000, 

119 max_allowed_packet=51200000 

120 ) 

121 if self.autocommit: 

122 dbConn.autocommit(True) 

123 

124 self.log.debug('completed the ``connect`` method') 

125 return dbConn 

126 

127 def _setup_tunnel( 

128 self, 

129 tunnelParameters): 

130 """ 

131 *setup a ssh tunnel for a database connection to port through* 

132 

133 **Key Arguments:** 

134 - ``tunnelParameters`` -- the tunnel parameters found associated with the database settings 

135 

136 **Return:** 

137 - ``sshPort`` -- the port the ssh tunnel is connected via 

138 """ 

139 self.log.debug('starting the ``_setup_tunnel`` method') 

140 

141 # TEST TUNNEL DOES NOT ALREADY EXIST 

142 sshPort = tunnelParameters["port"] 

143 connected = self._checkServer( 

144 "127.0.0.1", sshPort) 

145 if connected: 

146 self.log.debug('ssh tunnel already exists - moving on') 

147 else: 

148 # GRAB TUNNEL SETTINGS FROM SETTINGS FILE 

149 ru = tunnelParameters["remote user"] 

150 rip = tunnelParameters["remote ip"] 

151 rh = tunnelParameters["remote datbase host"] 

152 

153 cmd = "ssh -fnN %(ru)s@%(rip)s -L %(sshPort)s:%(rh)s:3306" % locals() 

154 p = Popen(cmd, shell=True, close_fds=True) 

155 output = p.communicate()[0] 

156 self.log.debug('output: %(output)s' % locals()) 

157 

158 # TEST CONNECTION - QUIT AFTER SO MANY TRIES 

159 connected = False 

160 count = 0 

161 while not connected: 

162 connected = self._checkServer( 

163 "127.0.0.1", sshPort) 

164 time.sleep(1) 

165 count += 1 

166 if count == 5: 

167 self.log.error( 

168 'cound not setup tunnel to remote datbase' % locals()) 

169 sys.exit(0) 

170 return sshPort 

171 

172 def _checkServer(self, address, port): 

173 """Check that the TCP Port we've decided to use for tunnelling is available 

174 """ 

175 self.log.debug('starting the ``_checkServer`` method') 

176 

177 # CREATE A TCP SOCKET 

178 import socket 

179 s = socket.socket() 

180 self.log.debug( 

181 """Attempting to connect to `%(address)s` on port `%(port)s`""" % locals()) 

182 try: 

183 s.connect((address, port)) 

184 self.log.debug( 

185 """Connected to `%(address)s` on port `%(port)s`""" % locals()) 

186 return True 

187 except socket.error as e: 

188 self.log.warning( 

189 """Connection to `%(address)s` on port `%(port)s` failed - try again: %(e)s""" % locals()) 

190 return False 

191 

192 return None 

193 

194 # xt-class-method