52
53
Manages the running instances.
54
55
def __init__(self):
55
""" load configuration options for this node and connect to the hypervisor"""
56
"""Load configuration options and connect to the hypervisor."""
56
57
super(ComputeService, self).__init__()
57
58
self._instances = {}
58
59
self._conn = virt_connection.get_connection()
59
# TODO(joshua): This needs to ensure system state, specifically: modprobe aoe
60
# TODO(joshua): This needs to ensure system state, specifically
62
""" simple test of an AMQP message call """
64
"""Simple test of an AMQP message call."""
63
65
return defer.succeed('PONG')
65
def update_state(self, instance_id):
66
inst = models.Instance.find(instance_id)
67
def update_state(self, instance_id, context):
67
68
# FIXME(ja): include other fields from state?
68
inst.state = self._conn.get_info(inst.name)['state']
71
@exception.wrap_exception
72
def adopt_instances(self):
73
""" if there are instances already running, adopt them """
74
return defer.succeed(0)
75
instance_names = self._conn.list_instances()
76
for name in instance_names:
78
new_inst = Instance.fromName(self._conn, name)
79
new_inst.update_state()
82
return defer.succeed(len(self._instances))
69
instance_ref = db.instance_get(context, instance_id)
70
state = self._conn.get_info(instance_ref.name)['state']
71
db.instance_state(context, instance_id, state)
84
73
@defer.inlineCallbacks
85
74
@exception.wrap_exception
86
def run_instance(self, instance_id, **_kwargs):
87
""" launch a new instance with specified options """
88
inst = models.Instance.find(instance_id)
89
if inst.name in self._conn.list_instances():
75
def run_instance(self, instance_id, context=None, **_kwargs):
76
"""Launch a new instance with specified options."""
77
instance_ref = db.instance_get(context, instance_id)
78
if instance_ref['name'] in self._conn.list_instances():
90
79
raise exception.Error("Instance has already been created")
91
80
logging.debug("Starting instance %s..." % (instance_id))
92
inst = models.Instance.find(instance_id)
93
82
# NOTE(vish): passing network type allows us to express the
94
83
# network without making a call to network to find
95
84
# out which type of network to setup
96
network_service.setup_compute_network(inst.project_id)
97
inst.node_name = FLAGS.node_name
85
network_service.setup_compute_network(instance_ref['project_id'])
86
db.instance_update(context, instance_id, {'node_name': FLAGS.node_name})
100
88
# TODO(vish) check to make sure the availability zone matches
101
inst.set_state(power_state.NOSTATE, 'spawning')
89
db.instance_state(context, instance_id, power_state.NOSTATE, 'spawning')
104
yield self._conn.spawn(inst)
92
yield self._conn.spawn(instance_ref)
106
logging.exception("Failed to spawn instance %s" % inst.name)
107
inst.set_state(power_state.SHUTDOWN)
94
logging.exception("Failed to spawn instance %s" %
96
db.instance_state(context, instance_id, power_state.SHUTDOWN)
109
self.update_state(instance_id)
98
self.update_state(instance_id, context)
111
100
@defer.inlineCallbacks
112
101
@exception.wrap_exception
113
def terminate_instance(self, instance_id):
114
""" terminate an instance on this machine """
102
def terminate_instance(self, instance_id, context=None):
103
"""Terminate an instance on this machine."""
115
104
logging.debug("Got told to terminate instance %s" % instance_id)
116
inst = models.Instance.find(instance_id)
105
instance_ref = db.instance_get(context, instance_id)
118
if inst.state == power_state.SHUTOFF:
119
# self.datamodel.destroy() FIXME: RE-ADD ?????
107
if instance_ref['state'] == power_state.SHUTOFF:
108
# self.datamodel.destroy() FIXME: RE-ADD?
120
109
raise exception.Error('trying to destroy already destroyed'
121
110
' instance: %s' % instance_id)
123
inst.set_state(power_state.NOSTATE, 'shutting_down')
124
yield self._conn.destroy(inst)
113
context, instance_id, power_state.NOSTATE, 'shutting_down')
114
yield self._conn.destroy(instance_ref)
125
116
# FIXME(ja): should we keep it in a terminated state for a bit?
117
db.instance_destroy(context, instance_id)
128
119
@defer.inlineCallbacks
129
120
@exception.wrap_exception
130
def reboot_instance(self, instance_id):
131
""" reboot an instance on this server
132
KVM doesn't support reboot, so we terminate and restart """
133
self.update_state(instance_id)
134
instance = models.Instance.find(instance_id)
121
def reboot_instance(self, instance_id, context=None):
122
"""Reboot an instance on this server.
124
KVM doesn't support reboot, so we terminate and restart.
127
self.update_state(instance_id, context)
128
instance_ref = db.instance_get(context, instance_id)
136
130
# FIXME(ja): this is only checking the model state - not state on disk?
137
if instance.state != power_state.RUNNING:
131
if instance_ref['state'] != power_state.RUNNING:
138
132
raise exception.Error(
139
133
'trying to reboot a non-running'
140
'instance: %s (state: %s excepted: %s)' % (instance.name, instance.state, power_state.RUNNING))
134
'instance: %s (state: %s excepted: %s)' %
135
(instance_ref['name'],
136
instance_ref['state'],
137
power_state.RUNNING))
142
logging.debug('rebooting instance %s' % instance.name)
143
instance.set_state(power_state.NOSTATE, 'rebooting')
144
yield self._conn.reboot(instance)
145
self.update_state(instance_id)
139
logging.debug('rebooting instance %s' % instance_ref['name'])
141
context, instance_id, power_state.NOSTATE, 'rebooting')
142
yield self._conn.reboot(instance_ref)
143
self.update_state(instance_id, context)
147
145
@exception.wrap_exception
148
def get_console_output(self, instance_id):
149
""" send the console output for an instance """
146
def get_console_output(self, instance_id, context=None):
147
"""Send the console output for an instance."""
150
148
# FIXME: Abstract this for Xen
152
150
logging.debug("Getting console output for %s" % (instance_id))
153
inst = models.Instance.find(instance_id)
151
instance_ref = db.instance_get(context, instance_id)
155
153
if FLAGS.connection_type == 'libvirt':
156
fname = os.path.abspath(
157
os.path.join(FLAGS.instances_path, inst.name, 'console.log'))
154
fname = os.path.abspath(os.path.join(FLAGS.instances_path,
155
instance_ref['name'],
158
157
with open(fname, 'r') as f:
159
158
output = f.read()
170
169
@defer.inlineCallbacks
171
170
@exception.wrap_exception
172
def attach_volume(self, instance_id = None,
173
volume_id = None, mountpoint = None):
174
volume = volume_service.get_volume(volume_id)
171
def attach_volume(self, instance_id=None, volume_id=None, mountpoint=None,
173
"""Attach a volume to an instance."""
174
# TODO(termie): check that instance_id exists
175
volume_ref = volume_get(context, volume_id)
175
176
yield self._init_aoe()
176
177
yield process.simple_execute(
177
178
"sudo virsh attach-disk %s /dev/etherd/%s %s" %
179
180
volume['aoe_device'],
180
181
mountpoint.rpartition('/dev/')[2]))
181
volume.finish_attach()
182
volume_attached(context, volume_id)
182
183
defer.returnValue(True)
184
185
@defer.inlineCallbacks
186
yield process.simple_execute("sudo aoe-discover")
187
yield process.simple_execute("sudo aoe-stat")
189
@defer.inlineCallbacks
190
186
@exception.wrap_exception
191
def detach_volume(self, instance_id, volume_id):
192
""" detach a volume from an instance """
187
def detach_volume(self, instance_id, volume_id, context=None):
188
"""Detach a volume from an instance."""
193
189
# despite the documentation, virsh detach-disk just wants the device
194
190
# name without the leading /dev/
195
volume = volume_service.get_volume(volume_id)
191
# TODO(termie): check that instance_id exists
192
volume_ref = volume_get(context, volume_id)
196
193
target = volume['mountpoint'].rpartition('/dev/')[2]
197
194
yield process.simple_execute(
198
195
"sudo virsh detach-disk %s %s " % (instance_id, target))
199
volume.finish_detach()
196
volume_detached(context, volume_id)
200
197
defer.returnValue(True)
199
@defer.inlineCallbacks
201
yield process.simple_execute("sudo aoe-discover")
202
yield process.simple_execute("sudo aoe-stat")