Skip to content

Commit 185565b

Browse files
longzhouGerrit Code Review
authored and
Gerrit Code Review
committed
Support adding image datastores - agent update_config API
Add an agent thrift API (AgentControl.update_config) to allow updating agent's configuration after provision has completed, and new configuration can take effect without restarting agent. Currently this API only updates image datastores. Change-Id: I91d3e37239b40ee6cefe12ea9d46c328329baee8
1 parent 977c0be commit 185565b

File tree

9 files changed

+125
-45
lines changed

9 files changed

+125
-45
lines changed

python/src/agent/agent/agent_config.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def _check_and_set_attr(self, attr_name, value):
144144
return True
145145

146146
@locked
147-
def update_config(self, provision_req):
147+
def provision(self, provision_req):
148148
"""
149149
Update the agent configuration using the provisioning request
150150
configuration.
@@ -238,6 +238,13 @@ def update_config(self, provision_req):
238238
str(self._options))
239239
self._reboot_required = True
240240

241+
@locked
242+
def update(self, update_config_req):
243+
image_datastores = self._convert_image_datastores(update_config_req.image_datastores)
244+
if self._check_and_set_attr(self.IMAGE_DATASTORES, image_datastores):
245+
self._persist_config()
246+
self._trigger_callbacks(self.IMAGE_DATASTORES, image_datastores)
247+
241248
@property
242249
@locked
243250
def datastores(self):

python/src/agent/agent/agent_control_handler.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
from gen.agent.ttypes import AgentStatusCode
2626
from gen.agent.ttypes import ProvisionResponse
2727
from gen.agent.ttypes import ProvisionResultCode
28+
from gen.agent.ttypes import UpdateConfigResponse
29+
from gen.agent.ttypes import UpdateConfigResultCode
2830
from gen.agent.ttypes import UpgradeResponse
2931
from gen.agent.ttypes import UpgradeResultCode
3032
from gen.agent.ttypes import VersionResponse
@@ -60,7 +62,7 @@ def provision(self, request):
6062

6163
try:
6264
agent_config = common.services.get(ServiceName.AGENT_CONFIG)
63-
agent_config.update_config(request)
65+
agent_config.provision(request)
6466
except InvalidConfig as e:
6567
return ProvisionResponse(ProvisionResultCode.INVALID_CONFIG, str(e))
6668
except Exception, e:
@@ -69,6 +71,24 @@ def provision(self, request):
6971

7072
return ProvisionResponse(ProvisionResultCode.OK)
7173

74+
@log_request
75+
@error_handler(UpdateConfigResponse, UpdateConfigResultCode)
76+
def update_config(self, request):
77+
"""
78+
Update agent's configuration.
79+
:type request: UpdateConfigRequest
80+
:rtype: UpdateConfigResponse
81+
"""
82+
83+
try:
84+
agent_config = common.services.get(ServiceName.AGENT_CONFIG)
85+
agent_config.update(request)
86+
except Exception, e:
87+
self._logger.warning("Unexpected exception", exc_info=True)
88+
return UpdateConfigResponse(UpdateConfigResultCode.SYSTEM_ERROR, str(e))
89+
90+
return UpdateConfigResponse(UpdateConfigResultCode.OK)
91+
7292
@log_request
7393
@error_handler(UpgradeResponse, UpgradeResultCode)
7494
def upgrade(self, request):

python/src/agent/agent/tests/unit/test_agent_config.py

+27-16
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from common.mode import Mode
2424
from common.service_name import ServiceName
2525
from common.state import State
26-
from gen.agent.ttypes import ProvisionRequest
26+
from gen.agent.ttypes import ProvisionRequest, UpdateConfigRequest
2727
from gen.common.ttypes import ServerAddress
2828
from gen.resource.ttypes import ImageDatastore
2929
from gen.stats.plugin.ttypes import StatsPluginConfig
@@ -138,7 +138,7 @@ def test_agent_config_update(self):
138138

139139
req.host_id = "host1"
140140
req.deployment_id = "deployment1"
141-
self.agent.update_config(req)
141+
self.agent.provision(req)
142142

143143
assert_that(self.agent.stats_store_endpoint, equal_to("10.0.0.100"))
144144
assert_that(self.agent.stats_store_port, equal_to(8081))
@@ -158,7 +158,7 @@ def test_agent_config_update(self):
158158
# Verify we are able to unset all the configuration.
159159
req = ProvisionRequest()
160160

161-
self.agent.update_config(req)
161+
self.agent.provision(req)
162162
assert_that(self.agent.hostname, equal_to(None))
163163
assert_that(self.agent.host_port, equal_to(8835))
164164
assert_that(self.agent.datastores, equal_to([]))
@@ -178,7 +178,7 @@ def test_agent_config_update(self):
178178
req.address = addr
179179

180180
# Verify an exception is raised.
181-
self.assertRaises(InvalidConfig, self.agent.update_config, req)
181+
self.assertRaises(InvalidConfig, self.agent.provision, req)
182182
assert_that(self.agent.hostname, equal_to(None))
183183
assert_that(self.agent.host_port, equal_to(8835))
184184
assert_that(self.agent.datastores, equal_to([]))
@@ -200,7 +200,7 @@ def test_reboot_required(self):
200200
req.stats_plugin_config.enabled = False
201201
addr = ServerAddress(host="localhost", port=2345)
202202
req.address = addr
203-
self.agent.update_config(req)
203+
self.agent.provision(req)
204204
# Verify that the bootstrap is still false as zk config is not
205205
# specified.
206206
self.assertFalse(self.agent.bootstrap_ready)
@@ -210,7 +210,7 @@ def test_reboot_required(self):
210210
req.datastores = ["ds3", "ds4"]
211211
addr = ServerAddress(host="localhost", port=2345)
212212
req.address = addr
213-
self.agent.update_config(req)
213+
self.agent.provision(req)
214214
self.assertTrue(self.agent.reboot_required)
215215

216216
def test_thrift_thread_settings(self):
@@ -253,15 +253,27 @@ def test_load_image_datastores(self):
253253
{"name": "ds2", "used_for_vms": False},
254254
]
255255
req = ProvisionRequest()
256-
req.datastores = ["ds1", "ds2", "ds3"]
257-
req.image_datastores = set([ImageDatastore("ds1", True),
258-
ImageDatastore("ds2", False)])
259-
self.agent.update_config(req)
260-
self.agent._persist_config()
256+
req.datastores = ["ds1", "ds2", "ds3", "ds4"]
257+
req.image_datastores = {ImageDatastore("ds1", True), ImageDatastore("ds2", False)}
258+
self.agent.provision(req)
261259
self.agent._load_config()
262-
assert_that(self.agent.datastores, equal_to(["ds1", "ds2", "ds3"]))
263-
assert_that(self.agent.image_datastores,
264-
contains_inanyorder(*expected_image_ds))
260+
assert_that(self.agent.datastores, equal_to(["ds1", "ds2", "ds3", "ds4"]))
261+
assert_that(self.agent.image_datastores, contains_inanyorder(*expected_image_ds))
262+
263+
imageds_callback = mock.MagicMock()
264+
self.agent.on_config_change(self.agent.IMAGE_DATASTORES, imageds_callback)
265+
req = UpdateConfigRequest()
266+
req.image_datastores = {ImageDatastore("ds1", True), ImageDatastore("ds2", False), ImageDatastore("ds3", True)}
267+
self.agent.update(req)
268+
expected_image_ds = [
269+
{"name": "ds1", "used_for_vms": True},
270+
{"name": "ds2", "used_for_vms": False},
271+
{"name": "ds3", "used_for_vms": True},
272+
]
273+
assert_that(self.agent.image_datastores, contains_inanyorder(*expected_image_ds))
274+
imageds_callback.assert_called_once_with(self.agent.image_datastores)
275+
self.agent._load_config()
276+
assert_that(self.agent.image_datastores, contains_inanyorder(*expected_image_ds))
265277

266278
def test_config_change(self):
267279
# Test cpu_overcommit and memory_overcommit config change
@@ -273,10 +285,9 @@ def test_config_change(self):
273285
provision.memory_overcommit = 6.0
274286
self.agent.on_config_change(self.agent.CPU_OVERCOMMIT, cpu_callback)
275287
self.agent.on_config_change(self.agent.MEMORY_OVERCOMMIT, mem_callback)
276-
self.agent.update_config(provision)
288+
self.agent.provision(provision)
277289
cpu_callback.assert_called_once_with(5.0)
278290
mem_callback.assert_called_once_with(6.0)
279291

280-
281292
if __name__ == "__main__":
282293
unittest.main()

python/src/host/host/hypervisor/datastore_manager.py

+4
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ def datastores_updated(self):
169169
"""vim client callback for datastore change"""
170170
self._initialize_datastores()
171171

172+
def set_image_datastores(self, image_datastores):
173+
self._configured_image_datastores = image_datastores
174+
self._initialize_datastores()
175+
172176
def datastore_type(self, datastore_id):
173177
""" Get datastore_type from datastore_id
174178

python/src/host/host/hypervisor/hypervisor.py

+3
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,6 @@ def cpu_overcommit(self):
108108

109109
def set_cpu_overcommit(self, value):
110110
self.placement_manager.cpu_overcommit = value
111+
112+
def set_image_datastores(self, value):
113+
self.datastore_manager.set_image_datastores(value)

python/src/host/host/plugin.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ def init(self):
3232
hv = hypervisor.Hypervisor(config)
3333

3434
# When configuration changes, notify hypervisor
35-
config.on_config_change(config.CPU_OVERCOMMIT,
36-
hv.set_cpu_overcommit)
37-
config.on_config_change(config.MEMORY_OVERCOMMIT,
38-
hv.set_memory_overcommit)
35+
config.on_config_change(config.CPU_OVERCOMMIT, hv.set_cpu_overcommit)
36+
config.on_config_change(config.MEMORY_OVERCOMMIT, hv.set_memory_overcommit)
37+
config.on_config_change(config.IMAGE_DATASTORES, hv.set_image_datastores)
3938

4039
# Register hypervisor in services
4140
common.services.register(ServiceName.HYPERVISOR, hv)
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) 2015 VMware, Inc. All Rights Reserved.
3+
import sys
4+
5+
from eccli.format import print_request
6+
from eccli.format import print_response
7+
from eccli.optparser import default_parser
8+
from eccli.thrift import get_client
9+
from gen.agent.ttypes import UpdateConfigRequest
10+
from gen.resource.ttypes import ImageDatastore
11+
12+
parser = default_parser(usage="eccli-config-update [options]",
13+
add_help=True)
14+
parser.add_option("-d", "--image_datastores",
15+
action="store", type="string", dest="imageds",
16+
help="list of image datastore names (e.g. ds1,ds2)")
17+
(options, args) = parser.parse_args()
18+
19+
if not options.imageds:
20+
print >> sys.stderr, "Error: image datastores are required\n"
21+
parser.print_help()
22+
exit(1)
23+
24+
client = get_client(options.host, "AgentControl")
25+
26+
request = UpdateConfigRequest()
27+
request.image_datastores = set([ImageDatastore(name=ds, used_for_vms=True) for ds in options.imageds.split(',')])
28+
29+
print_request(request)
30+
response = client.update_config(request)
31+
print_response(response)

thrift/agent.thrift

+28-18
Original file line numberDiff line numberDiff line change
@@ -78,20 +78,6 @@ struct VersionResponse {
7878
4: optional string revision
7979
}
8080

81-
82-
// Structure describing the refcount preserved with the image.
83-
// All fields are optional for future compatibility reasons.
84-
struct RefCount {
85-
// The generation number of the ref count file.
86-
1: optional i16 generation_num
87-
// Version of refcount implementation.
88-
// If unset assume initial version.
89-
2: optional byte version
90-
3: optional bool tombstone
91-
4: optional i16 ref_count
92-
5: optional binary vm_ids
93-
}
94-
9581
// Struct describing the provisioning configuration of the esx agent.
9682
struct ProvisionRequest {
9783
// The datastores to use for cloud virtual machine workloads
@@ -107,9 +93,6 @@ struct ProvisionRequest {
10793
// i.e. no overcommit
10894
8: optional double memory_overcommit
10995

110-
// The information about the image datastore configuration
111-
10: optional resource.ImageDatastore image_datastore_info
112-
11396
// The cpu overcommit for this host. If unspecified it defaults to 1.0,
11497
// i.e. no overcommit
11598
11: optional double cpu_overcommit
@@ -124,7 +107,6 @@ struct ProvisionRequest {
124107
14: optional string ntp_endpoint
125108

126109
// A set of image datastores for this host.
127-
// The image_datastore_info field will be deprecated.
128110
15: optional set<resource.ImageDatastore> image_datastores
129111

130112
// Configuration of the Stats Plugin
@@ -160,6 +142,31 @@ struct ProvisionResponse {
160142
2: optional string error
161143
}
162144

145+
// Struct describing the updating configuration of the esx agent.
146+
struct UpdateConfigRequest {
147+
// A set of image datastores for this host.
148+
1: optional set<resource.ImageDatastore> image_datastores
149+
150+
99: optional tracing.TracingInfo tracing_info
151+
}
152+
153+
// Update config result code
154+
enum UpdateConfigResultCode {
155+
// Update config was successful.
156+
OK = 0
157+
// The configuration provided is invalid.
158+
INVALID_CONFIG = 1
159+
160+
// Catch all error
161+
SYSTEM_ERROR = 15
162+
}
163+
164+
// Update config response
165+
struct UpdateConfigResponse {
166+
1: required UpdateConfigResultCode result
167+
2: optional string error
168+
}
169+
163170
// Upgrade request
164171
struct UpgradeRequest {
165172
99: optional tracing.TracingInfo tracing_info
@@ -205,6 +212,9 @@ service AgentControl {
205212
// Method to provision an agent for esxcloud purposes.
206213
ProvisionResponse provision(1: ProvisionRequest request)
207214

215+
// Method to update agent's configuration.
216+
UpdateConfigResponse update_config(1: UpdateConfigRequest request)
217+
208218
// Method to upgrade an agent.
209219
UpgradeResponse upgrade(1: UpgradeRequest request)
210220

thrift/resource.thrift

-5
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,6 @@ struct ImageDatastore {
111111
2: required bool used_for_vms
112112
}
113113

114-
// FaultDomain
115-
struct FaultDomain {
116-
1: required string id
117-
}
118-
119114
// ResourceConstraint
120115
struct ResourceConstraint {
121116
1: required ResourceConstraintType type

0 commit comments

Comments
 (0)