1""" 2Configure a Pacemaker/Corosync cluster with PCS 3=============================================== 4 5Configure Pacemaker/Cororsync clusters with the 6Pacemaker/Cororsync conifguration system (PCS) 7 8:depends: pcs 9 10.. versionadded:: 2016.3.0 11""" 12 13 14import logging 15 16import salt.utils.path 17 18log = logging.getLogger(__name__) 19 20 21def __virtual__(): 22 """ 23 Only load if pcs package is installed 24 """ 25 if salt.utils.path.which("pcs"): 26 return "pcs" 27 return (False, "Missing dependency: pcs") 28 29 30def __use_new_commands(): 31 """ 32 The command line arguments of pcs changed after version 0.10 33 This will return True if the new arguments are needed and 34 false if the old ones are needed 35 """ 36 pcs_version = __salt__["pkg.version"]("pcs") 37 log.debug("PCS package version %s", pcs_version) 38 39 if __salt__["pkg.version_cmp"](pcs_version, "0.10") == 1: 40 log.debug("New version, new command") 41 return True 42 else: 43 log.debug("Old Version") 44 return False 45 46 47def item_show( 48 item, item_id=None, item_type=None, show="show", extra_args=None, cibfile=None 49): 50 """ 51 Show an item via pcs command 52 (mainly for use with the pcs state module) 53 54 item 55 config, property, resource, constraint etc. 56 item_id 57 id of the item 58 item_type 59 item type 60 show 61 show command (probably None, default: show or status for newer implementation) 62 extra_args 63 additional options for the pcs command 64 cibfile 65 use cibfile instead of the live CIB 66 """ 67 68 new_commands = __use_new_commands() 69 70 cmd = ["pcs"] 71 72 if isinstance(cibfile, str): 73 cmd += ["-f", cibfile] 74 75 if isinstance(item, str): 76 cmd += [item] 77 elif isinstance(item, (list, tuple)): 78 cmd += item 79 80 # constraint command follows a different order 81 if item in ["constraint"]: 82 cmd += [item_type] 83 84 # New implementions use config instead of show. This resolves that issue. 85 if new_commands and ( 86 item != "config" and item != "constraint" and item != "property" 87 ): 88 if show == "show": 89 show = "config" 90 elif isinstance(show, (list, tuple)): 91 for index, value in enumerate(show): 92 if show[index] == "show": 93 show[index] = "config" 94 95 if isinstance(show, str): 96 cmd += [show] 97 elif isinstance(show, (list, tuple)): 98 cmd += show 99 100 if isinstance(item_id, str): 101 cmd += [item_id] 102 103 if isinstance(extra_args, (list, tuple)): 104 cmd += extra_args 105 106 # constraint command only shows id, when using '--full'-parameter 107 if item in ["constraint"]: 108 if not isinstance(extra_args, (list, tuple)) or "--full" not in extra_args: 109 cmd += ["--full"] 110 log.debug("Running item show %s", cmd) 111 return __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) 112 113 114def item_create( 115 item, item_id, item_type, create="create", extra_args=None, cibfile=None 116): 117 """ 118 Create an item via pcs command 119 (mainly for use with the pcs state module) 120 121 item 122 config, property, resource, constraint etc. 123 item_id 124 id of the item 125 item_type 126 item type 127 create 128 create command (create or set f.e., default: create) 129 extra_args 130 additional options for the pcs command 131 cibfile 132 use cibfile instead of the live CIB 133 """ 134 cmd = ["pcs"] 135 if isinstance(cibfile, str): 136 cmd += ["-f", cibfile] 137 138 if isinstance(item, str): 139 cmd += [item] 140 elif isinstance(item, (list, tuple)): 141 cmd += item 142 143 # constraint command follows a different order 144 if item in ["constraint"]: 145 if isinstance(item_type, str): 146 cmd += [item_type] 147 148 if isinstance(create, str): 149 cmd += [create] 150 elif isinstance(create, (list, tuple)): 151 cmd += create 152 153 # constraint command needs item_id in format 'id=<id' after all params 154 # constraint command follows a different order 155 if item not in ["constraint"]: 156 cmd += [item_id] 157 if isinstance(item_type, str): 158 cmd += [item_type] 159 160 if isinstance(extra_args, (list, tuple)): 161 # constraint command needs item_id in format 'id=<id' after all params 162 if item in ["constraint"]: 163 extra_args = extra_args + ["id={}".format(item_id)] 164 cmd += extra_args 165 166 return __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) 167 168 169def auth(nodes, pcsuser="hacluster", pcspasswd="hacluster", extra_args=None): 170 """ 171 Authorize nodes to the cluster 172 173 nodes 174 a list of nodes which should be authorized to the cluster 175 pcsuser 176 user for communitcation with PCS (default: hacluster) 177 pcspasswd 178 password for pcsuser (default: hacluster) 179 extra_args 180 list of extra option for the \'pcs cluster auth\' command. The newer cluster host command has no extra args and so will ignore it. 181 182 CLI Example: 183 184 .. code-block:: bash 185 186 salt '*' pcs.auth nodes='[ node1.example.org node2.example.org ]' pcsuser=hacluster pcspasswd=hoonetorg extra_args=[ '--force' ] 187 """ 188 if __use_new_commands(): 189 cmd = ["pcs", "host", "auth"] 190 else: 191 cmd = ["pcs", "cluster", "auth"] 192 193 cmd.extend(["-u", pcsuser, "-p", pcspasswd]) 194 195 if not __use_new_commands() and isinstance(extra_args, (list, tuple)): 196 cmd += extra_args 197 198 cmd += nodes 199 200 return __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) 201 202 203def is_auth(nodes, pcsuser="hacluster", pcspasswd="hacluster"): 204 """ 205 Check if nodes are already authorized 206 207 nodes 208 a list of nodes to be checked for authorization to the cluster 209 pcsuser 210 user for communitcation with PCS (default: hacluster) 211 pcspasswd 212 password for pcsuser (default: hacluster) 213 214 CLI Example: 215 216 .. code-block:: bash 217 218 salt '*' pcs.is_auth nodes='[node1.example.org node2.example.org]' pcsuser=hacluster pcspasswd=hoonetorg 219 """ 220 if __use_new_commands(): 221 cmd = ["pcs", "host", "auth", "-u", pcsuser, "-p", pcspasswd] 222 else: 223 cmd = ["pcs", "cluster", "auth"] 224 225 cmd += nodes 226 227 return __salt__["cmd.run_all"]( 228 cmd, stdin="\n\n", output_loglevel="trace", python_shell=False 229 ) 230 231 232def cluster_setup(nodes, pcsclustername="pcscluster", extra_args=None): 233 """ 234 Setup pacemaker cluster via pcs command 235 236 nodes 237 a list of nodes which should be set up 238 pcsclustername 239 Name of the Pacemaker cluster (default: pcscluster) 240 extra_args 241 list of extra option for the \'pcs cluster setup\' command 242 243 CLI Example: 244 245 .. code-block:: bash 246 247 salt '*' pcs.cluster_setup nodes='[ node1.example.org node2.example.org ]' pcsclustername=pcscluster 248 """ 249 cmd = ["pcs", "cluster", "setup"] 250 251 if __use_new_commands(): 252 cmd += [pcsclustername] 253 else: 254 cmd += ["--name", pcsclustername] 255 256 cmd += nodes 257 if isinstance(extra_args, (list, tuple)): 258 cmd += extra_args 259 260 log.debug("Running cluster setup: %s", cmd) 261 262 return __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) 263 264 265def cluster_destroy(extra_args=None): 266 """ 267 Destroy corosync cluster using the pcs command 268 269 extra_args 270 list of extra option for the \'pcs cluster destroy\' command (only really --all) 271 272 CLI Example: 273 274 .. code-block:: bash 275 276 salt '*' pcs.cluster_destroy extra_args=--all 277 """ 278 cmd = ["pcs", "cluster", "destroy"] 279 280 if isinstance(extra_args, (list, tuple)): 281 cmd += extra_args 282 283 log.debug("Running cluster destroy: %s", cmd) 284 285 return __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) 286 287 288def cluster_node_add(node, extra_args=None): 289 """ 290 Add a node to the pacemaker cluster via pcs command 291 292 node 293 node that should be added 294 extra_args 295 list of extra option for the \'pcs cluster node add\' command 296 297 CLI Example: 298 299 .. code-block:: bash 300 301 salt '*' pcs.cluster_node_add node=node2.example.org 302 """ 303 cmd = ["pcs", "cluster", "node", "add"] 304 305 cmd += [node] 306 if isinstance(extra_args, (list, tuple)): 307 cmd += extra_args 308 309 return __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) 310 311 312def cib_create(cibfile, scope="configuration", extra_args=None): 313 """ 314 Create a CIB-file from the current CIB of the cluster 315 316 cibfile 317 name/path of the file containing the CIB 318 scope 319 specific section of the CIB (default: configuration) 320 extra_args 321 additional options for creating the CIB-file 322 323 CLI Example: 324 325 .. code-block:: bash 326 327 salt '*' pcs.cib_create cibfile='/tmp/VIP_apache_1.cib' scope=False 328 """ 329 cmd = ["pcs", "cluster", "cib", cibfile] 330 if isinstance(scope, str): 331 cmd += ["scope={}".format(scope)] 332 if isinstance(extra_args, (list, tuple)): 333 cmd += extra_args 334 335 return __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) 336 337 338def cib_push(cibfile, scope="configuration", extra_args=None): 339 """ 340 Push a CIB-file as the new CIB to the cluster 341 342 cibfile 343 name/path of the file containing the CIB 344 scope 345 specific section of the CIB (default: configuration) 346 extra_args 347 additional options for creating the CIB-file 348 349 CLI Example: 350 351 .. code-block:: bash 352 353 salt '*' pcs.cib_push cibfile='/tmp/VIP_apache_1.cib' scope=False 354 """ 355 cmd = ["pcs", "cluster", "cib-push", cibfile] 356 if isinstance(scope, str): 357 cmd += ["scope={}".format(scope)] 358 if isinstance(extra_args, (list, tuple)): 359 cmd += extra_args 360 361 return __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) 362 363 364def config_show(cibfile=None): 365 """ 366 Show config of cluster 367 368 cibfile 369 name/path of the file containing the CIB 370 371 CLI Example: 372 373 .. code-block:: bash 374 375 salt '*' pcs.config_show cibfile='/tmp/cib_for_galera' 376 """ 377 return item_show(item="config", item_id=None, extra_args=None, cibfile=cibfile) 378 379 380def prop_show(prop, extra_args=None, cibfile=None): 381 """ 382 Show the value of a cluster property 383 384 prop 385 name of the property 386 extra_args 387 additional options for the pcs property command 388 cibfile 389 use cibfile instead of the live CIB 390 391 CLI Example: 392 393 .. code-block:: bash 394 395 salt '*' pcs.prop_show cibfile='/tmp/2_node_cluster.cib' prop='no-quorum-policy' cibfile='/tmp/2_node_cluster.cib' 396 """ 397 return item_show( 398 item="property", item_id=prop, extra_args=extra_args, cibfile=cibfile 399 ) 400 401 402def prop_set(prop, value, extra_args=None, cibfile=None): 403 """ 404 Set the value of a cluster property 405 406 prop 407 name of the property 408 value 409 value of the property prop 410 extra_args 411 additional options for the pcs property command 412 cibfile 413 use cibfile instead of the live CIB 414 415 CLI Example: 416 417 .. code-block:: bash 418 419 salt '*' pcs.prop_set prop='no-quorum-policy' value='ignore' cibfile='/tmp/2_node_cluster.cib' 420 """ 421 return item_create( 422 item="property", 423 item_id="{}={}".format(prop, value), 424 item_type=None, 425 create="set", 426 extra_args=extra_args, 427 cibfile=cibfile, 428 ) 429 430 431def stonith_show(stonith_id, extra_args=None, cibfile=None): 432 """ 433 Show the value of a cluster stonith 434 435 stonith_id 436 name for the stonith resource 437 extra_args 438 additional options for the pcs stonith command 439 cibfile 440 use cibfile instead of the live CIB 441 442 CLI Example: 443 444 .. code-block:: bash 445 446 salt '*' pcs.stonith_show stonith_id='eps_fence' cibfile='/tmp/2_node_cluster.cib' 447 """ 448 return item_show( 449 item="stonith", item_id=stonith_id, extra_args=extra_args, cibfile=cibfile 450 ) 451 452 453def stonith_create( 454 stonith_id, stonith_device_type, stonith_device_options=None, cibfile=None 455): 456 """ 457 Create a stonith resource via pcs command 458 459 stonith_id 460 name for the stonith resource 461 stonith_device_type 462 name of the stonith agent fence_eps, fence_xvm f.e. 463 stonith_device_options 464 additional options for creating the stonith resource 465 cibfile 466 use cibfile instead of the live CIB for manipulation 467 468 CLI Example: 469 470 .. code-block:: bash 471 472 salt '*' pcs.stonith_create stonith_id='eps_fence' stonith_device_type='fence_eps' 473 stonith_device_options="['pcmk_host_map=node1.example.org:01;node2.example.org:02', 'ipaddr=myepsdevice.example.org', 'action=reboot', 'power_wait=5', 'verbose=1', 'debug=/var/log/pcsd/eps_fence.log', 'login=hidden', 'passwd=hoonetorg']" cibfile='/tmp/cib_for_stonith.cib' 474 """ 475 return item_create( 476 item="stonith", 477 item_id=stonith_id, 478 item_type=stonith_device_type, 479 extra_args=stonith_device_options, 480 cibfile=cibfile, 481 ) 482 483 484def resource_show(resource_id, extra_args=None, cibfile=None): 485 """ 486 Show a resource via pcs command 487 488 resource_id 489 name of the resource 490 extra_args 491 additional options for the pcs command 492 cibfile 493 use cibfile instead of the live CIB 494 495 CLI Example: 496 497 .. code-block:: bash 498 499 salt '*' pcs.resource_show resource_id='galera' cibfile='/tmp/cib_for_galera.cib' 500 """ 501 return item_show( 502 item="resource", item_id=resource_id, extra_args=extra_args, cibfile=cibfile 503 ) 504 505 506def resource_create(resource_id, resource_type, resource_options=None, cibfile=None): 507 """ 508 Create a resource via pcs command 509 510 resource_id 511 name for the resource 512 resource_type 513 resource type (f.e. ocf:heartbeat:IPaddr2 or VirtualIP) 514 resource_options 515 additional options for creating the resource 516 cibfile 517 use cibfile instead of the live CIB for manipulation 518 519 CLI Example: 520 521 .. code-block:: bash 522 523 salt '*' pcs.resource_create resource_id='galera' resource_type='ocf:heartbeat:galera' resource_options="['wsrep_cluster_address=gcomm://node1.example.org,node2.example.org,node3.example.org', '--master']" cibfile='/tmp/cib_for_galera.cib' 524 """ 525 return item_create( 526 item="resource", 527 item_id=resource_id, 528 item_type=resource_type, 529 extra_args=resource_options, 530 cibfile=cibfile, 531 ) 532