def edit_launch_config(self, request, data): """ Edit the launch configuration for a scaling group, which includes the details of how to create a server, from what image, which load balancers to join it to, and what networks to add it to, and other metadata. This data provided in the request body in JSON format. If successful, no response body will be returned. Example request:: { "type": "launch_server", "args": { "server": { "flavorRef": 3, "name": "webhead", "imageRef": "0d589460-f177-4b0f-81c1-8ab8903ac7d8", "OS-DCF:diskConfig": "AUTO", "metadata": { "mykey": "myvalue" }, "personality": [ { "path": '/root/.ssh/authorized_keys', "contents": "ssh-rsa A... [email protected]" } ], "networks": [ { "uuid": "11111111-1111-1111-1111-111111111111" } ], }, "loadBalancers": [ { "loadBalancerId": 2200, "port": 8081 } ] } } The exact update cases are still up in the air -- can the user provide a mimimal schema, and if so, what happens with defaults? Nova should validate the image before saving the new config. Users may have an invalid configuration based on dependencies. """ rec = self.store.get_scaling_group( self.log, self.tenant_id, self.group_id) data = normalize_launch_config(data) group_schemas.validate_launch_config_servicenet(data) deferred = get_supervisor().validate_launch_config( self.log, self.tenant_id, data) deferred.addCallback(lambda _: rec.update_launch_config(data)) return deferred
def test_servicenet_validation_succeeds_if_rackconnect_but_no_network_info(self): """ If only RackConnect LBs are confgured on the launch configuration, and no ServiceNet is supplied, that's still ok because RackConnect does not need ServiceNet so validation succeeds. """ good_schema = { "server": {"networks": [{'uuid': "00000000-0000-0000-0000-000000000000"}]}, "loadBalancers": [{'loadBalancerId': '1234', 'type': 'RackConnectV3'}] } self.assertIsNone( group_schemas.validate_launch_config_servicenet( {'type': 'launch_server', 'args': good_schema}))
def test_servicenet_validation_succeeds_if_clbs_but_no_network_info(self): """ If CLBs are confgured on the launch configuration, but no network configuration is supplied, Nova sets up both ServiceNet and public network, so validation succeeds. """ good_schema = { "server": {}, "loadBalancers": [{'loadBalancerId': 1, "port": 80}] } self.assertIsNone( group_schemas.validate_launch_config_servicenet( {'type': 'launch_server', 'args': good_schema}))
def test_servicenet_validation_succeeds_if_clbs_and_servicenet(self): """ If CLBs are confgured on the launch configuration, but and ServiceNet is specifically provided, validation succeeds """ good_schema = { "server": {"networks": [ {'uuid': "00000000-0000-0000-0000-000000000000"}, {'uuid': "11111111-1111-1111-1111-111111111111"}]}, "loadBalancers": [{'loadBalancerId': 1, "port": 80}] } self.assertIsNone( group_schemas.validate_launch_config_servicenet( {'type': 'launch_server', 'args': good_schema}))
def create_new_scaling_group(self, request, data): """ Create a new scaling group, given the general scaling group configuration, launch configuration, and optional scaling policies. This data provided in the request body in JSON format. If successful, the created group in JSON format containing id and links is returned. Example request body containing some scaling policies:: { "launchConfiguration": { "args": { "loadBalancers": [ { "port": 8080, "loadBalancerId": 9099 } ], "server": { "name": "autoscale_server", "imageRef": "0d589460-f177-4b0f-81c1-8ab8903ac7d8", "flavorRef": "2", "OS-DCF:diskConfig": "AUTO", "metadata": { "meta_key_1": "meta_value_1", "meta_key_2": "meta_value_2" }, "networks": [ { "uuid": "11111111-1111-1111-1111-111111111111" }, { "uuid": "00000000-0000-0000-0000-000000000000" } ], "personality": [ { "path": "/root/.csivh", "contents": "VGhpcyBpcyBhIHRlc3QgZmlsZS4=" } ] } }, "type": "launch_server" }, "groupConfiguration": { "maxEntities": 10, "cooldown": 360, "name": "testscalinggroup198547", "minEntities": 0, "metadata": { "gc_meta_key_2": "gc_meta_value_2", "gc_meta_key_1": "gc_meta_value_1" } }, "scalingPolicies": [ { "cooldown": 0, "type": "webhook", "name": "scale up by 1", "change": 1 } ] } The ``scalingPolicies`` attribute can also be an empty list, or just left out entirely. Example response body to the above request:: { "group": { "launchConfiguration": { "args": { "loadBalancers": [ { "port": 8080, "loadBalancerId": 9099 } ], "server": { "name": "autoscale_server", "imageRef": "0d589460-f177-4b0f-81c1-8ab8903ac7d8", "flavorRef": "2", "OS-DCF:diskConfig": "AUTO", "personality": [ { "path": "/root/.csivh", "contents": "VGhpcyBpcyBhIHRlc3QgZmlsZS4=" } ], "networks": [ { "uuid": "11111111-1111-1111-1111-111111111111" }, { "uuid": "00000000-0000-0000-0000-000000000000" } ], "metadata": { "meta_key_1": "meta_value_1", "meta_key_2": "meta_value_2" } } }, "type": "launch_server" }, "groupConfiguration": { "maxEntities": 10, "cooldown": 360, "name": "testscalinggroup198547", "minEntities": 0, "metadata": { "gc_meta_key_2": "gc_meta_value_2", "gc_meta_key_1": "gc_meta_value_1" } }, "state": { "active": [], "activeCapacity": 0, "desiredCapacity": 0, "paused": false, "pendingCapacity": 0, "name": "testscalinggroup198547" }, "scalingPolicies": [ { "name": "scale up by 1", "links": [ { "href": "https://ord.autoscale.api.rackspacecloud.com/ v1.0/829409/groups/6791761b-821a-4d07-820d-0b2afc7dd7f6/ policies/dceb14ac-b2b3-4f06-aac9-a5b6cd5d40e1/", "rel": "self" } ], "cooldown": 0, "type": "webhook", "id": "dceb14ac-b2b3-4f06-aac9-a5b6cd5d40e1", "change": 1 } ], "links": [ { "href": "https://ord.autoscale.api.rackspacecloud.com/ v1.0/829409/groups/6791761b-821a-4d07-820d-0b2afc7dd7f6/", "rel": "self" } ], "id": "6791761b-821a-4d07-820d-0b2afc7dd7f6" } } """ group_cfg = data['groupConfiguration'] group_cfg.setdefault('maxEntities', MAX_ENTITIES) group_cfg.setdefault('metadata', {}) if group_cfg['minEntities'] > group_cfg['maxEntities']: raise InvalidMinEntities( "minEntities must be less than or equal to maxEntities") validate_launch_config_servicenet(data['launchConfiguration']) deferred = get_supervisor().validate_launch_config( self.log, self.tenant_id, data['launchConfiguration']) deferred.addCallback( lambda _: self.store.create_scaling_group( self.log, self.tenant_id, group_cfg, normalize_launch_config(data['launchConfiguration']), data.get('scalingPolicies', None))) def _do_obey_config_change(result): group_id = result['id'] config = result['groupConfiguration'] launch = result['launchConfiguration'] group = self.store.get_scaling_group( self.log, self.tenant_id, group_id) log = self.log.bind(scaling_group_id=group_id) d = controller.modify_and_trigger( self.dispatcher, group, bound_log_kwargs(log), partial( controller.obey_config_change, log, transaction_id(request), config, launch_config=launch), modify_state_reason='create_new_scaling_group') return d.addCallback(lambda _: result) deferred.addCallback(_do_obey_config_change) def _add_to_bobby(result, client): d = client.create_group(self.tenant_id, result['id']) return d.addCallback(lambda _: result) bobby = get_bobby() if bobby is not None: deferred.addCallback(_add_to_bobby, bobby) def _format_output(result): uuid = result['id'] result["state"] = format_state_dict(result["state"]) request.setHeader( "Location", get_autoscale_links(self.tenant_id, uuid, format=None)) result["links"] = get_autoscale_links(self.tenant_id, uuid) linkify_policy_list( result['scalingPolicies'], self.tenant_id, uuid) result['scalingPolicies_links'] = get_policies_links( result['scalingPolicies'], self.tenant_id, uuid, rel='policies') return {"group": result} deferred.addCallback(_format_output) deferred.addCallback(json.dumps) return deferred
def create_new_scaling_group(self, request, data): """ Create a new scaling group, given the general scaling group configuration, launch configuration, and optional scaling policies. This data provided in the request body in JSON format. If successful, the created group in JSON format containing id and links is returned. Example request body containing some scaling policies:: { "launchConfiguration": { "args": { "loadBalancers": [ { "port": 8080, "loadBalancerId": 9099 } ], "server": { "name": "autoscale_server", "imageRef": "0d589460-f177-4b0f-81c1-8ab8903ac7d8", "flavorRef": "2", "OS-DCF:diskConfig": "AUTO", "metadata": { "meta_key_1": "meta_value_1", "meta_key_2": "meta_value_2" }, "networks": [ { "uuid": "11111111-1111-1111-1111-111111111111" }, { "uuid": "00000000-0000-0000-0000-000000000000" } ], "personality": [ { "path": "/root/.csivh", "contents": "VGhpcyBpcyBhIHRlc3QgZmlsZS4=" } ] } }, "type": "launch_server" }, "groupConfiguration": { "maxEntities": 10, "cooldown": 360, "name": "testscalinggroup198547", "minEntities": 0, "metadata": { "gc_meta_key_2": "gc_meta_value_2", "gc_meta_key_1": "gc_meta_value_1" } }, "scalingPolicies": [ { "cooldown": 0, "type": "webhook", "name": "scale up by 1", "change": 1 } ] } The ``scalingPolicies`` attribute can also be an empty list, or just left out entirely. Example response body to the above request:: { "group": { "launchConfiguration": { "args": { "loadBalancers": [ { "port": 8080, "loadBalancerId": 9099 } ], "server": { "name": "autoscale_server", "imageRef": "0d589460-f177-4b0f-81c1-8ab8903ac7d8", "flavorRef": "2", "OS-DCF:diskConfig": "AUTO", "personality": [ { "path": "/root/.csivh", "contents": "VGhpcyBpcyBhIHRlc3QgZmlsZS4=" } ], "networks": [ { "uuid": "11111111-1111-1111-1111-111111111111" }, { "uuid": "00000000-0000-0000-0000-000000000000" } ], "metadata": { "meta_key_1": "meta_value_1", "meta_key_2": "meta_value_2" } } }, "type": "launch_server" }, "groupConfiguration": { "maxEntities": 10, "cooldown": 360, "name": "testscalinggroup198547", "minEntities": 0, "metadata": { "gc_meta_key_2": "gc_meta_value_2", "gc_meta_key_1": "gc_meta_value_1" } }, "state": { "active": [], "activeCapacity": 0, "desiredCapacity": 0, "paused": false, "pendingCapacity": 0, "name": "testscalinggroup198547" }, "scalingPolicies": [ { "name": "scale up by 1", "links": [ { "href": "https://ord.autoscale.api.rackspacecloud.com/ v1.0/829409/groups/6791761b-821a-4d07-820d-0b2afc7dd7f6/ policies/dceb14ac-b2b3-4f06-aac9-a5b6cd5d40e1/", "rel": "self" } ], "cooldown": 0, "type": "webhook", "id": "dceb14ac-b2b3-4f06-aac9-a5b6cd5d40e1", "change": 1 } ], "links": [ { "href": "https://ord.autoscale.api.rackspacecloud.com/ v1.0/829409/groups/6791761b-821a-4d07-820d-0b2afc7dd7f6/", "rel": "self" } ], "id": "6791761b-821a-4d07-820d-0b2afc7dd7f6" } } """ group_cfg = data['groupConfiguration'] group_cfg.setdefault('maxEntities', MAX_ENTITIES) group_cfg.setdefault('metadata', {}) if group_cfg['minEntities'] > group_cfg['maxEntities']: raise InvalidMinEntities( "minEntities must be less than or equal to maxEntities") if data['launchConfiguration']['type'] == 'launch_server': validate_launch_config_servicenet(data['launchConfiguration']) deferred = get_supervisor().validate_launch_config( self.log, self.tenant_id, data['launchConfiguration']) deferred.addCallback( lambda _: self.store.create_scaling_group( self.log, self.tenant_id, group_cfg, normalize_launch_config(data['launchConfiguration']), data.get('scalingPolicies', None))) def _do_obey_config_change(result): group_id = result['id'] config = result['groupConfiguration'] launch = result['launchConfiguration'] group = self.store.get_scaling_group( self.log, self.tenant_id, group_id) log = self.log.bind(scaling_group_id=group_id) d = controller.modify_and_trigger( self.dispatcher, group, bound_log_kwargs(log), partial( controller.obey_config_change, log, transaction_id(request), config, launch_config=launch), modify_state_reason='create_new_scaling_group') return d.addCallback(lambda _: result) deferred.addCallback(_do_obey_config_change) def _add_to_bobby(result, client): d = client.create_group(self.tenant_id, result['id']) return d.addCallback(lambda _: result) bobby = get_bobby() if bobby is not None: deferred.addCallback(_add_to_bobby, bobby) def _format_output(result): uuid = result['id'] result["state"] = format_state_dict(result["state"]) request.setHeader( "Location", get_autoscale_links(self.tenant_id, uuid, format=None)) result["links"] = get_autoscale_links(self.tenant_id, uuid) linkify_policy_list( result['scalingPolicies'], self.tenant_id, uuid) result['scalingPolicies_links'] = get_policies_links( result['scalingPolicies'], self.tenant_id, uuid, rel='policies') return {"group": result} deferred.addCallback(_format_output) deferred.addCallback(json.dumps) return deferred