Merge lp:~sidnei/charms/precise/postgresql/trunk into lp:charms/postgresql
- Precise Pangolin (12.04)
- trunk
- Merge into trunk
Proposed by Sidnei da Silva
| Status: | Merged | ||||
|---|---|---|---|---|---|
| Merged at revision: | 47 | ||||
| Proposed branch: | lp:~sidnei/charms/precise/postgresql/trunk | ||||
| Merge into: | lp:charms/postgresql | ||||
| Diff against target: | 760 lines (+216/-137) 5 files modified config.yaml (+4/-0) hooks/hooks.py (+210/-136) revision (+0/-1) templates/pg_hba.conf.tmpl (+1/-0) templates/postgres.cron.tmpl (+1/-0) | ||||
| To merge this branch: | bzr merge lp:~sidnei/charms/precise/postgresql/trunk | ||||
| Related bugs: |
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Marco Ceppi (community) | Approve | ||
| Review via email: | |||
Commit message
Fix flake8 warnings
Fix a NameError
Use params instead of string interpolation
Description of the change
Fix flake8 warnings
Fix a NameError
Use params instead of string interpolation
To post a comment you must log in.
- 101. By Sidnei da Silva
-
- Quote identifiers.
- Generate username like in -joined if not set on relation by the time -changed gets called.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === modified file 'config.yaml' |
| 2 | --- config.yaml 2013-04-01 17:13:24 +0000 |
| 3 | +++ config.yaml 2013-04-24 03:09:24 +0000 |
| 4 | @@ -286,6 +286,10 @@ |
| 5 | description: > |
| 6 | Block device for attached volumes as seen by the VM, will be "scanned" |
| 7 | for an unused device when "volume-map" is valid for the unit. |
| 8 | + backup_dir: |
| 9 | + default: "/var/lib/postgresql/backups" |
| 10 | + type: string |
| 11 | + description: Directory to place backups in |
| 12 | backup_schedule: |
| 13 | default: "13 4 * * *" |
| 14 | type: string |
| 15 | |
| 16 | === modified file 'hooks/hooks.py' |
| 17 | --- hooks/hooks.py 2013-04-10 15:14:18 +0000 |
| 18 | +++ hooks/hooks.py 2013-04-24 03:09:24 +0000 |
| 19 | @@ -66,7 +66,7 @@ |
| 20 | """Publish relevant unit state to relations""" |
| 21 | |
| 22 | def add(state_dict, key): |
| 23 | - if self.has_key(key): |
| 24 | + if key in self: |
| 25 | state_dict[key] = self[key] |
| 26 | |
| 27 | client_state = {} |
| 28 | @@ -159,7 +159,7 @@ |
| 29 | # shell helper |
| 30 | def volume_init_and_mount(volid): |
| 31 | command = ("scripts/volume-common.sh call " + |
| 32 | - "volume_init_and_mount %s" % volid) |
| 33 | + "volume_init_and_mount %s" % volid) |
| 34 | output = run(command) |
| 35 | if output.find("ERROR") >= 0: |
| 36 | return False |
| 37 | @@ -232,9 +232,8 @@ |
| 38 | # install_dir: create a directory |
| 39 | #------------------------------------------------------------------------------ |
| 40 | def install_dir(dirname, owner="root", group="root", mode=0700): |
| 41 | - command = \ |
| 42 | - '/usr/bin/install -o {} -g {} -m {} -d {}'.format(owner, group, oct(mode), |
| 43 | - dirname) |
| 44 | + command = '/usr/bin/install -o {} -g {} -m {} -d {}'.format( |
| 45 | + owner, group, oct(mode), dirname) |
| 46 | return run(command) |
| 47 | |
| 48 | |
| 49 | @@ -296,7 +295,7 @@ |
| 50 | |
| 51 | # Store a copy of our known live configuration so |
| 52 | # postgresql_reload_or_restart() can make good choices. |
| 53 | - if local_state.has_key('saved_config'): |
| 54 | + if 'saved_config' in local_state: |
| 55 | local_state['live_config'] = local_state['saved_config'] |
| 56 | local_state.save() |
| 57 | |
| 58 | @@ -356,7 +355,7 @@ |
| 59 | MSG_DEBUG, "PostgreSQL reload, config changes taking effect.") |
| 60 | rc = postgresql_reload() # No pending need to bounce, just reload. |
| 61 | |
| 62 | - if rc == 0 and local_state.has_key('saved_config'): |
| 63 | + if rc == 0 and 'saved_config' in local_state: |
| 64 | local_state['live_config'] = local_state['saved_config'] |
| 65 | local_state.save() |
| 66 | |
| 67 | @@ -508,7 +507,7 @@ |
| 68 | for unit in json.loads(json_units): |
| 69 | unit_data = \ |
| 70 | json.loads(relation_json(relation_id=relid, |
| 71 | - unit_name=unit)) |
| 72 | + unit_name=unit)) |
| 73 | for key in unit_data: |
| 74 | if key.endswith('-list'): |
| 75 | unit_data[key] = unit_data[key].split() |
| 76 | @@ -553,9 +552,9 @@ |
| 77 | conf_file = open("/etc/sysctl.d/50-postgresql.conf", "w") |
| 78 | conf_file.write("kernel.sem = 250 32000 100 1024\n") |
| 79 | conf_file.write("kernel.shmall = %s\n" % |
| 80 | - ((int(total_ram) * 1024 * 1024) + 1024),) |
| 81 | + ((int(total_ram) * 1024 * 1024) + 1024),) |
| 82 | conf_file.write("kernel.shmmax = %s\n" % |
| 83 | - ((int(total_ram) * 1024 * 1024) + 1024),) |
| 84 | + ((int(total_ram) * 1024 * 1024) + 1024),) |
| 85 | conf_file.close() |
| 86 | run("sysctl -p /etc/sysctl.d/50-postgresql.conf") |
| 87 | |
| 88 | @@ -578,7 +577,7 @@ |
| 89 | # Send config data to the template |
| 90 | # Return it as pg_config |
| 91 | pg_config = Template( |
| 92 | - open("templates/postgresql.conf.tmpl").read()).render(config_data) |
| 93 | + open("templates/postgresql.conf.tmpl").read()).render(config_data) |
| 94 | install_file(pg_config, postgresql_config) |
| 95 | |
| 96 | local_state['saved_config'] = config_data |
| 97 | @@ -600,7 +599,8 @@ |
| 98 | #------------------------------------------------------------------------------ |
| 99 | # generate_postgresql_hba: Creates the pg_hba.conf file |
| 100 | #------------------------------------------------------------------------------ |
| 101 | -def generate_postgresql_hba(postgresql_hba): |
| 102 | +def generate_postgresql_hba(postgresql_hba, user=None, |
| 103 | + schema_user=None, database=None): |
| 104 | |
| 105 | # Per Bug #1117542, when generating the postgresql_hba file we |
| 106 | # need to cope with private-address being either an IP address |
| 107 | @@ -620,19 +620,26 @@ |
| 108 | unit_name=os.environ['JUJU_UNIT_NAME'], relation_id=relid) |
| 109 | for unit in relation_list(relid): |
| 110 | relation = relation_get(unit_name=unit, relation_id=relid) |
| 111 | - relation['relation-id'] = relid |
| 112 | + relation['relation-id'] = relid |
| 113 | relation['unit'] = unit |
| 114 | |
| 115 | if relid.startswith('db-admin:'): |
| 116 | relation['user'] = 'all' |
| 117 | relation['database'] = 'all' |
| 118 | elif relid.startswith('db:'): |
| 119 | - relation['user'] = local_relation['user'] |
| 120 | - relation['schema_user'] = local_relation['schema_user'] |
| 121 | - relation['database'] = local_relation['database'] |
| 122 | + relation['user'] = local_relation.get('user', user) |
| 123 | + relation['schema_user'] = local_relation.get('schema_user', |
| 124 | + schema_user) |
| 125 | + relation['database'] = local_relation.get('database', database) |
| 126 | + |
| 127 | + if ((relation['user'] is None |
| 128 | + or relation['schema_user'] is None |
| 129 | + or relation['database'] is None)): |
| 130 | + # Missing info in relation for this unit, so skip it. |
| 131 | + continue |
| 132 | else: |
| 133 | raise RuntimeError( |
| 134 | - 'Unknown relation type {}'.format(repr(relation_id))) |
| 135 | + 'Unknown relation type {}'.format(repr(relid))) |
| 136 | |
| 137 | relation['private-address'] = munge_address( |
| 138 | relation['private-address']) |
| 139 | @@ -646,32 +653,31 @@ |
| 140 | # database. |
| 141 | for relid in relation_ids(relation_types=replication_relation_types): |
| 142 | for unit in relation_list(relid): |
| 143 | - replicated = True |
| 144 | relation = relation_get(unit_name=unit, relation_id=relid) |
| 145 | remote_addr = munge_address(relation['private-address']) |
| 146 | - remote_replication = { |
| 147 | - 'database': 'replication', 'user': 'juju_replication', |
| 148 | - 'private-address': remote_addr, |
| 149 | - 'relation-id': relid, |
| 150 | - 'unit': unit, |
| 151 | - } |
| 152 | + remote_replication = {'database': 'replication', |
| 153 | + 'user': 'juju_replication', |
| 154 | + 'private-address': remote_addr, |
| 155 | + 'relation-id': relid, |
| 156 | + 'unit': unit, |
| 157 | + } |
| 158 | relation_data.append(remote_replication) |
| 159 | - remote_pgdb = { |
| 160 | - 'database': 'postgres', 'user': 'juju_replication', |
| 161 | - 'private-address': remote_addr, |
| 162 | - 'relation-id': relid, |
| 163 | - 'unit': unit, |
| 164 | - } |
| 165 | + remote_pgdb = {'database': 'postgres', |
| 166 | + 'user': 'juju_replication', |
| 167 | + 'private-address': remote_addr, |
| 168 | + 'relation-id': relid, |
| 169 | + 'unit': unit, |
| 170 | + } |
| 171 | relation_data.append(remote_pgdb) |
| 172 | |
| 173 | # Hooks need permissions too to setup replication. |
| 174 | for relid in relation_ids(relation_types=['replication']): |
| 175 | - local_replication = { |
| 176 | - 'database': 'postgres', 'user': 'juju_replication', |
| 177 | - 'private-address': munge_address(get_unit_host()), |
| 178 | - 'relation-id': relid, |
| 179 | - 'unit': os.environ['JUJU_UNIT_NAME'], |
| 180 | - } |
| 181 | + local_replication = {'database': 'postgres', |
| 182 | + 'user': 'juju_replication', |
| 183 | + 'private-address': munge_address(get_unit_host()), |
| 184 | + 'relation-id': relid, |
| 185 | + 'unit': os.environ['JUJU_UNIT_NAME'], |
| 186 | + } |
| 187 | relation_data.append(local_replication) |
| 188 | |
| 189 | pg_hba_template = Template( |
| 190 | @@ -682,7 +688,6 @@ |
| 191 | postgresql_reload() |
| 192 | |
| 193 | |
| 194 | - |
| 195 | #------------------------------------------------------------------------------ |
| 196 | # install_postgresql_crontab: Creates the postgresql crontab file |
| 197 | #------------------------------------------------------------------------------ |
| 198 | @@ -718,7 +723,7 @@ |
| 199 | if port is None: |
| 200 | return(None) |
| 201 | return(subprocess.call(['open-port', "%d/%s" % |
| 202 | - (int(port), protocol)])) |
| 203 | + (int(port), protocol)])) |
| 204 | |
| 205 | |
| 206 | #------------------------------------------------------------------------------ |
| 207 | @@ -729,7 +734,7 @@ |
| 208 | if port is None: |
| 209 | return(None) |
| 210 | return(subprocess.call(['close-port', "%d/%s" % |
| 211 | - (int(port), protocol)])) |
| 212 | + (int(port), protocol)])) |
| 213 | |
| 214 | |
| 215 | #------------------------------------------------------------------------------ |
| 216 | @@ -754,9 +759,9 @@ |
| 217 | if pwd_length is None: |
| 218 | pwd_length = random.choice(range(20, 30)) |
| 219 | alphanumeric_chars = [l for l in (string.letters + string.digits) |
| 220 | - if l not in 'Iil0oO1'] |
| 221 | + if l not in 'Iil0oO1'] |
| 222 | random_chars = [random.choice(alphanumeric_chars) |
| 223 | - for i in range(pwd_length)] |
| 224 | + for i in range(pwd_length)] |
| 225 | return(''.join(random_chars)) |
| 226 | |
| 227 | |
| 228 | @@ -779,8 +784,8 @@ |
| 229 | return None |
| 230 | |
| 231 | |
| 232 | -def db_cursor( |
| 233 | - autocommit=False, db='template1', user='postgres', host=None, timeout=120): |
| 234 | +def db_cursor(autocommit=False, db='template1', user='postgres', |
| 235 | + host=None, timeout=120): |
| 236 | import psycopg2 |
| 237 | if host: |
| 238 | conn_str = "dbname={} host={} user={}".format(db, host, user) |
| 239 | @@ -844,34 +849,38 @@ |
| 240 | if volid: |
| 241 | if volume_is_permanent(volid): |
| 242 | if not volume_init_and_mount(volid): |
| 243 | - juju_log(MSG_ERROR, "volume_init_and_mount failed, " + |
| 244 | - "not applying changes") |
| 245 | + juju_log(MSG_ERROR, |
| 246 | + "volume_init_and_mount failed, " |
| 247 | + "not applying changes") |
| 248 | return False |
| 249 | |
| 250 | if not os.path.exists(data_directory_path): |
| 251 | - juju_log(MSG_CRITICAL, ("postgresql data dir = %s not found, " + |
| 252 | - "not applying changes.") % data_directory_path) |
| 253 | + juju_log(MSG_CRITICAL, |
| 254 | + "postgresql data dir = %s not found, " |
| 255 | + "not applying changes." % data_directory_path) |
| 256 | return False |
| 257 | |
| 258 | mount_point = volume_mount_point_from_volid(volid) |
| 259 | new_pg_dir = os.path.join(mount_point, "postgresql") |
| 260 | - new_pg_version_cluster_dir = os.path.join(new_pg_dir, |
| 261 | - config_data["version"], config_data["cluster_name"]) |
| 262 | + new_pg_version_cluster_dir = os.path.join( |
| 263 | + new_pg_dir, config_data["version"], config_data["cluster_name"]) |
| 264 | if not mount_point: |
| 265 | - juju_log(MSG_ERROR, "invalid mount point from volid = \"%s\", " + |
| 266 | + juju_log(MSG_ERROR, |
| 267 | + "invalid mount point from volid = \"%s\", " |
| 268 | "not applying changes." % mount_point) |
| 269 | return False |
| 270 | |
| 271 | - if (os.path.islink(data_directory_path) and |
| 272 | - os.readlink(data_directory_path) == new_pg_version_cluster_dir and |
| 273 | - os.path.isdir(new_pg_version_cluster_dir)): |
| 274 | - juju_log(MSG_INFO, |
| 275 | - "NOTICE: postgresql data dir '%s' already points to '%s', \ |
| 276 | - skipping storage changes." % |
| 277 | - (data_directory_path, new_pg_version_cluster_dir)) |
| 278 | - juju_log(MSG_INFO, |
| 279 | - "existing-symlink: to fix/avoid UID changes from previous " |
| 280 | - "units, doing: chown -R postgres:postgres %s" % new_pg_dir) |
| 281 | + if ((os.path.islink(data_directory_path) and |
| 282 | + os.readlink(data_directory_path) == new_pg_version_cluster_dir and |
| 283 | + os.path.isdir(new_pg_version_cluster_dir))): |
| 284 | + juju_log(MSG_INFO, |
| 285 | + "NOTICE: postgresql data dir '%s' already points " |
| 286 | + "to '%s', skipping storage changes." % |
| 287 | + (data_directory_path, new_pg_version_cluster_dir)) |
| 288 | + juju_log(MSG_INFO, |
| 289 | + "existing-symlink: to fix/avoid UID changes from " |
| 290 | + "previous units, doing: " |
| 291 | + "chown -R postgres:postgres %s" % new_pg_dir) |
| 292 | run("chown -R postgres:postgres %s" % new_pg_dir) |
| 293 | return True |
| 294 | |
| 295 | @@ -880,8 +889,8 @@ |
| 296 | # /var/lib/postgresql/9.1/main |
| 297 | curr_dir_stat = os.stat(data_directory_path) |
| 298 | for new_dir in [new_pg_dir, |
| 299 | - os.path.join(new_pg_dir, config_data["version"]), |
| 300 | - new_pg_version_cluster_dir]: |
| 301 | + os.path.join(new_pg_dir, config_data["version"]), |
| 302 | + new_pg_version_cluster_dir]: |
| 303 | if not os.path.isdir(new_dir): |
| 304 | juju_log(MSG_INFO, "mkdir %s" % new_dir) |
| 305 | os.mkdir(new_dir) |
| 306 | @@ -895,10 +904,10 @@ |
| 307 | # main-$TIMESTAMP |
| 308 | if not postgresql_stop(): |
| 309 | juju_log(MSG_ERROR, |
| 310 | - "postgresql_stop() returned False - can't migrate data.") |
| 311 | + "postgresql_stop() returned False - can't migrate data.") |
| 312 | return False |
| 313 | if not os.path.exists(os.path.join(new_pg_version_cluster_dir, |
| 314 | - "PG_VERSION")): |
| 315 | + "PG_VERSION")): |
| 316 | juju_log(MSG_WARNING, "migrating PG data %s/ -> %s/" % ( |
| 317 | data_directory_path, new_pg_version_cluster_dir)) |
| 318 | # void copying PID file to perm storage (shouldn't be any...) |
| 319 | @@ -909,18 +918,19 @@ |
| 320 | run(command) |
| 321 | try: |
| 322 | os.rename(data_directory_path, "%s-%d" % ( |
| 323 | - data_directory_path, int(time.time()))) |
| 324 | + data_directory_path, int(time.time()))) |
| 325 | juju_log(MSG_INFO, "NOTICE: symlinking %s -> %s" % |
| 326 | - (new_pg_version_cluster_dir, data_directory_path)) |
| 327 | + (new_pg_version_cluster_dir, data_directory_path)) |
| 328 | os.symlink(new_pg_version_cluster_dir, data_directory_path) |
| 329 | juju_log(MSG_INFO, |
| 330 | - "after-symlink: to fix/avoid UID changes from previous " |
| 331 | - "units, doing: chown -R postgres:postgres %s" % new_pg_dir) |
| 332 | + "after-symlink: to fix/avoid UID changes from " |
| 333 | + "previous units, doing: " |
| 334 | + "chown -R postgres:postgres %s" % new_pg_dir) |
| 335 | run("chown -R postgres:postgres %s" % new_pg_dir) |
| 336 | return True |
| 337 | except OSError: |
| 338 | juju_log(MSG_CRITICAL, "failed to symlink \"%s\" -> \"%s\"" % ( |
| 339 | - data_directory_path, mount_point)) |
| 340 | + data_directory_path, mount_point)) |
| 341 | return False |
| 342 | else: |
| 343 | juju_log(MSG_ERROR, "ERROR: Invalid volume storage configuration, " + |
| 344 | @@ -945,9 +955,9 @@ |
| 345 | if mounts: |
| 346 | juju_log(MSG_INFO, "FYI current mounted volumes: %s" % mounts) |
| 347 | juju_log(MSG_ERROR, |
| 348 | - "Disabled and stopped postgresql service, \ |
| 349 | - because of broken volume configuration - check \ |
| 350 | - 'volume-ephermeral-storage' and 'volume-map'") |
| 351 | + "Disabled and stopped postgresql service, " |
| 352 | + "because of broken volume configuration - check " |
| 353 | + "'volume-ephemeral-storage' and 'volume-map'") |
| 354 | sys.exit(1) |
| 355 | |
| 356 | if volume_is_permanent(volid): |
| 357 | @@ -963,8 +973,8 @@ |
| 358 | if mounts: |
| 359 | juju_log(MSG_INFO, "FYI current mounted volumes: %s" % mounts) |
| 360 | juju_log(MSG_ERROR, |
| 361 | - "Disabled and stopped postgresql service \ |
| 362 | - (config_changed_volume_apply failure)") |
| 363 | + "Disabled and stopped postgresql service " |
| 364 | + "(config_changed_volume_apply failure)") |
| 365 | sys.exit(1) |
| 366 | current_service_port = get_service_port(postgresql_config) |
| 367 | create_postgresql_config(postgresql_config) |
| 368 | @@ -1000,7 +1010,7 @@ |
| 369 | packages.extend(config_data["extra-packages"].split()) |
| 370 | apt_get_install(packages) |
| 371 | |
| 372 | - if not local_state.has_key('state'): |
| 373 | + if not 'state' in local_state: |
| 374 | # Fresh installation. Because this function is invoked by both |
| 375 | # the install hook and the upgrade-charm hook, we need to guard |
| 376 | # any non-idempotent setup. We should probably fix this; it |
| 377 | @@ -1028,9 +1038,9 @@ |
| 378 | backup_job = Template( |
| 379 | open("templates/pg_backup_job.tmpl").read()).render(paths) |
| 380 | install_file(dump_script, '{}/dump-pg-db'.format(postgresql_scripts_dir), |
| 381 | - mode=0755) |
| 382 | + mode=0755) |
| 383 | install_file(backup_job, '{}/pg_backup_job'.format(postgresql_scripts_dir), |
| 384 | - mode=0755) |
| 385 | + mode=0755) |
| 386 | install_postgresql_crontab(postgresql_crontab) |
| 387 | open_port(5432) |
| 388 | |
| 389 | @@ -1043,7 +1053,7 @@ |
| 390 | def upgrade_charm(): |
| 391 | # Detect if we are upgrading from the old charm that used repmgr for |
| 392 | # replication. |
| 393 | - from_repmgr = not local_state.has_key('juju_replication') |
| 394 | + from_repmgr = not 'juju_replication' in local_state |
| 395 | |
| 396 | # Handle renaming of the repmgr user to juju_replication. |
| 397 | if from_repmgr and local_state['state'] == 'master': |
| 398 | @@ -1073,16 +1083,61 @@ |
| 399 | break |
| 400 | |
| 401 | |
| 402 | +def quote_identifier(identifier): |
| 403 | + r'''Quote an identifier, such as a table or role name. |
| 404 | + |
| 405 | + In SQL, identifiers are quoted using " rather than ' (which is reserved |
| 406 | + for strings). |
| 407 | + |
| 408 | + >>> print(quote_identifier('hello')) |
| 409 | + "hello" |
| 410 | + |
| 411 | + Quotes and Unicode are handled if you make use of them in your |
| 412 | + identifiers. |
| 413 | + |
| 414 | + >>> print(quote_identifier("'")) |
| 415 | + "'" |
| 416 | + >>> print(quote_identifier('"')) |
| 417 | + """" |
| 418 | + >>> print(quote_identifier("\\")) |
| 419 | + "\" |
| 420 | + >>> print(quote_identifier('\\"')) |
| 421 | + "\""" |
| 422 | + >>> print(quote_identifier('\\ aargh \u0441\u043b\u043e\u043d')) |
| 423 | + U&"\\ aargh \0441\043b\043e\043d" |
| 424 | + ''' |
| 425 | + try: |
| 426 | + return '"%s"' % identifier.encode('US-ASCII').replace('"', '""') |
| 427 | + except UnicodeEncodeError: |
| 428 | + escaped = [] |
| 429 | + for c in identifier: |
| 430 | + if c == '\\': |
| 431 | + escaped.append('\\\\') |
| 432 | + elif c == '"': |
| 433 | + escaped.append('""') |
| 434 | + else: |
| 435 | + c = c.encode('US-ASCII', 'backslashreplace') |
| 436 | + # Note Python only supports 32 bit unicode, so we use |
| 437 | + # the 4 hexdigit PostgreSQL syntax (\1234) rather than |
| 438 | + # the 6 hexdigit format (\+123456). |
| 439 | + if c.startswith('\\u'): |
| 440 | + c = '\\' + c[2:] |
| 441 | + escaped.append(c) |
| 442 | + return 'U&"%s"' % ''.join(escaped) |
| 443 | + |
| 444 | + |
| 445 | +def sanitize(s): |
| 446 | + s = s.replace(':', '_') |
| 447 | + s = s.replace('-', '_') |
| 448 | + s = s.replace('/', '_') |
| 449 | + s = s.replace('"', '_') |
| 450 | + s = s.replace("'", '_') |
| 451 | + return s |
| 452 | + |
| 453 | + |
| 454 | def user_name(relid, remote_unit, admin=False, schema=False): |
| 455 | - def sanitize(s): |
| 456 | - s = s.replace(':', '_') |
| 457 | - s = s.replace('-', '_') |
| 458 | - s = s.replace('/', '_') |
| 459 | - s = s.replace('"', '_') |
| 460 | - s = s.replace("'", '_') |
| 461 | - return s |
| 462 | # Per Bug #1160530, don't append the remote unit number to the user name. |
| 463 | - components = [sanitize(relid), sanitize(re.split("/",remote_unit)[0])] |
| 464 | + components = [sanitize(relid), sanitize(re.split("/", remote_unit)[0])] |
| 465 | if admin: |
| 466 | components.append("admin") |
| 467 | elif schema: |
| 468 | @@ -1099,6 +1154,8 @@ |
| 469 | |
| 470 | |
| 471 | def create_user(user, admin=False, replication=False): |
| 472 | + from psycopg2.extensions import AsIs |
| 473 | + |
| 474 | password = get_password(user) |
| 475 | if password is None: |
| 476 | password = pwgen() |
| 477 | @@ -1107,8 +1164,7 @@ |
| 478 | action = ["ALTER ROLE"] |
| 479 | else: |
| 480 | action = ["CREATE ROLE"] |
| 481 | - action.append('"{}"'.format(user)) |
| 482 | - action.append('WITH LOGIN') |
| 483 | + action.append('%s WITH LOGIN') |
| 484 | if admin: |
| 485 | action.append('SUPERUSER') |
| 486 | else: |
| 487 | @@ -1119,11 +1175,13 @@ |
| 488 | action.append('NOREPLICATION') |
| 489 | action.append('PASSWORD %s') |
| 490 | sql = ' '.join(action) |
| 491 | - run_sql_as_postgres(sql, password) |
| 492 | + run_sql_as_postgres(sql, AsIs(quote_identifier(user)), password) |
| 493 | return password |
| 494 | |
| 495 | |
| 496 | def grant_roles(user, roles): |
| 497 | + from psycopg2.extensions import AsIs |
| 498 | + |
| 499 | # Delete previous roles |
| 500 | sql = ("DELETE FROM pg_auth_members WHERE member IN (" |
| 501 | "SELECT oid FROM pg_roles WHERE rolname = %s)") |
| 502 | @@ -1131,33 +1189,39 @@ |
| 503 | |
| 504 | for role in roles: |
| 505 | ensure_role(role) |
| 506 | - sql = "GRANT {} to {}".format(role, user) |
| 507 | - run_sql_as_postgres(sql) |
| 508 | + sql = "GRANT %s to %s" |
| 509 | + run_sql_as_postgres(sql, AsIs(quote_identifier(role)), |
| 510 | + AsIs(quote_identifier(user))) |
| 511 | |
| 512 | |
| 513 | def ensure_role(role): |
| 514 | + from psycopg2.extensions import AsIs |
| 515 | + |
| 516 | sql = "SELECT oid FROM pg_roles WHERE rolname = %s" |
| 517 | if run_select_as_postgres(sql, role)[0] != 0: |
| 518 | # role already exists |
| 519 | pass |
| 520 | else: |
| 521 | - sql = "CREATE ROLE {} INHERIT NOLOGIN".format(role) |
| 522 | - run_sql_as_postgres(sql) |
| 523 | + sql = "CREATE ROLE %s INHERIT NOLOGIN" |
| 524 | + run_sql_as_postgres(sql, AsIs(quote_identifier(role))) |
| 525 | |
| 526 | |
| 527 | def ensure_database(user, schema_user, database): |
| 528 | + from psycopg2.extensions import AsIs |
| 529 | + |
| 530 | sql = "SELECT datname FROM pg_database WHERE datname = %s" |
| 531 | if run_select_as_postgres(sql, database)[0] != 0: |
| 532 | # DB already exists |
| 533 | pass |
| 534 | else: |
| 535 | - sql = "CREATE DATABASE {}".format(database) |
| 536 | - run_sql_as_postgres(sql) |
| 537 | - sql = "GRANT ALL PRIVILEGES ON DATABASE {} TO {}".format(database, |
| 538 | - schema_user) |
| 539 | - run_sql_as_postgres(sql) |
| 540 | - sql = "GRANT CONNECT ON DATABASE {} TO {}".format(database, user) |
| 541 | - run_sql_as_postgres(sql) |
| 542 | + sql = "CREATE DATABASE %s" |
| 543 | + run_sql_as_postgres(sql, AsIs(quote_identifier(database))) |
| 544 | + sql = "GRANT ALL PRIVILEGES ON DATABASE %s TO %s" |
| 545 | + run_sql_as_postgres(sql, AsIs(quote_identifier(database)), |
| 546 | + AsIs(quote_identifier(schema_user))) |
| 547 | + sql = "GRANT CONNECT ON DATABASE %s TO %s" |
| 548 | + run_sql_as_postgres(sql, AsIs(quote_identifier(database)), |
| 549 | + AsIs(quote_identifier(user))) |
| 550 | |
| 551 | |
| 552 | def get_relation_host(): |
| 553 | @@ -1189,7 +1253,9 @@ |
| 554 | host = get_unit_host() |
| 555 | run("relation-set host='%s' database='%s' port='%s'" % ( |
| 556 | host, database, config_data["listen_port"])) |
| 557 | - generate_postgresql_hba(postgresql_hba) |
| 558 | + generate_postgresql_hba(postgresql_hba, user=user, |
| 559 | + schema_user=schema_user, |
| 560 | + database=database) |
| 561 | |
| 562 | |
| 563 | def db_admin_relation_joined_changed(user, database='all'): |
| 564 | @@ -1204,15 +1270,20 @@ |
| 565 | |
| 566 | |
| 567 | def db_relation_broken(user, database): |
| 568 | - sql = "REVOKE ALL PRIVILEGES ON {} FROM {}".format(database, user) |
| 569 | - run_sql_as_postgres(sql) |
| 570 | - sql = "REVOKE ALL PRIVILEGES ON {} FROM {}_schema".format(database, user) |
| 571 | - run_sql_as_postgres(sql) |
| 572 | + from psycopg2.extensions import AsIs |
| 573 | + |
| 574 | + sql = "REVOKE ALL PRIVILEGES ON %s FROM %s" |
| 575 | + run_sql_as_postgres(sql, AsIs(quote_identifier(database)), |
| 576 | + AsIs(quote_identifier(user))) |
| 577 | + run_sql_as_postgres(sql, AsIs(quote_identifier(database)), |
| 578 | + AsIs(quote_identifier(user + "_schema"))) |
| 579 | |
| 580 | |
| 581 | def db_admin_relation_broken(user): |
| 582 | - sql = "ALTER USER {} NOSUPERUSER".format(user) |
| 583 | - run_sql_as_postgres(sql) |
| 584 | + from psycopg2.extensions import AsIs |
| 585 | + |
| 586 | + sql = "ALTER USER %s NOSUPERUSER" |
| 587 | + run_sql_as_postgres(sql, AsIs(quote_identifier(user))) |
| 588 | generate_postgresql_hba(postgresql_hba) |
| 589 | |
| 590 | |
| 591 | @@ -1301,7 +1372,7 @@ |
| 592 | if passwords: |
| 593 | pgpass = '\n'.join( |
| 594 | "*:*:*:{}:{}".format(username, password) |
| 595 | - for username, password in passwords.items()) |
| 596 | + for username, password in passwords.items()) |
| 597 | install_file( |
| 598 | pgpass, charm_pgpass, |
| 599 | owner="postgres", group="postgres", mode=0o400) |
| 600 | @@ -1528,8 +1599,8 @@ |
| 601 | |
| 602 | if remote_is_master and remote_has_authorized: |
| 603 | replication_password = relation['replication_password'] |
| 604 | - if local_state.get( |
| 605 | - 'replication_password', None) != replication_password: |
| 606 | + if ((local_state.get('replication_password', None) != |
| 607 | + replication_password)): |
| 608 | local_state['replication_password'] = replication_password |
| 609 | generate_pgpass() |
| 610 | |
| 611 | @@ -1601,12 +1672,10 @@ |
| 612 | postgresql_stop() |
| 613 | juju_log(MSG_INFO, "Cloning master {}".format(master_unit)) |
| 614 | |
| 615 | - cmd = [ |
| 616 | - 'sudo', '-E', '-u', 'postgres', # -E needed to locate pgpass file. |
| 617 | - 'pg_basebackup', '-D', postgresql_cluster_dir, |
| 618 | - '--xlog', '--checkpoint=fast', '--no-password', |
| 619 | - '-h', master_host, '-p', '5432', '--username=juju_replication', |
| 620 | - ] |
| 621 | + cmd = ['sudo', '-E', '-u', 'postgres', # -E needed to locate pgpass file. |
| 622 | + 'pg_basebackup', '-D', postgresql_cluster_dir, |
| 623 | + '--xlog', '--checkpoint=fast', '--no-password', |
| 624 | + '-h', master_host, '-p', '5432', '--username=juju_replication'] |
| 625 | juju_log(MSG_DEBUG, ' '.join(cmd)) |
| 626 | if os.path.isdir(postgresql_cluster_dir): |
| 627 | shutil.rmtree(postgresql_cluster_dir) |
| 628 | @@ -1667,8 +1736,8 @@ |
| 629 | os.path.join(postgresql_cluster_dir, 'backup_label')) |
| 630 | |
| 631 | |
| 632 | -def postgresql_wal_received_offset( |
| 633 | - host, db='postgres', user='juju_replication'): |
| 634 | +def postgresql_wal_received_offset(host, db='postgres', |
| 635 | + user='juju_replication'): |
| 636 | cur = db_cursor(autocommit=True, db=db, user=user, host=host) |
| 637 | cur.execute('SELECT pg_is_in_recovery(), pg_last_xlog_receive_location()') |
| 638 | is_in_recovery, xlog_received = cur.fetchone() |
| 639 | @@ -1713,8 +1782,8 @@ |
| 640 | # --- exported service configuration file |
| 641 | from jinja2 import Environment, FileSystemLoader |
| 642 | template_env = Environment( |
| 643 | - loader=FileSystemLoader(os.path.join(os.environ['CHARM_DIR'], |
| 644 | - 'templates'))) |
| 645 | + loader=FileSystemLoader( |
| 646 | + os.path.join(os.environ['CHARM_DIR'], 'templates'))) |
| 647 | templ_vars = { |
| 648 | 'nagios_hostname': nagios_hostname, |
| 649 | 'nagios_servicegroup': config_data['nagios_context'], |
| 650 | @@ -1730,11 +1799,11 @@ |
| 651 | with open(nrpe_check_file, 'w') as nrpe_check_config: |
| 652 | nrpe_check_config.write("# check pgsql\n") |
| 653 | nrpe_check_config.write( |
| 654 | - "command[check_pgsql]=/usr/lib/nagios/plugins/check_pgsql -p {}" |
| 655 | + "command[check_pgsql]=/usr/lib/nagios/plugins/check_pgsql -P {}" |
| 656 | .format(config_data['listen_port'])) |
| 657 | # pgsql backups |
| 658 | nrpe_check_file = '/etc/nagios/nrpe.d/check_pgsql_backups.cfg' |
| 659 | - backup_log = "%s/backups.log".format(postgresql_logs_dir) |
| 660 | + backup_log = "{}/backups.log".format(postgresql_logs_dir) |
| 661 | # XXX: these values _should_ be calculated from the backup schedule |
| 662 | # perhaps warn = backup_frequency * 1.5, crit = backup_frequency * 2 |
| 663 | warn_age = 172800 |
| 664 | @@ -1765,12 +1834,15 @@ |
| 665 | postgresql_crontab = "/etc/cron.d/postgresql" |
| 666 | postgresql_service_config_dir = "/var/run/postgresql" |
| 667 | postgresql_scripts_dir = os.path.join(postgresql_data_dir, 'scripts') |
| 668 | -postgresql_backups_dir = os.path.join(postgresql_data_dir, 'backups') |
| 669 | +postgresql_backups_dir = ( |
| 670 | + config_data['backup_dir'].strip() or |
| 671 | + os.path.join(postgresql_data_dir, 'backups')) |
| 672 | postgresql_logs_dir = os.path.join(postgresql_data_dir, 'logs') |
| 673 | postgres_ssh_dir = os.path.expanduser('~postgres/.ssh') |
| 674 | postgres_ssh_public_key = os.path.join(postgres_ssh_dir, 'id_rsa.pub') |
| 675 | postgres_ssh_private_key = os.path.join(postgres_ssh_dir, 'id_rsa') |
| 676 | -postgres_ssh_authorized_keys = os.path.join(postgres_ssh_dir, 'authorized_keys') |
| 677 | +postgres_ssh_authorized_keys = os.path.join(postgres_ssh_dir, |
| 678 | + 'authorized_keys') |
| 679 | postgres_ssh_known_hosts = os.path.join(postgres_ssh_dir, 'known_hosts') |
| 680 | hook_name = os.path.basename(sys.argv[0]) |
| 681 | replication_relation_types = ['master', 'slave', 'replication'] |
| 682 | @@ -1829,7 +1901,9 @@ |
| 683 | database = relation_get('database', os.environ['JUJU_UNIT_NAME']) |
| 684 | |
| 685 | user = relation_get('user', os.environ['JUJU_UNIT_NAME']) |
| 686 | - assert user, 'user not set' |
| 687 | + if not user: |
| 688 | + user = user_name( |
| 689 | + os.environ['JUJU_RELATION_ID'], os.environ['JUJU_REMOTE_UNIT']) |
| 690 | db_relation_joined_changed(user, database, roles) |
| 691 | |
| 692 | elif hook_name == "db-relation-broken": |
| 693 | @@ -1838,31 +1912,31 @@ |
| 694 | os.environ['JUJU_RELATION_ID'], os.environ['JUJU_REMOTE_UNIT']) |
| 695 | db_relation_broken(user, database) |
| 696 | |
| 697 | - elif hook_name in [ |
| 698 | - "db-admin-relation-joined", "db-admin-relation-changed"]: |
| 699 | + elif hook_name in ("db-admin-relation-joined", |
| 700 | + "db-admin-relation-changed"): |
| 701 | user = user_name(os.environ['JUJU_RELATION_ID'], |
| 702 | - os.environ['JUJU_REMOTE_UNIT'], admin=True) |
| 703 | + os.environ['JUJU_REMOTE_UNIT'], admin=True) |
| 704 | db_admin_relation_joined_changed(user, 'all') |
| 705 | |
| 706 | elif hook_name == "db-admin-relation-broken": |
| 707 | # XXX: Fix: relation is not set when it is already broken |
| 708 | # cannot determine the user name |
| 709 | user = user_name(os.environ['JUJU_RELATION_ID'], |
| 710 | - os.environ['JUJU_REMOTE_UNIT'], admin=True) |
| 711 | + os.environ['JUJU_REMOTE_UNIT'], admin=True) |
| 712 | db_admin_relation_broken(user) |
| 713 | |
| 714 | elif hook_name == "nrpe-external-master-relation-changed": |
| 715 | update_nrpe_checks() |
| 716 | |
| 717 | - elif hook_name in ( |
| 718 | - 'master-relation-joined', 'master-relation-changed', |
| 719 | - 'slave-relation-joined', 'slave-relation-changed', |
| 720 | - 'replication-relation-joined', 'replication-relation-changed'): |
| 721 | + elif hook_name in ('master-relation-joined', 'master-relation-changed', |
| 722 | + 'slave-relation-joined', 'slave-relation-changed', |
| 723 | + 'replication-relation-joined', |
| 724 | + 'replication-relation-changed'): |
| 725 | replication_relation_changed() |
| 726 | |
| 727 | - elif hook_name in ( |
| 728 | - 'master-relation-broken', 'slave-relation-broken', |
| 729 | - 'replication-relation-broken', 'replication-relation-departed'): |
| 730 | + elif hook_name in ('master-relation-broken', 'slave-relation-broken', |
| 731 | + 'replication-relation-broken', |
| 732 | + 'replication-relation-departed'): |
| 733 | replication_relation_broken() |
| 734 | |
| 735 | #-------- persistent-storage-relation-joined, |
| 736 | |
| 737 | === removed file 'revision' |
| 738 | --- revision 2013-04-15 11:50:30 +0000 |
| 739 | +++ revision 1970-01-01 00:00:00 +0000 |
| 740 | @@ -1,1 +0,0 @@ |
| 741 | -27 |
| 742 | |
| 743 | === modified file 'templates/pg_hba.conf.tmpl' |
| 744 | --- templates/pg_hba.conf.tmpl 2013-02-22 12:02:45 +0000 |
| 745 | +++ templates/pg_hba.conf.tmpl 2013-04-24 03:09:24 +0000 |
| 746 | @@ -4,6 +4,7 @@ |
| 747 | |
| 748 | # Database administrative login by UNIX sockets |
| 749 | local all postgres ident map=superusers |
| 750 | +local all nagios md5 |
| 751 | |
| 752 | {% if access_list is defined -%} |
| 753 | {% for unit in access_list -%} |
| 754 | |
| 755 | === modified file 'templates/postgres.cron.tmpl' |
| 756 | --- templates/postgres.cron.tmpl 2012-10-16 19:03:17 +0000 |
| 757 | +++ templates/postgres.cron.tmpl 2013-04-24 03:09:24 +0000 |
| 758 | @@ -1,1 +1,2 @@ |
| 759 | {{backup_schedule}} postgres {{scripts_dir}}/pg_backup_job {{backup_days}} |
| 760 | + |
LGTM, thanks!