This source file includes following definitions.
- pcmk__rsc_agent_changed
- add_rsc_if_matching
- pcmk__rscs_matching_id
- set_assignment_methods_for_rsc
- pcmk__set_assignment_methods
- add_colocated_resources
- pcmk__colocated_resources
- pcmk__noop_add_graph_meta
- pcmk__output_resource_actions
- add_assigned_resource
- pcmk__assign_resource
- pcmk__unassign_resource
- pcmk__threshold_reached
- get_node_score
- cmp_resources
- pcmk__sort_resources
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdlib.h>
  13 #include <string.h>
  14 #include <crm/msg_xml.h>
  15 #include <pacemaker-internal.h>
  16 
  17 #include "libpacemaker_private.h"
  18 
  19 
  20 static pcmk_assignment_methods_t assignment_methods[] = {
  21     {
  22         pcmk__primitive_assign,
  23         pcmk__primitive_create_actions,
  24         pcmk__probe_rsc_on_node,
  25         pcmk__primitive_internal_constraints,
  26         pcmk__primitive_apply_coloc_score,
  27         pcmk__colocated_resources,
  28         pcmk__with_primitive_colocations,
  29         pcmk__primitive_with_colocations,
  30         pcmk__add_colocated_node_scores,
  31         pcmk__apply_location,
  32         pcmk__primitive_action_flags,
  33         pcmk__update_ordered_actions,
  34         pcmk__output_resource_actions,
  35         pcmk__add_rsc_actions_to_graph,
  36         pcmk__primitive_add_graph_meta,
  37         pcmk__primitive_add_utilization,
  38         pcmk__primitive_shutdown_lock,
  39     },
  40     {
  41         pcmk__group_assign,
  42         pcmk__group_create_actions,
  43         pcmk__probe_rsc_on_node,
  44         pcmk__group_internal_constraints,
  45         pcmk__group_apply_coloc_score,
  46         pcmk__group_colocated_resources,
  47         pcmk__with_group_colocations,
  48         pcmk__group_with_colocations,
  49         pcmk__group_add_colocated_node_scores,
  50         pcmk__group_apply_location,
  51         pcmk__group_action_flags,
  52         pcmk__group_update_ordered_actions,
  53         pcmk__output_resource_actions,
  54         pcmk__add_rsc_actions_to_graph,
  55         pcmk__noop_add_graph_meta,
  56         pcmk__group_add_utilization,
  57         pcmk__group_shutdown_lock,
  58     },
  59     {
  60         pcmk__clone_assign,
  61         pcmk__clone_create_actions,
  62         pcmk__clone_create_probe,
  63         pcmk__clone_internal_constraints,
  64         pcmk__clone_apply_coloc_score,
  65         pcmk__colocated_resources,
  66         pcmk__with_clone_colocations,
  67         pcmk__clone_with_colocations,
  68         pcmk__add_colocated_node_scores,
  69         pcmk__clone_apply_location,
  70         pcmk__clone_action_flags,
  71         pcmk__instance_update_ordered_actions,
  72         pcmk__output_resource_actions,
  73         pcmk__clone_add_actions_to_graph,
  74         pcmk__clone_add_graph_meta,
  75         pcmk__clone_add_utilization,
  76         pcmk__clone_shutdown_lock,
  77     },
  78     {
  79         pcmk__bundle_assign,
  80         pcmk__bundle_create_actions,
  81         pcmk__bundle_create_probe,
  82         pcmk__bundle_internal_constraints,
  83         pcmk__bundle_apply_coloc_score,
  84         pcmk__colocated_resources,
  85         pcmk__with_bundle_colocations,
  86         pcmk__bundle_with_colocations,
  87         pcmk__add_colocated_node_scores,
  88         pcmk__bundle_apply_location,
  89         pcmk__bundle_action_flags,
  90         pcmk__instance_update_ordered_actions,
  91         pcmk__output_bundle_actions,
  92         pcmk__bundle_add_actions_to_graph,
  93         pcmk__noop_add_graph_meta,
  94         pcmk__bundle_add_utilization,
  95         pcmk__bundle_shutdown_lock,
  96     }
  97 };
  98 
  99 
 100 
 101 
 102 
 103 
 104 
 105 
 106 
 107 
 108 
 109 
 110 bool
 111 pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node,
     
 112                         const xmlNode *rsc_entry, bool active_on_node)
 113 {
 114     bool changed = false;
 115     const char *attr_list[] = {
 116         XML_ATTR_TYPE,
 117         XML_AGENT_ATTR_CLASS,
 118         XML_AGENT_ATTR_PROVIDER
 119     };
 120 
 121     for (int i = 0; i < PCMK__NELEM(attr_list); i++) {
 122         const char *value = crm_element_value(rsc->xml, attr_list[i]);
 123         const char *old_value = crm_element_value(rsc_entry, attr_list[i]);
 124 
 125         if (!pcmk__str_eq(value, old_value, pcmk__str_none)) {
 126             changed = true;
 127             trigger_unfencing(rsc, node, "Device definition changed", NULL,
 128                               rsc->cluster);
 129             if (active_on_node) {
 130                 crm_notice("Forcing restart of %s on %s "
 131                            "because %s changed from '%s' to '%s'",
 132                            rsc->id, pe__node_name(node), attr_list[i],
 133                            pcmk__s(old_value, ""), pcmk__s(value, ""));
 134             }
 135         }
 136     }
 137     if (changed && active_on_node) {
 138         
 139         custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE,
 140                       rsc->cluster);
 141         pe__set_resource_flags(rsc, pcmk_rsc_start_pending);
 142     }
 143     return changed;
 144 }
 145 
 146 
 147 
 148 
 149 
 150 
 151 
 152 
 153 
 154 
 155 
 156 static GList *
 157 add_rsc_if_matching(GList *result, pcmk_resource_t *rsc, const char *id)
     
 158 {
 159     if ((strcmp(rsc->id, id) == 0)
 160         || ((rsc->clone_name != NULL) && (strcmp(rsc->clone_name, id) == 0))) {
 161         result = g_list_prepend(result, rsc);
 162     }
 163     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 164         pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
 165 
 166         result = add_rsc_if_matching(result, child, id);
 167     }
 168     return result;
 169 }
 170 
 171 
 172 
 173 
 174 
 175 
 176 
 177 
 178 
 179 
 180 
 181 
 182 GList *
 183 pcmk__rscs_matching_id(const char *id, const pcmk_scheduler_t *scheduler)
     
 184 {
 185     GList *result = NULL;
 186 
 187     CRM_CHECK((id != NULL) && (scheduler != NULL), return NULL);
 188     for (GList *iter = scheduler->resources; iter != NULL; iter = iter->next) {
 189         result = add_rsc_if_matching(result, (pcmk_resource_t *) iter->data,
 190                                      id);
 191     }
 192     return result;
 193 }
 194 
 195 
 196 
 197 
 198 
 199 
 200 
 201 
 202 static void
 203 set_assignment_methods_for_rsc(gpointer data, gpointer user_data)
     
 204 {
 205     pcmk_resource_t *rsc = data;
 206 
 207     rsc->cmds = &assignment_methods[rsc->variant];
 208     g_list_foreach(rsc->children, set_assignment_methods_for_rsc, NULL);
 209 }
 210 
 211 
 212 
 213 
 214 
 215 
 216 
 217 void
 218 pcmk__set_assignment_methods(pcmk_scheduler_t *scheduler)
     
 219 {
 220     g_list_foreach(scheduler->resources, set_assignment_methods_for_rsc, NULL);
 221 }
 222 
 223 
 224 
 225 
 226 
 227 
 228 
 229 
 230 
 231 
 232 
 233 static inline void
 234 add_colocated_resources(const pcmk_resource_t *rsc,
     
 235                         const pcmk_resource_t *orig_rsc, GList **list)
 236 {
 237     *list = rsc->cmds->colocated_resources(rsc, orig_rsc, *list);
 238 }
 239 
 240 
 241 GList *
 242 pcmk__colocated_resources(const pcmk_resource_t *rsc,
     
 243                           const pcmk_resource_t *orig_rsc,
 244                           GList *colocated_rscs)
 245 {
 246     const GList *iter = NULL;
 247     GList *colocations = NULL;
 248 
 249     if (orig_rsc == NULL) {
 250         orig_rsc = rsc;
 251     }
 252 
 253     if ((rsc == NULL) || (g_list_find(colocated_rscs, rsc) != NULL)) {
 254         return colocated_rscs;
 255     }
 256 
 257     pe_rsc_trace(orig_rsc, "%s is in colocation chain with %s",
 258                  rsc->id, orig_rsc->id);
 259     colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc);
 260 
 261     
 262     colocations = pcmk__this_with_colocations(rsc);
 263     for (iter = colocations; iter != NULL; iter = iter->next) {
 264         const pcmk__colocation_t *constraint = iter->data;
 265         const pcmk_resource_t *primary = constraint->primary;
 266 
 267         if (primary == orig_rsc) {
 268             continue; 
 269         }
 270 
 271         if ((constraint->score == INFINITY) &&
 272             (pcmk__colocation_affects(rsc, primary, constraint,
 273                                       true) == pcmk__coloc_affects_location)) {
 274             add_colocated_resources(primary, orig_rsc, &colocated_rscs);
 275         }
 276     }
 277     g_list_free(colocations);
 278 
 279     
 280     colocations = pcmk__with_this_colocations(rsc);
 281     for (iter = colocations; iter != NULL; iter = iter->next) {
 282         const pcmk__colocation_t *constraint = iter->data;
 283         const pcmk_resource_t *dependent = constraint->dependent;
 284 
 285         if (dependent == orig_rsc) {
 286             continue; 
 287         }
 288 
 289         if (pe_rsc_is_clone(rsc) && !pe_rsc_is_clone(dependent)) {
 290             continue; 
 291         }
 292 
 293         if ((constraint->score == INFINITY) &&
 294             (pcmk__colocation_affects(dependent, rsc, constraint,
 295                                       true) == pcmk__coloc_affects_location)) {
 296             add_colocated_resources(dependent, orig_rsc, &colocated_rscs);
 297         }
 298     }
 299     g_list_free(colocations);
 300 
 301     return colocated_rscs;
 302 }
 303 
 304 
 305 void
 306 pcmk__noop_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml)
     
 307 {
 308 }
 309 
 310 
 311 
 312 
 313 
 314 
 315 
 316 void
 317 pcmk__output_resource_actions(pcmk_resource_t *rsc)
     
 318 {
 319     pcmk_node_t *next = NULL;
 320     pcmk_node_t *current = NULL;
 321     pcmk__output_t *out = NULL;
 322 
 323     CRM_ASSERT(rsc != NULL);
 324 
 325     out = rsc->cluster->priv;
 326     if (rsc->children != NULL) {
 327         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 328             pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
 329 
 330             child->cmds->output_actions(child);
 331         }
 332         return;
 333     }
 334 
 335     next = rsc->allocated_to;
 336     if (rsc->running_on) {
 337         current = pe__current_node(rsc);
 338         if (rsc->role == pcmk_role_stopped) {
 339             
 340 
 341 
 342             rsc->role = pcmk_role_started;
 343         }
 344     }
 345 
 346     if ((current == NULL) && pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
 347         
 348         return;
 349     }
 350 
 351     out->message(out, "rsc-action", rsc, current, next);
 352 }
 353 
 354 
 355 
 356 
 357 
 358 
 359 
 360 
 361 static inline void
 362 add_assigned_resource(pcmk_node_t *node, pcmk_resource_t *rsc)
     
 363 {
 364     node->details->allocated_rsc = g_list_prepend(node->details->allocated_rsc,
 365                                                   rsc);
 366 }
 367 
 368 
 369 
 370 
 371 
 372 
 373 
 374 
 375 
 376 
 377 
 378 
 379 
 380 
 381 
 382 
 383 
 384 
 385 
 386 
 387 
 388 
 389 
 390 
 391 
 392 
 393 
 394 
 395 
 396 
 397 
 398 
 399 
 400 
 401 
 402 
 403 bool
 404 pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force,
     
 405                       bool stop_if_fail)
 406 {
 407     bool changed = false;
 408 
 409     CRM_ASSERT(rsc != NULL);
 410 
 411     if (rsc->children != NULL) {
 412         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 413             pcmk_resource_t *child_rsc = iter->data;
 414 
 415             changed |= pcmk__assign_resource(child_rsc, node, force,
 416                                              stop_if_fail);
 417         }
 418         return changed;
 419     }
 420 
 421     
 422 
 423     if (!force && (node != NULL)
 424         && ((node->weight < 0)
 425             
 426             || (!pcmk__node_available(node, true, false)
 427                 && !pe__is_guest_node(node)))) {
 428 
 429         pe_rsc_debug(rsc,
 430                      "All nodes for resource %s are unavailable, unclean or "
 431                      "shutting down (%s can%s run resources, with score %s)",
 432                      rsc->id, pe__node_name(node),
 433                      (pcmk__node_available(node, true, false)? "" : "not"),
 434                      pcmk_readable_score(node->weight));
 435 
 436         if (stop_if_fail) {
 437             pe__set_next_role(rsc, pcmk_role_stopped, "node availability");
 438         }
 439         node = NULL;
 440     }
 441 
 442     if (rsc->allocated_to != NULL) {
 443         changed = !pe__same_node(rsc->allocated_to, node);
 444     } else {
 445         changed = (node != NULL);
 446     }
 447     pcmk__unassign_resource(rsc);
 448     pe__clear_resource_flags(rsc, pcmk_rsc_unassigned);
 449 
 450     if (node == NULL) {
 451         char *rc_stopped = NULL;
 452 
 453         pe_rsc_debug(rsc, "Could not assign %s to a node", rsc->id);
 454 
 455         if (!stop_if_fail) {
 456             return changed;
 457         }
 458         pe__set_next_role(rsc, pcmk_role_stopped, "unable to assign");
 459 
 460         for (GList *iter = rsc->actions; iter != NULL; iter = iter->next) {
 461             pcmk_action_t *op = (pcmk_action_t *) iter->data;
 462 
 463             pe_rsc_debug(rsc, "Updating %s for %s assignment failure",
 464                          op->uuid, rsc->id);
 465 
 466             if (pcmk__str_eq(op->task, PCMK_ACTION_STOP, pcmk__str_none)) {
 467                 pe__clear_action_flags(op, pcmk_action_optional);
 468 
 469             } else if (pcmk__str_eq(op->task, PCMK_ACTION_START,
 470                                     pcmk__str_none)) {
 471                 pe__clear_action_flags(op, pcmk_action_runnable);
 472 
 473             } else {
 474                 
 475                 const char *interval_ms_s = NULL;
 476                 const char *target_rc_s = NULL;
 477 
 478                 interval_ms_s = g_hash_table_lookup(op->meta,
 479                                                     XML_LRM_ATTR_INTERVAL_MS);
 480                 target_rc_s = g_hash_table_lookup(op->meta,
 481                                                   XML_ATTR_TE_TARGET_RC);
 482                 if (rc_stopped == NULL) {
 483                     rc_stopped = pcmk__itoa(PCMK_OCF_NOT_RUNNING);
 484                 }
 485 
 486                 if (!pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)
 487                     && !pcmk__str_eq(rc_stopped, target_rc_s, pcmk__str_none)) {
 488 
 489                     pe__clear_action_flags(op, pcmk_action_runnable);
 490                 }
 491             }
 492         }
 493         free(rc_stopped);
 494         return changed;
 495     }
 496 
 497     pe_rsc_debug(rsc, "Assigning %s to %s", rsc->id, pe__node_name(node));
 498     rsc->allocated_to = pe__copy_node(node);
 499 
 500     add_assigned_resource(node, rsc);
 501     node->details->num_resources++;
 502     node->count++;
 503     pcmk__consume_node_capacity(node->details->utilization, rsc);
 504 
 505     if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_show_utilization)) {
 506         pcmk__output_t *out = rsc->cluster->priv;
 507 
 508         out->message(out, "resource-util", rsc, node, __func__);
 509     }
 510     return changed;
 511 }
 512 
 513 
 514 
 515 
 516 
 517 
 518 
 519 
 520 
 521 
 522 
 523 
 524 void
 525 pcmk__unassign_resource(pcmk_resource_t *rsc)
     
 526 {
 527     pcmk_node_t *old = rsc->allocated_to;
 528 
 529     if (old == NULL) {
 530         crm_info("Unassigning %s", rsc->id);
 531     } else {
 532         crm_info("Unassigning %s from %s", rsc->id, pe__node_name(old));
 533     }
 534 
 535     pe__set_resource_flags(rsc, pcmk_rsc_unassigned);
 536 
 537     if (rsc->children == NULL) {
 538         if (old == NULL) {
 539             return;
 540         }
 541         rsc->allocated_to = NULL;
 542 
 543         
 544 
 545 
 546         old->details->allocated_rsc = g_list_remove(old->details->allocated_rsc,
 547                                                     rsc);
 548         old->details->num_resources--;
 549         pcmk__release_node_capacity(old->details->utilization, rsc);
 550         free(old);
 551         return;
 552     }
 553 
 554     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 555         pcmk__unassign_resource((pcmk_resource_t *) iter->data);
 556     }
 557 }
 558 
 559 
 560 
 561 
 562 
 563 
 564 
 565 
 566 
 567 
 568 
 569 
 570 bool
 571 pcmk__threshold_reached(pcmk_resource_t *rsc, const pcmk_node_t *node,
     
 572                         pcmk_resource_t **failed)
 573 {
 574     int fail_count, remaining_tries;
 575     pcmk_resource_t *rsc_to_ban = rsc;
 576 
 577     
 578     if (rsc->migration_threshold == 0) {
 579         return false;
 580     }
 581 
 582     
 583     if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) {
 584         return false;
 585     }
 586 
 587     
 588     fail_count = pe_get_failcount(node, rsc, NULL,
 589                                   pcmk__fc_effective|pcmk__fc_fillers, NULL);
 590     if (fail_count <= 0) {
 591         return false;
 592     }
 593 
 594     
 595     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
 596         rsc_to_ban = uber_parent(rsc);
 597     }
 598 
 599     
 600     remaining_tries = rsc->migration_threshold - fail_count;
 601 
 602     if (remaining_tries <= 0) {
 603         crm_warn("%s cannot run on %s due to reaching migration threshold "
 604                  "(clean up resource to allow again)"
 605                  CRM_XS " failures=%d migration-threshold=%d",
 606                  rsc_to_ban->id, pe__node_name(node), fail_count,
 607                  rsc->migration_threshold);
 608         if (failed != NULL) {
 609             *failed = rsc_to_ban;
 610         }
 611         return true;
 612     }
 613 
 614     crm_info("%s can fail %d more time%s on "
 615              "%s before reaching migration threshold (%d)",
 616              rsc_to_ban->id, remaining_tries, pcmk__plural_s(remaining_tries),
 617              pe__node_name(node), rsc->migration_threshold);
 618     return false;
 619 }
 620 
 621 
 622 
 623 
 624 
 625 
 626 
 627 
 628 
 629 
 630 static int
 631 get_node_score(const pcmk_node_t *node, GHashTable *nodes)
     
 632 {
 633     pcmk_node_t *found_node = NULL;
 634 
 635     if ((node != NULL) && (nodes != NULL)) {
 636         found_node = g_hash_table_lookup(nodes, node->details->id);
 637     }
 638     return (found_node == NULL)? -INFINITY : found_node->weight;
 639 }
 640 
 641 
 642 
 643 
 644 
 645 
 646 
 647 
 648 
 649 
 650 
 651 
 652 static gint
 653 cmp_resources(gconstpointer a, gconstpointer b, gpointer data)
     
 654 {
 655     
 656 
 657 
 658 
 659     pcmk_resource_t *resource1 = (pcmk_resource_t *) a;
 660     pcmk_resource_t *resource2 = (pcmk_resource_t *) b;
 661     const GList *nodes = data;
 662 
 663     int rc = 0;
 664     int r1_score = -INFINITY;
 665     int r2_score = -INFINITY;
 666     pcmk_node_t *r1_node = NULL;
 667     pcmk_node_t *r2_node = NULL;
 668     GHashTable *r1_nodes = NULL;
 669     GHashTable *r2_nodes = NULL;
 670     const char *reason = NULL;
 671 
 672     
 673     reason = "priority";
 674     r1_score = resource1->priority;
 675     r2_score = resource2->priority;
 676     if (r1_score > r2_score) {
 677         rc = -1;
 678         goto done;
 679     }
 680     if (r1_score < r2_score) {
 681         rc = 1;
 682         goto done;
 683     }
 684 
 685     
 686     reason = "no node list";
 687     if (nodes == NULL) {
 688         goto done;
 689     }
 690 
 691     
 692     resource1->cmds->add_colocated_node_scores(resource1, NULL, resource1->id,
 693                                                &r1_nodes, NULL, 1,
 694                                                pcmk__coloc_select_this_with);
 695     resource2->cmds->add_colocated_node_scores(resource2, NULL, resource2->id,
 696                                                &r2_nodes, NULL, 1,
 697                                                pcmk__coloc_select_this_with);
 698     pe__show_node_scores(true, NULL, resource1->id, r1_nodes,
 699                          resource1->cluster);
 700     pe__show_node_scores(true, NULL, resource2->id, r2_nodes,
 701                          resource2->cluster);
 702 
 703     
 704     reason = "current location";
 705     if (resource1->running_on != NULL) {
 706         r1_node = pe__current_node(resource1);
 707     }
 708     if (resource2->running_on != NULL) {
 709         r2_node = pe__current_node(resource2);
 710     }
 711     r1_score = get_node_score(r1_node, r1_nodes);
 712     r2_score = get_node_score(r2_node, r2_nodes);
 713     if (r1_score > r2_score) {
 714         rc = -1;
 715         goto done;
 716     }
 717     if (r1_score < r2_score) {
 718         rc = 1;
 719         goto done;
 720     }
 721 
 722     
 723     reason = "score";
 724     for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
 725         const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
 726 
 727         r1_score = get_node_score(node, r1_nodes);
 728         r2_score = get_node_score(node, r2_nodes);
 729         if (r1_score > r2_score) {
 730             rc = -1;
 731             goto done;
 732         }
 733         if (r1_score < r2_score) {
 734             rc = 1;
 735             goto done;
 736         }
 737     }
 738 
 739 done:
 740     crm_trace("%s (%d)%s%s %c %s (%d)%s%s: %s",
 741               resource1->id, r1_score,
 742               ((r1_node == NULL)? "" : " on "),
 743               ((r1_node == NULL)? "" : r1_node->details->id),
 744               ((rc < 0)? '>' : ((rc > 0)? '<' : '=')),
 745               resource2->id, r2_score,
 746               ((r2_node == NULL)? "" : " on "),
 747               ((r2_node == NULL)? "" : r2_node->details->id),
 748               reason);
 749     if (r1_nodes != NULL) {
 750         g_hash_table_destroy(r1_nodes);
 751     }
 752     if (r2_nodes != NULL) {
 753         g_hash_table_destroy(r2_nodes);
 754     }
 755     return rc;
 756 }
 757 
 758 
 759 
 760 
 761 
 762 
 763 
 764 void
 765 pcmk__sort_resources(pcmk_scheduler_t *scheduler)
     
 766 {
 767     GList *nodes = g_list_copy(scheduler->nodes);
 768 
 769     nodes = pcmk__sort_nodes(nodes, NULL);
 770     scheduler->resources = g_list_sort_with_data(scheduler->resources,
 771                                                  cmp_resources, nodes);
 772     g_list_free(nodes);
 773 }