pgtoolkit/toolbase.py
changeset 101 2a2d0d5df03b
parent 100 d6088dba8fea
child 102 fda45bdfd68d
equal deleted inserted replaced
100:d6088dba8fea 101:2a2d0d5df03b
    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