예제 #1
0
 def test_invalid_options(self):
     allowed_options = [
         "ip_version",
         "link_priority",
         "linknumber",
         "mcastport",
         "ping_interval",
         "ping_precision",
         "ping_timeout",
         "pong_count",
         "transport",
     ]
     assert_report_item_list_equal(
         config_validators.create_link_list_knet(
             [{
                 "nonsense1": "0",
                 "nonsense2": "doesnt matter",
             }, {
                 "nonsense3": "who cares",
             }], 3), [
                 fixture.error(
                     report_codes.INVALID_OPTIONS,
                     option_names=["nonsense1", "nonsense2"],
                     option_type="link",
                     allowed=allowed_options,
                     allowed_patterns=[],
                 ),
                 fixture.error(
                     report_codes.INVALID_OPTIONS,
                     option_names=["nonsense3"],
                     option_type="link",
                     allowed=allowed_options,
                     allowed_patterns=[],
                 ),
             ])
예제 #2
0
 def test_max_link_number_too_low(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([
             {
                 "ip_version": "ipv4",
                 "linknumber": "0",
             },
         ], -1), [])
예제 #3
0
 def test_invalid_all_values(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([{
             "ip_version": "ipv5",
             "linknumber": "-1",
             "link_priority": "256",
             "mcastport": "65536",
             "transport": "tcp",
         }, {
             "ping_interval": "-250",
             "ping_precision": "-15",
             "ping_timeout": "-750",
             "pong_count": "-10",
             "transport": "udpu",
         }], 3), [
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="ipv5",
                           option_name="ip_version",
                           allowed_values=("ipv4", "ipv6")),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="-1",
                           option_name="linknumber",
                           allowed_values="0..3"),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="256",
                           option_name="link_priority",
                           allowed_values="0..255"),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="65536",
                           option_name="mcastport",
                           allowed_values="a port number (1-65535)"),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="tcp",
                           option_name="transport",
                           allowed_values=("sctp", "udp")),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="-250",
                           option_name="ping_interval",
                           allowed_values="a non-negative integer"),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="-15",
                           option_name="ping_precision",
                           allowed_values="a non-negative integer"),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="-750",
                           option_name="ping_timeout",
                           allowed_values="a non-negative integer"),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="-10",
                           option_name="pong_count",
                           allowed_values="a non-negative integer"),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="udpu",
                           option_name="transport",
                           allowed_values=("sctp", "udp")),
         ])
예제 #4
0
 def test_link_count_in_range(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
         ], 1), [])
예제 #5
0
 def test_linknumber_to_high(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([{
             "linknumber": "3"
         }], 2), [
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="3",
                           option_name="linknumber",
                           allowed_values="0..2"),
         ])
예제 #6
0
 def test_link_count_too_high(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
         ], 1), [
             fixture.error(report_codes.COROSYNC_TOO_MANY_LINKS,
                           actual_count=3,
                           max_count=2,
                           transport="knet")
         ])
예제 #7
0
 def test_max_link_number_too_high(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([
             {
                 "ip_version": "ipv4",
                 "linknumber": "8",
             },
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
             {
                 "ip_version": "ipv4"
             },
         ], 8), [
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="8",
                           option_name="linknumber",
                           allowed_values="0..7"),
             fixture.error(report_codes.COROSYNC_TOO_MANY_LINKS,
                           actual_count=9,
                           max_count=8,
                           transport="knet")
         ])
예제 #8
0
 def test_ping_dependencies(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([
             {
                 "ping_interval": "250",
                 "ping_timeout": "750",
             },
             {
                 "ping_interval": "250",
             },
             {
                 "ping_timeout": "750",
             },
             {
                 "ping_interval": "",
                 "ping_timeout": "750",
             },
             {
                 "ping_interval": "250",
                 "ping_timeout": "",
             },
         ], 5), [
             fixture.error(report_codes.PREREQUISITE_OPTION_IS_MISSING,
                           option_name="ping_interval",
                           option_type="link",
                           prerequisite_name="ping_timeout",
                           prerequisite_type="link"),
             fixture.error(report_codes.PREREQUISITE_OPTION_IS_MISSING,
                           option_name="ping_timeout",
                           option_type="link",
                           prerequisite_name="ping_interval",
                           prerequisite_type="link"),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="",
                           option_name="ping_interval",
                           allowed_values="a non-negative integer"),
             fixture.error(report_codes.INVALID_OPTION_VALUE,
                           option_value="",
                           option_name="ping_timeout",
                           allowed_values="a non-negative integer"),
         ])
예제 #9
0
 def test_linknumber_not_unique(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([
             {
                 "linknumber": "2"
             },
             {
                 "linknumber": "0"
             },
             {
                 "linknumber": "0"
             },
             {
                 "linknumber": "1"
             },
             {
                 "linknumber": "2"
             },
         ], 4), [
             fixture.error(report_codes.COROSYNC_LINK_NUMBER_DUPLICATION,
                           link_number_list=["0", "2"])
         ])
예제 #10
0
 def test_all_valid(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([{
             "ip_version": "ipv4",
             "linknumber": "0",
             "link_priority": "20",
             "mcastport": "5405",
             "ping_interval": "250",
             "ping_precision": "15",
             "ping_timeout": "750",
             "pong_count": "10",
             "transport": "sctp",
         }, {
             "ip_version": "ipv6",
             "linknumber": "1",
             "link_priority": "10",
             "mcastport": "5415",
             "ping_interval": "2500",
             "ping_precision": "150",
             "ping_timeout": "7500",
             "pong_count": "100",
             "transport": "udp",
         }], 2), [])
예제 #11
0
 def test_linknumber_within_range(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([{
             "linknumber": "2"
         }], 2), [])
예제 #12
0
 def test_no_options(self):
     assert_report_item_list_equal(
         config_validators.create_link_list_knet([{}], 8), [])
예제 #13
0
파일: cluster.py 프로젝트: bashims/pcs
def setup(env,
          cluster_name,
          nodes,
          transport_type=None,
          transport_options=None,
          link_list=None,
          compression_options=None,
          crypto_options=None,
          totem_options=None,
          quorum_options=None,
          wait=False,
          start=False,
          enable=False,
          force=False,
          force_unresolvable=False):
    """
    Set up cluster on specified nodes.
    Validation of the inputs is done here. Possible existing clusters are
    destroyed (when using force). Authkey files for corosync and pacemaer,
    known hosts and and newly generated corosync.conf are distributed to all
    nodes.
    Raise LibraryError on any error.

    env LibraryEnvironment
    cluster_name string -- name of a cluster to set up
    nodes list -- list of dicts which represents node.
        Supported keys are: name (required), addrs
    transport_type string -- transport type of a cluster
    transport_options dict -- transport specific options
    link_list list of dict -- list of links, depends of transport_type
    compression_options dict -- only available for transport_type == 'knet'. In
        corosync.conf they are prefixed 'knet_compression_'
    crypto_options dict -- only available for transport_type == 'knet'. In
        corosync.conf they are prefixed 'crypto_'
    totem_options dict -- options of section 'totem' in corosync.conf
    quorum_options dict -- options of section 'quorum' in corosync.conf
    wait -- specifies if command should try to wait for cluster to start up.
        Has no effect start is False. If set to False command will not wait for
        cluster to start. If None command will wait for some default timeout.
        If int wait set timeout to int value of seconds.
    start bool -- if True start cluster when it is set up
    enable bool -- if True enable cluster when it is set up
    force bool -- if True some validations errors are treated as warnings
    force_unresolvable bool -- if True not resolvable addresses of nodes are
        treated as warnings
    """
    _ensure_live_env(env)  # raises if env is not live

    transport_options = transport_options or {}
    link_list = link_list or []
    compression_options = compression_options or {}
    crypto_options = crypto_options or {}
    totem_options = totem_options or {}
    quorum_options = quorum_options or {}
    nodes = [_normalize_dict(node, {"addrs"}) for node in nodes]

    report_processor = SimpleReportProcessor(env.report_processor)
    target_factory = env.get_node_target_factory()

    # Get targets for all nodes and report unknown (== not-authorized) nodes.
    # If a node doesn't contain the 'name' key, validation of inputs reports it.
    # That means we don't report missing names but cannot rely on them being
    # present either.
    target_report_list, target_list = (
        target_factory.get_target_list_with_reports(
            [node["name"] for node in nodes if "name" in node],
            allow_skip=False,
        ))
    report_processor.report_list(target_report_list)

    # Use an address defined in known-hosts for each node with no addresses
    # specified. This allows users not to specify node addresses at all which
    # simplifies the whole cluster setup command / form significantly.
    addrs_defaulter = _get_addrs_defaulter(
        report_processor, {target.label: target
                           for target in target_list})
    nodes = [
        _set_defaults_in_dict(node, {"addrs": addrs_defaulter})
        for node in nodes
    ]

    # Validate inputs.
    report_processor.report_list(
        config_validators.create(cluster_name,
                                 nodes,
                                 transport_type,
                                 force_unresolvable=force_unresolvable))
    if transport_type in corosync_constants.TRANSPORTS_KNET:
        max_link_number = max([len(node["addrs"]) for node in nodes],
                              default=0)
        report_processor.report_list(
            config_validators.create_transport_knet(
                transport_options, compression_options, crypto_options) +
            config_validators.create_link_list_knet(link_list, max_link_number)
        )
    elif transport_type in corosync_constants.TRANSPORTS_UDP:
        report_processor.report_list(
            config_validators.create_transport_udp(
                transport_options, compression_options, crypto_options) +
            config_validators.create_link_list_udp(link_list))
    report_processor.report_list(
        config_validators.create_totem(totem_options) +
        # We are creating the config and we know there is no qdevice in it.
        config_validators.create_quorum_options(quorum_options, False))

    # Validate flags
    wait_timeout = _get_validated_wait_timeout(report_processor, wait, start)

    # Validate the nodes
    com_cmd = GetHostInfo(report_processor)
    com_cmd.set_targets(target_list)
    report_processor.report_list(
        _host_check_cluster_setup(
            run_com(env.get_node_communicator(), com_cmd), force))

    if report_processor.has_errors:
        raise LibraryError()

    # Validation done. If errors occured, an exception has been raised and we
    # don't get below this line.

    # Destroy cluster on all nodes.
    com_cmd = cluster.Destroy(env.report_processor)
    com_cmd.set_targets(target_list)
    run_and_raise(env.get_node_communicator(), com_cmd)

    # Distribute auth tokens.
    com_cmd = UpdateKnownHosts(
        env.report_processor,
        known_hosts_to_add=env.get_known_hosts(
            [target.label for target in target_list]),
        known_hosts_to_remove=[],
    )
    com_cmd.set_targets(target_list)
    run_and_raise(env.get_node_communicator(), com_cmd)

    # Distribute configuration files except corosync.conf. Sending
    # corosync.conf serves as a "commit" as its presence on a node marks the
    # node as a part of a cluster.
    corosync_authkey = generate_binary_key(random_bytes_count=128)
    pcmk_authkey = generate_binary_key(random_bytes_count=128)
    actions = {}
    actions.update(
        node_communication_format.corosync_authkey_file(corosync_authkey))
    actions.update(node_communication_format.pcmk_authkey_file(pcmk_authkey))
    com_cmd = DistributeFilesWithoutForces(env.report_processor, actions)
    com_cmd.set_targets(target_list)
    run_and_raise(env.get_node_communicator(), com_cmd)
    # TODO This should be in the previous call but so far we don't have a call
    # which allows to save and delete files at the same time.
    com_cmd = RemoveFilesWithoutForces(
        env.report_processor,
        {"pcsd settings": {
            "type": "pcsd_settings"
        }},
    )
    com_cmd.set_targets(target_list)
    run_and_raise(env.get_node_communicator(), com_cmd)

    # Distribute and reload pcsd SSL certificate
    report_processor.report(
        reports.pcsd_ssl_cert_and_key_distribution_started(
            [target.label for target in target_list]))
    ssl_key_raw = ssl.generate_key()
    ssl_key = ssl.dump_key(ssl_key_raw)
    ssl_cert = ssl.dump_cert(
        ssl.generate_cert(ssl_key_raw, target_list[0].label))
    com_cmd = SendPcsdSslCertAndKey(env.report_processor, ssl_cert, ssl_key)
    com_cmd.set_targets(target_list)
    run_and_raise(env.get_node_communicator(), com_cmd)

    # Create and distribute corosync.conf. Once a node saves corosync.conf it
    # is considered to be in a cluster.
    corosync_conf = config_facade.ConfigFacade.create(cluster_name, nodes,
                                                      transport_type)
    corosync_conf.set_totem_options(totem_options)
    corosync_conf.set_quorum_options(quorum_options)
    corosync_conf.create_link_list(link_list)
    if transport_type in corosync_constants.TRANSPORTS_KNET:
        corosync_conf.set_transport_knet_options(transport_options,
                                                 compression_options,
                                                 crypto_options)
    elif transport_type in corosync_constants.TRANSPORTS_UDP:
        corosync_conf.set_transport_udp_options(transport_options)

    com_cmd = DistributeFilesWithoutForces(
        env.report_processor,
        node_communication_format.corosync_conf_file(
            corosync_conf.config.export()),
    )
    com_cmd.set_targets(target_list)
    run_and_raise(env.get_node_communicator(), com_cmd)

    env.report_processor.process(reports.cluster_setup_success())

    # Optionally enable and start cluster services.
    if enable:
        com_cmd = EnableCluster(env.report_processor)
        com_cmd.set_targets(target_list)
        run_and_raise(env.get_node_communicator(), com_cmd)
    if start:
        _start_cluster(
            env.communicator_factory,
            env.report_processor,
            target_list,
            wait_timeout=wait_timeout,
        )