71410724cd
Removes copy of ajaxterm code, nova-ajax-console-proxy, and support for get_ajax_console from Nova proper. Implements blueprint remove-ajaxterm Fixes bug 917963 Change-Id: I2c0ff427c53c0f63a18b10475d6b4cbe9a085d83
636 lines
25 KiB
Python
636 lines
25 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2011 Justin Santa Barbara
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
Driver base-classes:
|
|
|
|
(Beginning of) the contract that compute drivers must follow, and shared
|
|
types that support that contract
|
|
"""
|
|
|
|
from nova.compute import power_state
|
|
|
|
|
|
class InstanceInfo(object):
|
|
def __init__(self, name, state):
|
|
self.name = name
|
|
assert state in power_state.valid_states(), "Bad state: %s" % state
|
|
self.state = state
|
|
|
|
|
|
def block_device_info_get_root(block_device_info):
|
|
block_device_info = block_device_info or {}
|
|
return block_device_info.get('root_device_name')
|
|
|
|
|
|
def block_device_info_get_swap(block_device_info):
|
|
block_device_info = block_device_info or {}
|
|
return block_device_info.get('swap') or {'device_name': None,
|
|
'swap_size': 0}
|
|
|
|
|
|
def swap_is_usable(swap):
|
|
return swap and swap['device_name'] and swap['swap_size'] > 0
|
|
|
|
|
|
def block_device_info_get_ephemerals(block_device_info):
|
|
block_device_info = block_device_info or {}
|
|
ephemerals = block_device_info.get('ephemerals') or []
|
|
return ephemerals
|
|
|
|
|
|
def block_device_info_get_mapping(block_device_info):
|
|
block_device_info = block_device_info or {}
|
|
block_device_mapping = block_device_info.get('block_device_mapping') or []
|
|
return block_device_mapping
|
|
|
|
|
|
class ComputeDriver(object):
|
|
"""Base class for compute drivers.
|
|
|
|
The interface to this class talks in terms of 'instances' (Amazon EC2 and
|
|
internal Nova terminology), by which we mean 'running virtual machine'
|
|
(XenAPI terminology) or domain (Xen or libvirt terminology).
|
|
|
|
An instance has an ID, which is the identifier chosen by Nova to represent
|
|
the instance further up the stack. This is unfortunately also called a
|
|
'name' elsewhere. As far as this layer is concerned, 'instance ID' and
|
|
'instance name' are synonyms.
|
|
|
|
Note that the instance ID or name is not human-readable or
|
|
customer-controlled -- it's an internal ID chosen by Nova. At the
|
|
nova.virt layer, instances do not have human-readable names at all -- such
|
|
things are only known higher up the stack.
|
|
|
|
Most virtualization platforms will also have their own identity schemes,
|
|
to uniquely identify a VM or domain. These IDs must stay internal to the
|
|
platform-specific layer, and never escape the connection interface. The
|
|
platform-specific layer is responsible for keeping track of which instance
|
|
ID maps to which platform-specific ID, and vice versa.
|
|
|
|
In contrast, the list_disks and list_interfaces calls may return
|
|
platform-specific IDs. These identify a specific virtual disk or specific
|
|
virtual network interface, and these IDs are opaque to the rest of Nova.
|
|
|
|
Some methods here take an instance of nova.compute.service.Instance. This
|
|
is the data structure used by nova.compute to store details regarding an
|
|
instance, and pass them into this layer. This layer is responsible for
|
|
translating that generic data structure into terms that are specific to the
|
|
virtualization platform.
|
|
|
|
"""
|
|
|
|
def init_host(self, host):
|
|
"""Initialize anything that is necessary for the driver to function,
|
|
including catching up with currently running VM's on the given host."""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def get_info(self, instance_name):
|
|
"""Get the current status of an instance, by name (not ID!)
|
|
|
|
Returns a dict containing:
|
|
|
|
:state: the running state, one of the power_state codes
|
|
:max_mem: (int) the maximum memory in KBytes allowed
|
|
:mem: (int) the memory in KBytes used by the domain
|
|
:num_cpu: (int) the number of virtual CPUs for the domain
|
|
:cpu_time: (int) the CPU time used in nanoseconds
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def list_instances(self):
|
|
"""
|
|
Return the names of all the instances known to the virtualization
|
|
layer, as a list.
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def list_instances_detail(self):
|
|
"""Return a list of InstanceInfo for all registered VMs"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def spawn(self, context, instance, image_meta,
|
|
network_info=None, block_device_info=None):
|
|
"""
|
|
Create a new instance/VM/domain on the virtualization platform.
|
|
|
|
Once this successfully completes, the instance should be
|
|
running (power_state.RUNNING).
|
|
|
|
If this fails, any partial instance should be completely
|
|
cleaned up, and the virtualization platform should be in the state
|
|
that it was before this call began.
|
|
|
|
:param context: security context
|
|
:param instance: Instance object as returned by DB layer.
|
|
This function should use the data there to guide
|
|
the creation of the new instance.
|
|
:param image_meta: image object returned by nova.image.glance that
|
|
defines the image from which to boot this instance
|
|
:param network_info:
|
|
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
|
|
:param block_device_info: Information about block devices to be
|
|
attached to the instance.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def destroy(self, instance, network_info, block_device_info=None):
|
|
"""Destroy (shutdown and delete) the specified instance.
|
|
|
|
If the instance is not found (for example if networking failed), this
|
|
function should still succeed. It's probably a good idea to log a
|
|
warning in that case.
|
|
|
|
:param instance: Instance object as returned by DB layer.
|
|
:param network_info:
|
|
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
|
|
:param block_device_info: Information about block devices that should
|
|
be detached from the instance.
|
|
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def reboot(self, instance, network_info, reboot_type):
|
|
"""Reboot the specified instance.
|
|
|
|
:param instance: Instance object as returned by DB layer.
|
|
:param network_info:
|
|
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
|
|
:param reboot_type: Either a HARD or SOFT reboot
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def snapshot_instance(self, context, instance_id, image_id):
|
|
raise NotImplementedError()
|
|
|
|
def get_console_pool_info(self, console_type):
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def get_console_output(self, instance):
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def get_vnc_console(self, instance):
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def get_diagnostics(self, instance):
|
|
"""Return data about VM diagnostics"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def get_all_bw_usage(self, start_time, stop_time=None):
|
|
"""Return bandwidth usage info for each interface on each
|
|
running VM"""
|
|
raise NotImplementedError()
|
|
|
|
def get_host_ip_addr(self):
|
|
"""
|
|
Retrieves the IP address of the dom0
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def attach_volume(self, connection_info, instance_name, mountpoint):
|
|
"""Attach the disk to the instance at mountpoint using info"""
|
|
raise NotImplementedError()
|
|
|
|
def detach_volume(self, connection_info, instance_name, mountpoint):
|
|
"""Detach the disk attached to the instance"""
|
|
raise NotImplementedError()
|
|
|
|
def compare_cpu(self, cpu_info):
|
|
"""Compares given cpu info against host
|
|
|
|
Before attempting to migrate a VM to this host,
|
|
compare_cpu is called to ensure that the VM will
|
|
actually run here.
|
|
|
|
:param cpu_info: (str) JSON structure describing the source CPU.
|
|
:returns: None if migration is acceptable
|
|
:raises: :py:class:`~nova.exception.InvalidCPUInfo` if migration
|
|
is not acceptable.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def migrate_disk_and_power_off(self, context, instance, dest,
|
|
instance_type):
|
|
"""
|
|
Transfers the disk of a running instance in multiple phases, turning
|
|
off the instance before the end.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def snapshot(self, context, instance, image_id):
|
|
"""
|
|
Snapshots the specified instance.
|
|
|
|
:param context: security context
|
|
:param instance: Instance object as returned by DB layer.
|
|
:param image_id: Reference to a pre-created image that will
|
|
hold the snapshot.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def finish_migration(self, context, migration, instance, disk_info,
|
|
network_info, image_meta, resize_instance):
|
|
"""Completes a resize, turning on the migrated instance
|
|
|
|
:param network_info:
|
|
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
|
|
:param image_meta: image object returned by nova.image.glance that
|
|
defines the image from which this instance
|
|
was created
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def confirm_migration(self, migration, instance, network_info):
|
|
"""Confirms a resize, destroying the source VM"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def finish_revert_migration(self, instance):
|
|
"""Finish reverting a resize, powering back on the instance"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def pause(self, instance):
|
|
"""Pause the specified instance."""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def unpause(self, instance):
|
|
"""Unpause paused VM instance"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def suspend(self, instance):
|
|
"""suspend the specified instance"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def resume(self, instance):
|
|
"""resume the specified instance"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def rescue(self, context, instance, network_info, image_meta):
|
|
"""Rescue the specified instance"""
|
|
raise NotImplementedError()
|
|
|
|
def unrescue(self, instance, network_info):
|
|
"""Unrescue the specified instance"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def power_off(self, instance):
|
|
"""Power off the specified instance."""
|
|
raise NotImplementedError()
|
|
|
|
def power_on(self, instance):
|
|
"""Power on the specified instance"""
|
|
raise NotImplementedError()
|
|
|
|
def update_available_resource(self, ctxt, host):
|
|
"""Updates compute manager resource info on ComputeNode table.
|
|
|
|
This method is called when nova-compute launches, and
|
|
whenever admin executes "nova-manage service update_resource".
|
|
|
|
:param ctxt: security context
|
|
:param host: hostname that compute manager is currently running
|
|
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def live_migration(self, ctxt, instance_ref, dest,
|
|
post_method, recover_method):
|
|
"""Spawning live_migration operation for distributing high-load.
|
|
|
|
:param ctxt: security context
|
|
:param instance_ref:
|
|
nova.db.sqlalchemy.models.Instance object
|
|
instance object that is migrated.
|
|
:param dest: destination host
|
|
:param post_method:
|
|
post operation method.
|
|
expected nova.compute.manager.post_live_migration.
|
|
:param recover_method:
|
|
recovery method when any exception occurs.
|
|
expected nova.compute.manager.recover_live_migration.
|
|
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def refresh_security_group_rules(self, security_group_id):
|
|
"""This method is called after a change to security groups.
|
|
|
|
All security groups and their associated rules live in the datastore,
|
|
and calling this method should apply the updated rules to instances
|
|
running the specified security group.
|
|
|
|
An error should be raised if the operation cannot complete.
|
|
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def refresh_security_group_members(self, security_group_id):
|
|
"""This method is called when a security group is added to an instance.
|
|
|
|
This message is sent to the virtualization drivers on hosts that are
|
|
running an instance that belongs to a security group that has a rule
|
|
that references the security group identified by `security_group_id`.
|
|
It is the responsibility of this method to make sure any rules
|
|
that authorize traffic flow with members of the security group are
|
|
updated and any new members can communicate, and any removed members
|
|
cannot.
|
|
|
|
Scenario:
|
|
* we are running on host 'H0' and we have an instance 'i-0'.
|
|
* instance 'i-0' is a member of security group 'speaks-b'
|
|
* group 'speaks-b' has an ingress rule that authorizes group 'b'
|
|
* another host 'H1' runs an instance 'i-1'
|
|
* instance 'i-1' is a member of security group 'b'
|
|
|
|
When 'i-1' launches or terminates we will receive the message
|
|
to update members of group 'b', at which time we will make
|
|
any changes needed to the rules for instance 'i-0' to allow
|
|
or deny traffic coming from 'i-1', depending on if it is being
|
|
added or removed from the group.
|
|
|
|
In this scenario, 'i-1' could just as easily have been running on our
|
|
host 'H0' and this method would still have been called. The point was
|
|
that this method isn't called on the host where instances of that
|
|
group are running (as is the case with
|
|
:method:`refresh_security_group_rules`) but is called where references
|
|
are made to authorizing those instances.
|
|
|
|
An error should be raised if the operation cannot complete.
|
|
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def refresh_provider_fw_rules(self):
|
|
"""This triggers a firewall update based on database changes.
|
|
|
|
When this is called, rules have either been added or removed from the
|
|
datastore. You can retrieve rules with
|
|
:method:`nova.db.provider_fw_rule_get_all`.
|
|
|
|
Provider rules take precedence over security group rules. If an IP
|
|
would be allowed by a security group ingress rule, but blocked by
|
|
a provider rule, then packets from the IP are dropped. This includes
|
|
intra-project traffic in the case of the allow_project_net_traffic
|
|
flag for the libvirt-derived classes.
|
|
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def reset_network(self, instance):
|
|
"""reset networking for specified instance"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
pass
|
|
|
|
def ensure_filtering_rules_for_instance(self, instance_ref, network_info):
|
|
"""Setting up filtering rules and waiting for its completion.
|
|
|
|
To migrate an instance, filtering rules to hypervisors
|
|
and firewalls are inevitable on destination host.
|
|
( Waiting only for filtering rules to hypervisor,
|
|
since filtering rules to firewall rules can be set faster).
|
|
|
|
Concretely, the below method must be called.
|
|
- setup_basic_filtering (for nova-basic, etc.)
|
|
- prepare_instance_filter(for nova-instance-instance-xxx, etc.)
|
|
|
|
to_xml may have to be called since it defines PROJNET, PROJMASK.
|
|
but libvirt migrates those value through migrateToURI(),
|
|
so , no need to be called.
|
|
|
|
Don't use thread for this method since migration should
|
|
not be started when setting-up filtering rules operations
|
|
are not completed.
|
|
|
|
:params instance_ref: nova.db.sqlalchemy.models.Instance object
|
|
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def unfilter_instance(self, instance, network_info):
|
|
"""Stop filtering instance"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def set_admin_password(self, context, instance_id, new_pass=None):
|
|
"""
|
|
Set the root password on the specified instance.
|
|
|
|
The first parameter is an instance of nova.compute.service.Instance,
|
|
and so the instance is being specified as instance.name. The second
|
|
parameter is the value of the new password.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def inject_file(self, instance, b64_path, b64_contents):
|
|
"""
|
|
Writes a file on the specified instance.
|
|
|
|
The first parameter is an instance of nova.compute.service.Instance,
|
|
and so the instance is being specified as instance.name. The second
|
|
parameter is the base64-encoded path to which the file is to be
|
|
written on the instance; the third is the contents of the file, also
|
|
base64-encoded.
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def agent_update(self, instance, url, md5hash):
|
|
"""
|
|
Update agent on the specified instance.
|
|
|
|
The first parameter is an instance of nova.compute.service.Instance,
|
|
and so the instance is being specified as instance.name. The second
|
|
parameter is the URL of the agent to be fetched and updated on the
|
|
instance; the third is the md5 hash of the file for verification
|
|
purposes.
|
|
"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def inject_network_info(self, instance, nw_info):
|
|
"""inject network info for specified instance"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
pass
|
|
|
|
def poll_rebooting_instances(self, timeout):
|
|
"""Poll for rebooting instances"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def poll_rescued_instances(self, timeout):
|
|
"""Poll for rescued instances"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def poll_unconfirmed_resizes(self, resize_confirm_window):
|
|
"""Poll for unconfirmed resizes"""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def host_power_action(self, host, action):
|
|
"""Reboots, shuts down or powers up the host."""
|
|
raise NotImplementedError()
|
|
|
|
def set_host_enabled(self, host, enabled):
|
|
"""Sets the specified host's ability to accept new instances."""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def plug_vifs(self, instance, network_info):
|
|
"""Plug VIFs into networks."""
|
|
# TODO(Vek): Need to pass context in for access to auth_token
|
|
raise NotImplementedError()
|
|
|
|
def unplug_vifs(self, instance, network_info):
|
|
"""Unplug VIFs from networks."""
|
|
raise NotImplementedError()
|
|
|
|
def update_host_status(self):
|
|
"""Refresh host stats"""
|
|
raise NotImplementedError()
|
|
|
|
def get_host_stats(self, refresh=False):
|
|
"""Return currently known host stats"""
|
|
raise NotImplementedError()
|
|
|
|
def list_disks(self, instance_name):
|
|
"""
|
|
Return the IDs of all the virtual disks attached to the specified
|
|
instance, as a list. These IDs are opaque to the caller (they are
|
|
only useful for giving back to this layer as a parameter to
|
|
disk_stats). These IDs only need to be unique for a given instance.
|
|
|
|
Note that this function takes an instance ID.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def list_interfaces(self, instance_name):
|
|
"""
|
|
Return the IDs of all the virtual network interfaces attached to the
|
|
specified instance, as a list. These IDs are opaque to the caller
|
|
(they are only useful for giving back to this layer as a parameter to
|
|
interface_stats). These IDs only need to be unique for a given
|
|
instance.
|
|
|
|
Note that this function takes an instance ID.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def resize(self, instance, flavor):
|
|
"""
|
|
Resizes/Migrates the specified instance.
|
|
|
|
The flavor parameter determines whether or not the instance RAM and
|
|
disk space are modified, and if so, to what size.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def block_stats(self, instance_name, disk_id):
|
|
"""
|
|
Return performance counters associated with the given disk_id on the
|
|
given instance_name. These are returned as [rd_req, rd_bytes, wr_req,
|
|
wr_bytes, errs], where rd indicates read, wr indicates write, req is
|
|
the total number of I/O requests made, bytes is the total number of
|
|
bytes transferred, and errs is the number of requests held up due to a
|
|
full pipeline.
|
|
|
|
All counters are long integers.
|
|
|
|
This method is optional. On some platforms (e.g. XenAPI) performance
|
|
statistics can be retrieved directly in aggregate form, without Nova
|
|
having to do the aggregation. On those platforms, this method is
|
|
unused.
|
|
|
|
Note that this function takes an instance ID.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def interface_stats(self, instance_name, iface_id):
|
|
"""
|
|
Return performance counters associated with the given iface_id on the
|
|
given instance_id. These are returned as [rx_bytes, rx_packets,
|
|
rx_errs, rx_drop, tx_bytes, tx_packets, tx_errs, tx_drop], where rx
|
|
indicates receive, tx indicates transmit, bytes and packets indicate
|
|
the total number of bytes or packets transferred, and errs and dropped
|
|
is the total number of packets failed / dropped.
|
|
|
|
All counters are long integers.
|
|
|
|
This method is optional. On some platforms (e.g. XenAPI) performance
|
|
statistics can be retrieved directly in aggregate form, without Nova
|
|
having to do the aggregation. On those platforms, this method is
|
|
unused.
|
|
|
|
Note that this function takes an instance ID.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def legacy_nwinfo(self):
|
|
"""
|
|
Indicate if the driver requires the legacy network_info format.
|
|
"""
|
|
# TODO(tr3buchet): update all subclasses and remove this
|
|
return True
|
|
|
|
def manage_image_cache(self, context):
|
|
"""
|
|
Manage the driver's local image cache.
|
|
|
|
Some drivers chose to cache images for instances on disk. This method
|
|
is an opportunity to do management of that cache which isn't directly
|
|
related to other calls into the driver. The prime example is to clean
|
|
the cache and remove images which are no longer of interest.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def get_volume_connector(self, instance):
|
|
"""
|
|
Get connector information for the instance for attaching to volumes.
|
|
|
|
Connector information is a dictionary representing the ip of the
|
|
machine that will be making the connection and and the name of the
|
|
iscsi initiator as follows:
|
|
{
|
|
'ip': ip,
|
|
'initiator': initiator,
|
|
}
|
|
"""
|
|
raise NotImplementedError()
|