RightScale¶
Documentation¶
kingpin.actors.rightscale.base
¶
The RightScale Actors allow you to interact with resources inside your
Rightscale account. These actors all support dry runs properly, but each
actor has its own caveats with dry=True
. Please read the instructions
below for using each actor.
Required Environment Variables
RIGHTSCALE_TOKEN: | |
---|---|
RightScale API Refresh Token (from the Account Settings/API Credentials page) | |
RIGHTSCALE_ENDPOINT: | |
Your account-specific API Endpoint (defaults to https://my.rightscale.com) |
-
class
kingpin.actors.rightscale.base.
EnsurableRightScaleBaseActor
(*args, **kwargs)[source] Hacky way to re-use the RightScaleBaseActor but make it ensurable.
Deployment¶
kingpin.actors.rightscale.deployment
¶
-
class
kingpin.actors.rightscale.deployment.
Create
(*args, **kwargs)[source] Creates a RightScale deployment.
Options match the documentation in RightScale: http://reference.rightscale.com/api1.5/resources/ResourceDeployments.html
Options
Name: The name of the deployment to be created. Description: The description of the deployment to be created. (optional) Server_tag_scope: The routing scope for tags for servers in the deployment. Can be ‘deployment’ or ‘account’ (optional, default: deployment)
-
class
kingpin.actors.rightscale.deployment.
Destroy
(*args, **kwargs)[source] Deletes a RightScale deployment.
Options match the documentation in RightScale: http://reference.rightscale.com/api1.5/resources/ResourceDeployments.html
Options
Name: The name of the deployment to be deleted.
Alert Specs¶
kingpin.actors.rightscale.alerts
¶
-
class
kingpin.actors.rightscale.alerts.
Create
(*args, **kwargs)[source] Create a RightScale Alert Spec
Options match the documentation in RightScale: http://reference.rightscale.com/api1.5/resources/ResourceAlertSpecs.html#create
Options
Array: The name of the Server or ServerArray to create the AlertSpec on. Strict_array: Whether or not to fail if the Server/ServerArray does not exist. (default: False) Condition: The condition (operator) in the condition sentence. ( >, >=, <, <=, ==, !=
)Description: The description of the AlertSpec. (optional) Duration: The duration in minutes of the condition sentence. ( ^d+$
)Escalation_name: Escalate to the named alert escalation when the alert is triggered. (optional) File: The RRD path/file_name of the condition sentence. Name: The name of the AlertSpec. Threshold: The threshold of the condition sentence. Variable: The RRD variable of the condition sentence Vote_tag: Should correspond to a vote tag on a ServerArray if vote to grow or shrink. Vote_type: Vote to grow or shrink a ServerArray when the alert is triggered. Must either escalate or vote. ( grow
orshrink
)Examples
Create a high network activity alert on my-array:
{ "desc": "Create high network rx alert", "actor": "rightscale.alerts.Create", "options": { "array": "my-array", "strict_array": true, "condition": ">", "description": "Alert if amount of network data received is high", "duration": 180, "escalation_name": "Email Engineering", "file": "interface/if_octets-eth0", "name": "high network rx activity", "threshold": "50000000", "variable": "rx" } }
Dry Mode
In Dry mode this actor does validate that the
array
array exists. If it does not, akingpin.actors.rightscale.api.ServerArrayException
is thrown. Once that has been validated, the dry mode execution simply logs the Alert Spec that it would have created.Example dry output:
TODO: Fill this in
-
class
kingpin.actors.rightscale.alerts.
Destroy
(*args, **kwargs)[source] Destroy existing RightScale Alert Specs
This actor searches RightScale for any Alert Specs that match the
name
andarray
that you supplied, then deletes all of them. RightScale lets you have multiple alert specs with the same name, so if this actor finds multiple specs, it will delete them all.Options
Array: The name of the Server or ServerArray to delete the AlertSpec from. Name: The name of the AlertSpec. Examples
Destroy a high network activity alert on my-array:
{ "desc": "Destroy high network rx alert", "actor": "rightscale.alerts.Destroy", "options": { "array": "my-array", "name": "high network rx activity", } }
Dry Mode
In Dry mode this actor does validate that the
array
array exists, and that the AlertSpec exists on that array so that it can be deleted. A RecoverableActorFailure error is thrown if it does not exist.Example dry output:
14:31:49 INFO Rehearsing... Break a leg! 14:31:49 INFO [DRY: Kingpin] Preparing actors from delete.json 14:31:53 INFO [DRY: Destroy high network rx alert] Found my-array (/api/server_arrays/329142003) to delete alert spec from 14:31:54 INFO [DRY: Destroy high network rx alert] Would have deleted the alert spec "high network rx activity" on my-array
-
class
kingpin.actors.rightscale.alerts.
AlertSpecSchema
[source] Provides JSON-Schema based verification of the supplied AlertSpec
- The majority of the schema mirrors the RightScale API:
- http://reference.rightscale.com/api1.5/resources/ResourceAlertSpecs.html#create
-
class
kingpin.actors.rightscale.alerts.
AlertSpecsSchema
[source] Provides JSON-Schema verification that the supplied input was a list of AlertSpecSchemas.
-
class
kingpin.actors.rightscale.alerts.
AlertSpecBase
(*args, **kwargs)[source] Extremely simple AlertSpec creation actor.
This actor isn’t really meant to be instantiated on its own – it provides the base functionality though for creating, deleting and updating an AlertSpec on a given RightScale resource. The resource can be either a Server Array, Server Template, Instance or Deployment.
Options
Href: The RightScale HREF for the resource you wish to apply the Alert Spec to. State: (str) Either present
orabsent
Spec: A dictionary that conforms to the AlertSpecSchema
.Examples
{ "actor": "rightscale.alerts.AlertSpecBase", "options": { "href": "/api/server_arrays/abcd1234", "spec": { "name": "Instance Stranded", "description": "Alert if an instance enders a stranded", "file": "RS/server-failure", "variable": "state", "condition": "==", "threshold": "stranded", "duration": 2, "escalation_name": "critical" } } }
-
class
kingpin.actors.rightscale.alerts.
AlertSpecsBase
(*args, **kwargs)[source] Extremely simple AlertSpec management actor.
This actor isn’t really meant to be instantiated on its own – it provides the base functionality though for creating, deleting and updating an AlertSpec on a given RightScale resource. The subtle difference here is that this actor manages the entire list of AlertSpecs on a given resource, rather than just a single spec.
Options
Href: The RightScale HREF for the resource you wish to apply the Alert Spec to. State: (str) Either present
orabsent
Spec: A dictionary that conforms to the AlertSpecSchema
.Examples
{ "actor": "rightscale.alerts.AlertSpecsBase", "options": { "href": "/api/server_arrays/abcd1234", "specs": [ { "name": "Instance Stranded", "description": "Alert if an instance enders a stranded", "file": "RS/server-failure", "variable": "state", "condition": "==", "threshold": "stranded", "duration": 2, "escalation_name": "critical" } ] } }
Server Arrays¶
kingpin.actors.rightscale.server_array
¶
-
class
kingpin.actors.rightscale.server_array.
Clone
(*args, **kwargs)[source] Clones a RightScale Server Array.
Clones a ServerArray in RightScale and renames it to the newly supplied name. By default, this actor is extremely strict about validating that the
source
array already exists, and that thedest
array does not yet exist. This behavior can be overridden though if your Kingpin script creates thesource
, or destroys an existingdest
ServerArray sometime before this actor executes.Options
Source: The name of the ServerArray to clone Strict_source: Whether or not to fail if the source ServerArray does not exist. (default: True) Dest: The new name for your cloned ServerArray Strict_dest: Whether or not to fail if the destination ServerArray already exists. (default: True) Examples
Clone my-template-array to my-new-array:
{ "desc": "Clone my array", "actor": "rightscale.server_array.Clone", "options": { "source": "my-template-array", "dest": "my-new-array" } }
Clone an array that was created sometime earlier in the Kingpin JSON, and thus does not exist yet during the dry run:
{ "desc": "Clone that array we created earlier", "actor": "rightscale.server_array.Clone", "options": { "source": "my-template-array", "strict_source": false, "dest": "my-new-array" } }
Clone an array into a destination name that was destroyed sometime earlier in the Kingpin JSON:
{ "desc": "Clone that array we created earlier", "actor": "rightscale.server_array.Clone", "options": { "source": "my-template-array", "dest": "my-new-array", "strict_dest": false, } }
Dry Mode
In Dry mode this actor does validate that the
source
array exists. If it does not, akingpin.actors.rightscale.api.ServerArrayException
is thrown. Once that has been validated, the dry mode execution pretends to copy the array by creating a mocked cloned array resource. This mocked resource is then operated on during the rest of the execution of the actor, guaranteeing that no live resources are modified.Example dry output:
[Copy Test (DRY Mode)] Verifying that array "temp" exists [Copy Test (DRY Mode)] Verifying that array "new" does not exist [Copy Test (DRY Mode)] Cloning array "temp" [Copy Test (DRY Mode)] Renaming array "<mocked clone of temp>" to "new"
-
class
kingpin.actors.rightscale.server_array.
Update
(*args, **kwargs)[source] Update ServerArray Settings
Updates an existing ServerArray in RightScale with the supplied parameters. Can update any parameter that is described in the RightScale API docs here:
Parameters are passed into the actor in the form of a dictionary, and are then converted into the RightScale format. See below for examples.
Options
Array: (str) The name of the ServerArray to update Exact: (bool) whether or not to search for the exact array name. (default: true
)Params: (dict) Dictionary of parameters to update Inputs: (dict) Dictionary of next-instance server arryay inputs to update Examples
{ "desc": "Update my array", "actor": "rightscale.server_array.Update", "options": { "array": "my-new-array", "params": { "elasticity_params": { "bounds": { "min_count": 4 }, "schedule": [ {"day": "Sunday", "max_count": 2, "min_count": 1, "time": "07:00" }, {"day": "Sunday", "max_count": 2, "min_count": 2, "time": "09:00" } ] }, "name": "my-really-new-name" } } }
{ "desc": "Update my array inputs", "actor": "rightscale.server_array.Update", "options": { "array": "my-new-array", "inputs": { "ELB_NAME": "text:foobar" } } }
Dry Mode
In Dry mode this actor does search for the
array
, but allows it to be missing because its highly likely that the array does not exist yet. If the array does not exist, a mocked array object is created for the rest of the execution.During the rest of the execution, the code bypasses making any real changes and just tells you what changes it would have made.
This means that the dry mode cannot validate that the supplied inputs will work.
Example dry output:
[Update Test (DRY Mode)] Verifying that array "new" exists [Update Test (DRY Mode)] Array "new" not found -- creating a mock. [Update Test (DRY Mode)] Would have updated "<mocked array new>" with params: {'server_array[name]': 'my-really-new-name', 'server_array[elasticity_params][bounds][min_count]': '4'}
-
class
kingpin.actors.rightscale.server_array.
UpdateNextInstance
(*args, **kwargs)[source] Update the Next Instance parameters for a Server Array
Updates an existing ServerArray in RightScale with the supplied parameters. Can update any parameter that is described in the RightScale ResourceInstances docs.
Note about the image_href parameter
If you pass in the string
default
to theimage_href
key in yourparams
dictionary, we will search and find the default image that your ServerArray’s Multi Cloud Image refers to. This helper is useful if you update your ServerArrays to use custom AMIs, and then occasionally want to go back to using a stock AMI. For example, if you boot up your instances occasionally off a stock AMI, customize the host, and then bake that host into a custom AMI.Parameters are passed into the actor in the form of a dictionary, and are then converted into the RightScale format. See below for examples.
Options
Array: (str) The name of the ServerArray to update Exact: (bool) whether or not to search for the exact array name. (default: true
)Params: (dict) Dictionary of parameters to update Examples
{ "desc": "Update my array", "actor": "rightscale.server_array.UpdateNextInstance", "options": { "array": "my-new-array", "params": { "associate_public_ip_address": true, "image_href": "/image/href/123", } } }
{ "desc": "Reset the AMI image to the MCI default", "actor": "rightscale.server_array.UpdateNextInstance", "options": { "array": "my-new-array", "params": { "image_href": "default", } } }
Dry Mode
In Dry mode this actor does search for the
array
, but allows it to be missing because its highly likely that the array does not exist yet. If the array does not exist, a mocked array object is created for the rest of the execution.During the rest of the execution, the code bypasses making any real changes and just tells you what changes it would have made.
This means that the dry mode cannot validate that the supplied params will work.
Example dry output:
[Update my array (DRY Mode)] Verifying that array "new" exists [Update my array (DRY Mode)] Array "new" not found -- creating a mock. [Update my array (DRY Mode)] Would have updated "<mocked array new>" with params: {'server_array[associate_public_ip_address]': true, 'server_array[image_href]': '/image/href/'}
-
class
kingpin.actors.rightscale.server_array.
Terminate
(*args, **kwargs)[source] Terminate all instances in a ServerArray
Terminates all instances for a ServerArray in RightScale marking the array disabled.
Options
Array: (str) The name of the ServerArray to destroy Exact: (bool) Whether or not to search for the exact array name. (default: true
)Strict: (bool) Whether or not to fail if the ServerArray does not exist. (default: true
)Examples
{ "desc": "Terminate my array", "actor": "rightscale.server_array.Terminate", "options": { "array": "my-array" } }
{ "desc": "Terminate many arrays", "actor": "rightscale.server_array.Terminate", "options": { "array": "array-prefix", "exact": false, } }
Dry Mode
Dry mode still validates that the server array you want to terminate is actually gone. If you want to bypass this check, then set the
warn_on_failure
flag for the actor.
-
class
kingpin.actors.rightscale.server_array.
Destroy
(*args, **kwargs)[source] Destroy a ServerArray in RightScale
Destroys a ServerArray in RightScale by first invoking the Terminate actor, and then deleting the array as soon as all of the running instances have been terminated.
Options
Array: (str) The name of the ServerArray to destroy Exact: (bool) Whether or not to search for the exact array name. (default: true
)Strict: (bool) Whether or not to fail if the ServerArray does not exist. (default: true
)Examples
{ "desc": "Destroy my array", "actor": "rightscale.server_array.Destroy", "options": { "array": "my-array" } }
{ "desc": "Destroy many arrays", "actor": "rightscale.server_array.Destroy", "options": { "array": "array-prefix", "exact": false, } }
Dry Mode
In Dry mode this actor does search for the
array
, but allows it to be missing because its highly likely that the array does not exist yet. If the array does not exist, a mocked array object is created for the rest of the execution.During the rest of the execution, the code bypasses making any real changes and just tells you what changes it would have made.
Example dry output:
[Destroy Test (DRY Mode)] Beginning [Destroy Test (DRY Mode)] Terminating array before destroying it. [Destroy Test (terminate) (DRY Mode)] Array "my-array" not found -- creating a mock. [Destroy Test (terminate) (DRY Mode)] Disabling Array "my-array" [Destroy Test (terminate) (DRY Mode)] Would have terminated all array "<mocked array my-array>" instances. [Destroy Test (terminate) (DRY Mode)] Pretending that array <mocked array my-array> instances are terminated. [Destroy Test (DRY Mode)] Pretending to destroy array "<mocked array my-array>" [Destroy Test (DRY Mode)] Finished successfully. Result: True
-
class
kingpin.actors.rightscale.server_array.
Launch
(*args, **kwargs)[source] Launch instances in a ServerArray
Launches instances in an existing ServerArray and waits until that array has become healthy before returning. Healthy means that the array has at least the user-specified
count
ormax_count
number of instances running as defined by the array definition in RightScale.Options
Array: (str) The name of the ServerArray to launch Count: (str, int) Optional number of instance to launch. Defaults to max_count of the array. Success_pct’: (str, int) Optional percent (0-100) to wait for instances to launch before exiting this actor as successful. Default: 100. Enable: (bool) Should the autoscaling of the array be enabled? Settings this to false
, or omitting the parameter will not disable an enabled array.Exact: (bool) Whether or not to search for the exact array name. (default: true
)Examples
{ "desc": "Enable array and launch it", "actor": "rightscale.server_array.Launch", "options": { "array": "my-array", "enable": true } }
{ "desc": "Enable arrays starting with my-array and launch them", "actor": "rightscale.server_array.Launch", "options": { "array": "my-array", "enable": true, "exact": false } }
{ "desc": "Enable array and launch 1 instance", "actor": "rightscale.server_array.Launch", "options": { "array": "my-array", "count": 1 } }
Dry Mode
In Dry mode this actor does search for the
array
, but allows it to be missing because its highly likely that the array does not exist yet. If the array does not exist, a mocked array object is created for the rest of the execution.During the rest of the execution, the code bypasses making any real changes and just tells you what changes it would have made.
Example dry output:
[Launch Array Test #0 (DRY Mode)] Verifying that array "my-array" exists [Launch Array Test #0 (DRY Mode)] Array "my-array" not found -- creating a mock. [Launch Array Test #0 (DRY Mode)] Enabling Array "my-array" [Launch Array Test #0 (DRY Mode)] Launching Array "my-array" instances [Launch Array Test #0 (DRY Mode)] Would have launched instances of array <MagicMock name='my-array.self.show().soul.__getitem__()' id='4420453200'> [Launch Array Test #0 (DRY Mode)] Pretending that array <MagicMock name='my-array.self.show().soul.__getitem__()' id='4420453200'> instances are launched.
-
class
kingpin.actors.rightscale.server_array.
Execute
(*args, **kwargs)[source] Executes a RightScale script/recipe on a ServerArray
Executes a RightScript or Recipe on a set of hosts in a ServerArray in RightScale using individual calls to the live running instances. These can be found in your RightScale account under Design -> RightScript or Design -> Cookbooks
The RightScale API offers a multi_run_executable method that can be used to run a single script on all servers in an array – but unfortunately this API method provides no way to monitor the progress of the individual jobs on the hosts. Furthermore, the method often executes on recently terminated or terminating hosts, which throws false-negative error results.
Our actor explicitly retrieves a list of the operational hosts in an array and kicks off individual execution tasks for every host. It then tracks the execution of those tasks from start to finish and returns the results.
Options
Array: (str) The name of the ServerArray to operate on Script: (str) The name of the RightScript or Recipe to execute Expected_runtime: (str, int) Expected number of seconds to execute. (default: 5
)Concurrency: Max number of concurrent executions. This will fire off N executions in parallel, and continue with the remained as soon as the first execution is done. This is faster than creating N Sync executions. Note: When applied to multiple (M) arrays cumulative concurrency accross all arrays will remain at N. It will not be M x N. Inputs: (dict) Dictionary of Key/Value pairs to use as inputs for the script Exact: (str) Boolean whether or not to search for the exact array name. (default: true
)Examples
{ "desc":" Execute script on my-array", "actor": "rightscale.server_array.Execute", "options": { "array": "my-array", "script": "connect to elb", "expected_runtime": 3, "inputs": { "ELB_NAME": "text:my-elb" } } }
Dry Mode
In Dry mode this actor does search for the
array
, but allows it to be missing because its highly likely that the array does not exist yet. If the array does not exist, a mocked array object is created for the rest of the execution.During the rest of the execution, the code bypasses making any real changes and just tells you what changes it would have made.
Example dry output:
[Destroy Test (DRY Mode)] Verifying that array "my-array" exists [Execute Test (DRY Mode)] kingpin.actors.rightscale.server_array.Execute Initialized [Execute Test (DRY Mode)] Beginning execution [Execute Test (DRY Mode)] Verifying that array "my-array" exists [Execute Test (DRY Mode)] Would have executed "Connect instance to ELB" with inputs "{'inputs[ELB_NAME]': 'text:my-elb'}" on "my-array". [Execute Test (DRY Mode)] Returning result: True
Multi Cloud Images¶
-
class
kingpin.actors.rightscale.
MCI
(*args, **kwargs)[source] Manages the state of an RightScale MultiCloudImage.
This actor is able to create, destroy, modify and commit MultiCloudImage revisions.
Options match the documentation in RightScale: http://reference.rightscale.com/api1.5/resources/ResourceMultiCloudImages.html
Committing
If you wish to commit the MCI, set the
commit
option to the commit message you wish to use. If any change is made to the resource, Kingpin will commit a new revision with this message.Options
Name: (str) The name of the MCI to be updated. State: (str) Present or Absent. Default: “present” Commit: (str, None) If present, upon making changes to the HEAD version of the MCI, we will commit a new revision to RightScale. The provided string will be used as the commit message. Default: None Tags: (list, str) A list of tags to add to the MultiCloud image. Default: None Description: (str) The description of the MCI image itself. Default: “” Images: A list of dicts that each describe a single cloud and the image in that cloud to launch. See below for details. Image Definitions
Each cloud image definition is a dictionary that takes a few keys.
Cloud: (str) The name of the cloud as found in RightScale. We use the cloud ‘Name’ which can be found in your Settings -> Account Settings -> Clouds -> insert_cloud_here
page. For exampleAWS us-west-2
.Image: (str) The cloud-specific Image UID. For example ami-a1234abc
.Instance_type: (str) The default instance type to launch when this AMI is launched. For example, m1.small
. (optional)User_data: (str) The custom user data to pass to the instance on-bootup. (optional) Examples
{ "actor": "rightscale.MCI", "options": { "name": "Ubuntu i386 14.04", "description": "this is our test mci", "images": [ { "cloud": "EC2 us-west-2", "image": "ami-e29774d1", "instance_type": "m1.small", "user_data": "cd /bin/bash" }, { "cloud": "EC2 us-west-1", "image": "ami-b58142f1", "instance_type": "m1.small", "user_data": "cd /bin/bash" } ] } }
Right Scripts¶
-
class
kingpin.actors.rightscale.
RightScript
(*args, **kwargs)[source] Manages the state of a RightScale Script
Options match the documentation in RightScale: http://reference.rightscale.com/api1.5/resources/ResourceRightScripts.html
RightScript Inputs RightScripts have a concept of “inputs” ... pieces of data that are supplied to the script on-startup to help configure your instance. By default, RightScale parses the script you attach and automatically exposes inputs for you:
http://docs.rightscale.com/cm/rs101/understanding_inputs.html
RightScale has recently released an automatic script parser that looks for YAML-formatted comments in your script and reads the metadata from the comments. This is much more powerful and allows you to be explicit about the inputs required for your script to function.
http://docs.rightscale.com/cm/dashboard/design/rightscripts/rightscripts_metadata_comments.html
Options
Name: (str) The name of the RightScript. Description: (str) Optional description of the RightScript. Packages: (list, str) A list of packages that need to be installed in order to run the script. Source: (str) A file name with the contents of the script you want to upload. Script should contain the RightScale Metadata Comments (see above) which will automatically handle configuring the inputs for your script. Commit: (str) Optional comment used to commit the revision if Kingpin makes any changes at all. Examples
Create a high network activity alert on my-array:
{ "actor": "rightscale.alerts.Create", "options": { "name": "Set Hostname", "ensure": "present", "commit": "yep", "description": "Set the hostname to something usable", "packages": "hostname sed curl", "source": "./set_hostname.sh" } }
Server Templates¶
-
class
kingpin.actors.rightscale.
ServerTemplate
(*args, **kwargs)[source] Manages the state of an RightScale ServerTemplate.
This actor is able to create, destroy, modify and commit ServerTemplate revisions.
Options match the documentation in RightScale: http://reference.rightscale.com/api1.5/resources/ResourceServerTemplates.html
Committing
If you wish to commit the template, set the
commit
option to the commit message you wish to use. If any change is made to the resource, Kingpin will commit a new revision with this message.Options
Name: (str) The name of the template to be updated. State: (str) Present or Absent. Default: “present” Commit: (str, None) If present, upon making changes to the HEAD version of the template, we will commit a new revision to RightScale. The provided string will be used as the commit message. Default: None Commit_head_dependencies: (bool) Commit all HEAD revisions (if any) of the associated MultiCloud Images, RightScripts and Chef repo sequences. Default: False Freeze_repositories: (bool) Freeze the repositories on commit. Default: False Tags: (list, str) A list of tags to add to the ServerTemplate. Default: None Description: (str) The description of the MCI image itself. Default: “” Images: A list of dicts that each describe a single cloud and the image in that cloud to launch. See below for details. Runnable_bindings: A list of runnable binding references. Alerts: A list of kingpin.actors.rightscale.alerts.AlertSpecSchema
compatible dictionaries.Image Definitions
Each cloud image definition is a dictionary that takes a few keys.
Mci: (str) The name of the MultiCloudImage Rev: (str, int) The revision of the MultiCloudImage to use. Default: 0
Is_default: (bool) Whether or not this is the default MultiCloudImage for the template. Examples
{ "actor": "rightscale.ServerTemplate", "options": { "name": "Linux Server", "description": "this is a simple linux host", "images": [ { "mci": "Ubuntu 14.04 HVM", "rev": 5, "is_default": true }, { "mci": "Ubuntu 14.04 EBS" } ], "boot_bindings": [ { "right_script": "Boot Script", "rev": 2 } ] } }