24 def _fill_text(self, text, width, indent): |
24 def _fill_text(self, text, width, indent): |
25 return textwrap.dedent(text) |
25 return textwrap.dedent(text) |
26 |
26 |
27 |
27 |
28 class ToolBase: |
28 class ToolBase: |
29 def __init__(self, name, desc, **kwargs): |
29 |
30 self.parser = argparse.ArgumentParser(prog=name, description=desc, |
30 def __init__(self, name, desc=None, **kwargs): |
|
31 self.config = ConfigParser() |
|
32 self.parser = argparse.ArgumentParser(prog=name, description=desc or self.__doc__, |
31 formatter_class=ToolDescriptionFormatter) |
33 formatter_class=ToolDescriptionFormatter) |
32 self.parser.add_argument('-d', dest='debug', action='store_true', |
34 self.pgm = pgmanager.get_instance() |
33 help='Debug mode - print database queries.') |
35 self.target_isolation_level = None |
34 |
36 |
35 self.config = ConfigParser() |
37 def setup(self, args=None): |
|
38 self.specify_args() |
|
39 self.load_args(args) |
|
40 self.init_logging() |
|
41 |
|
42 def specify_args(self): |
36 self.config.add_option('databases', dict) |
43 self.config.add_option('databases', dict) |
37 self.config.add_option('meta_db') |
44 self.config.add_option('meta_db') |
38 self.config.add_option('meta_query') |
45 self.config.add_option('meta_query') |
39 |
46 self.parser.add_argument('-Q', dest='queries', action='store_true', |
40 self.pgm = pgmanager.get_instance() |
47 help='Print database queries.') |
41 self.target_isolation_level = None |
48 |
42 |
49 def load_args(self, args=None, config_file=None): |
43 def init(self, args=None): |
50 self.config.load(config_file or 'pgtoolkit.conf') |
44 self.config.load('pgtoolkit.conf') |
|
45 self.args = self.parser.parse_args(args) |
51 self.args = self.parser.parse_args(args) |
46 self.init_logging() |
|
47 |
52 |
48 def init_logging(self): |
53 def init_logging(self): |
49 # logging |
54 # logging |
50 format = ColoredFormatter(highlight(1,7,0)+'%(asctime)s %(levelname)-5s'+highlight(0)+' %(message)s', '%H:%M:%S') |
55 format = ColoredFormatter(highlight(1,7,0)+'%(asctime)s %(levelname)-5s'+highlight(0)+' %(message)s', '%H:%M:%S') |
51 handler = logging.StreamHandler() |
56 handler = logging.StreamHandler() |
57 |
62 |
58 log_notices = logging.getLogger('pgmanager_notices') |
63 log_notices = logging.getLogger('pgmanager_notices') |
59 log_notices.addHandler(handler) |
64 log_notices.addHandler(handler) |
60 log_notices.setLevel(logging.DEBUG) |
65 log_notices.setLevel(logging.DEBUG) |
61 |
66 |
62 if self.args.debug: |
67 if self.args.queries: |
63 log_sql = logging.getLogger('pgmanager_sql') |
68 log_sql = logging.getLogger('pgmanager_sql') |
64 log_sql.addHandler(handler) |
69 log_sql.addHandler(handler) |
65 log_sql.setLevel(logging.DEBUG) |
70 log_sql.setLevel(logging.DEBUG) |
66 |
71 |
67 def prepare_conn_from_metadb(self, name, lookup_name): |
72 def prepare_conn_from_metadb(self, name, lookup_name): |
68 '''Create connection in pgmanager using meta DB. |
73 """Create connection in pgmanager using meta DB. |
69 |
74 |
70 name -- Name for connection in pgmanager. |
75 name -- Name for connection in pgmanager. |
71 lookup_name -- Name of connection in meta DB. |
76 lookup_name -- Name of connection in meta DB. |
72 |
77 |
73 ''' |
78 """ |
|
79 if not self.pgm.knows_conn('meta'): |
|
80 self.pgm.create_conn(name='meta', dsn=self.config.meta_db) |
74 with self.pgm.cursor('meta') as curs: |
81 with self.pgm.cursor('meta') as curs: |
75 curs.execute(self.config.meta_query, [lookup_name]) |
82 curs.execute(self.config.meta_query, [lookup_name]) |
76 row = curs.fetchone_dict() |
83 row = curs.fetchone_dict() |
77 curs.connection.commit() |
84 curs.connection.commit() |
78 if row: |
85 if row: |
79 self.pgm.create_conn(name=name, |
86 self.pgm.create_conn(name=name, |
80 isolation_level=self.target_isolation_level, |
87 isolation_level=self.target_isolation_level, |
81 **row) |
88 **row) |
82 return True |
89 return True |
|
90 self.pgm.close_conn('meta') |
83 |
91 |
84 def prepare_conn_from_config(self, name, lookup_name): |
92 def prepare_conn_from_config(self, name, lookup_name): |
85 '''Create connection in pgmanager using info in config.databases.''' |
93 """Create connection in pgmanager using info in config.databases.""" |
86 if self.config.databases: |
94 if self.config.databases: |
87 if lookup_name in self.config.databases: |
95 if lookup_name in self.config.databases: |
88 dsn = self.config.databases[lookup_name] |
96 dsn = self.config.databases[lookup_name] |
89 self.pgm.create_conn(name=name, |
97 self.pgm.create_conn(name=name, |
90 isolation_level=self.target_isolation_level, |
98 isolation_level=self.target_isolation_level, |
97 Keyword arguments meaning: |
105 Keyword arguments meaning: |
98 key: connection name for use in PgManager |
106 key: connection name for use in PgManager |
99 value: connection name in config or meta DB |
107 value: connection name in config or meta DB |
100 |
108 |
101 """ |
109 """ |
102 if self.config.meta_db: |
|
103 self.pgm.create_conn(name='meta', dsn=self.config.meta_db) |
|
104 |
|
105 for name in kwargs: |
110 for name in kwargs: |
106 lookup_name = kwargs[name] |
111 lookup_name = kwargs[name] |
107 found = self.prepare_conn_from_config(name, lookup_name) |
112 found = self.prepare_conn_from_config(name, lookup_name) |
108 if not found and self.config.meta_db: |
113 if not found and self.config.meta_db: |
109 found = self.prepare_conn_from_metadb(name, lookup_name) |
114 found = self.prepare_conn_from_metadb(name, lookup_name) |
110 if not found: |
115 if not found: |
111 raise ConnectionInfoNotFound('Connection name "%s" not found in config nor in meta DB.' % lookup_name) |
116 raise ConnectionInfoNotFound('Connection name "%s" not found in config nor in meta DB.' % lookup_name) |
112 |
117 |
113 if self.config.meta_db: |
|
114 self.pgm.close_conn('meta') |
|
115 |
|
116 |
118 |
117 class SimpleTool(ToolBase): |
119 class SimpleTool(ToolBase): |
118 def __init__(self, name, desc, **kwargs): |
120 |
|
121 def __init__(self, name, desc=None, **kwargs): |
119 ToolBase.__init__(self, name, desc, **kwargs) |
122 ToolBase.__init__(self, name, desc, **kwargs) |
|
123 |
|
124 def specify_args(self): |
|
125 ToolBase.specify_args(self) |
120 self.parser.add_argument('target', metavar='target', type=str, help='Target database') |
126 self.parser.add_argument('target', metavar='target', type=str, help='Target database') |
121 |
127 |
122 def init(self, args=None): |
128 def setup(self, args=None): |
123 ToolBase.init(self, args) |
129 ToolBase.setup(self, args) |
124 self.prepare_conns(target=self.args.target) |
130 self.prepare_conns(target=self.args.target) |
125 |
131 |
126 |
132 |
127 class SrcDstTool(ToolBase): |
133 class SrcDstTool(ToolBase): |
128 def __init__(self, name, desc, **kwargs): |
134 |
|
135 def __init__(self, name, desc=None, *, allow_reverse=False, force_reverse=False, **kwargs): |
129 ToolBase.__init__(self, name, desc, **kwargs) |
136 ToolBase.__init__(self, name, desc, **kwargs) |
|
137 self.allow_reverse = allow_reverse |
|
138 self.force_reverse = force_reverse |
|
139 |
|
140 def specify_args(self): |
|
141 ToolBase.specify_args(self) |
130 self.parser.add_argument('src', metavar='source', type=str, help='Source database') |
142 self.parser.add_argument('src', metavar='source', type=str, help='Source database') |
131 self.parser.add_argument('dst', metavar='destination', type=str, help='Destination database') |
143 self.parser.add_argument('dst', metavar='destination', type=str, help='Destination database') |
132 if 'allow_reverse' in kwargs and kwargs['allow_reverse']: |
144 if self.allow_reverse: |
133 self.parser.add_argument('-r', '--reverse', action='store_true', help='Reverse operation. Swap source and destination.') |
145 self.parser.add_argument('-r', '--reverse', action='store_true', help='Reverse operation. Swap source and destination.') |
134 |
146 |
135 def init(self, args=None): |
147 def load_args(self, args=None, config_file=None): |
136 ToolBase.init(self, args) |
148 ToolBase.load_args(self, args, config_file) |
137 if self.is_reversed(): |
149 if self.is_reversed(): |
138 self.args.src, self.args.dst = self.args.dst, self.args.src |
150 self.args.src, self.args.dst = self.args.dst, self.args.src |
|
151 |
|
152 def setup(self, args=None): |
|
153 ToolBase.setup(self, args) |
139 self.prepare_conns(src=self.args.src, dst=self.args.dst) |
154 self.prepare_conns(src=self.args.src, dst=self.args.dst) |
140 |
155 |
141 def is_reversed(self): |
156 def is_reversed(self): |
142 return 'reverse' in self.args and self.args.reverse |
157 return ('reverse' in self.args and self.args.reverse) or self.force_reverse |
143 |
158 |
144 |
159 |
145 class SrcDstTablesTool(SrcDstTool): |
160 class SrcDstTablesTool(SrcDstTool): |
146 def __init__(self, name, desc, **kwargs): |
161 |
147 SrcDstTool.__init__(self, name, desc, **kwargs) |
162 def specify_args(self): |
|
163 SrcDstTool.specify_args(self) |
148 self.parser.add_argument('-t', '--src-table', metavar='source_table', |
164 self.parser.add_argument('-t', '--src-table', metavar='source_table', |
149 dest='srctable', type=str, default='', help='Source table name.') |
165 dest='srctable', type=str, default='', help='Source table name.') |
150 self.parser.add_argument('-s', '--src-schema', metavar='source_schema', |
166 self.parser.add_argument('-s', '--src-schema', metavar='source_schema', |
151 dest='srcschema', type=str, default='', help='Source schema name (default=public).') |
167 dest='srcschema', type=str, default='', help='Source schema name (default=public).') |
152 self.parser.add_argument('--dst-table', metavar='destination_table', |
168 self.parser.add_argument('--dst-table', metavar='destination_table', |
153 dest='dsttable', type=str, default='', help='Destination table name (default=source_table).') |
169 dest='dsttable', type=str, default='', help='Destination table name (default=source_table).') |
154 self.parser.add_argument('--dst-schema', metavar='destination_schema', |
170 self.parser.add_argument('--dst-schema', metavar='destination_schema', |
155 dest='dstschema', type=str, default='', help='Destination schema name (default=source_schema).') |
171 dest='dstschema', type=str, default='', help='Destination schema name (default=source_schema).') |
156 self.parser.add_argument('--regex', action='store_true', help="Use RE in schema or table name.") |
172 self.parser.add_argument('--regex', action='store_true', help="Use RE in schema or table name.") |
157 |
173 |
158 def init(self, args=None): |
174 def load_args(self, args=None, config_file=None): |
159 SrcDstTool.init(self, args) |
175 SrcDstTool.load_args(self, args, config_file) |
160 |
176 self.load_table_names() |
|
177 |
|
178 def load_table_names(self): |
161 self.schema1 = self.args.srcschema |
179 self.schema1 = self.args.srcschema |
162 self.table1 = self.args.srctable |
180 self.table1 = self.args.srctable |
163 self.schema2 = self.args.dstschema |
181 self.schema2 = self.args.dstschema |
164 self.table2 = self.args.dsttable |
182 self.table2 = self.args.dsttable |
165 |
183 |