Move all extensions from contrib dir
All extensions from novaclient.v2.contrib should be not be extensions in case of api version >=2.0;<=3.0 (historically, they are turned on by default for cli layer), so let's move it from contrib dir and turn on by default. Change-Id: I4ef4e44cf970947dad33110ce658a133e4f2893e
This commit is contained in:
parent
43bbe88ac8
commit
f834711d2f
@ -22,12 +22,9 @@ OpenStack Client interface. Handles the REST calls and responses.
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import functools
|
import functools
|
||||||
import glob
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import imp
|
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
@ -772,18 +769,14 @@ def _discover_via_python_path():
|
|||||||
|
|
||||||
|
|
||||||
def _discover_via_contrib_path(version):
|
def _discover_via_contrib_path(version):
|
||||||
module_path = os.path.dirname(os.path.abspath(__file__))
|
if version.ver_major == 2:
|
||||||
ext_path = os.path.join(module_path, "v%s" % version.ver_major, 'contrib')
|
modules = {"baremetal": "novaclient.v2.contrib.baremetal",
|
||||||
ext_glob = os.path.join(ext_path, "*.py")
|
"tenant_networks": "novaclient.v2.contrib.tenant_networks"}
|
||||||
|
|
||||||
for ext_path in glob.iglob(ext_glob):
|
for name, module_name in modules.items():
|
||||||
name = os.path.basename(ext_path)[:-3]
|
module_loader = pkgutil.get_loader(module_name)
|
||||||
|
module = module_loader.load_module(module_name)
|
||||||
if name in extensions_ignored_name:
|
yield name, module
|
||||||
continue
|
|
||||||
|
|
||||||
module = imp.load_source(name, ext_path)
|
|
||||||
yield name, module
|
|
||||||
|
|
||||||
|
|
||||||
def _discover_via_entry_points():
|
def _discover_via_entry_points():
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
# Copyright 2012 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from novaclient.tests.unit.v2 import fakes
|
|
||||||
from novaclient.v2 import client
|
|
||||||
|
|
||||||
FAKE_REQUEST_ID_LIST = fakes.FAKE_REQUEST_ID_LIST
|
|
||||||
FAKE_RESPONSE_HEADERS = fakes.FAKE_RESPONSE_HEADERS
|
|
||||||
|
|
||||||
|
|
||||||
class FakeClient(fakes.FakeClient):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
client.Client.__init__(self, 'username', 'password',
|
|
||||||
'project_id', 'auth_url',
|
|
||||||
extensions=kwargs.get('extensions'),
|
|
||||||
direct_use=False)
|
|
||||||
self.client = FakeHTTPClient(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class FakeHTTPClient(fakes.FakeHTTPClient):
|
|
||||||
def get_os_tenant_networks(self):
|
|
||||||
return (200, FAKE_RESPONSE_HEADERS, {
|
|
||||||
'networks': [{"label": "1", "cidr": "10.0.0.0/24",
|
|
||||||
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
|
||||||
'id': '1'}]})
|
|
||||||
|
|
||||||
def get_os_tenant_networks_1(self, **kw):
|
|
||||||
return (200, FAKE_RESPONSE_HEADERS, {
|
|
||||||
'network': {"label": "1", "cidr": "10.0.0.0/24",
|
|
||||||
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
|
||||||
'id': '1'}})
|
|
||||||
|
|
||||||
def post_os_tenant_networks(self, **kw):
|
|
||||||
return (201, FAKE_RESPONSE_HEADERS, {
|
|
||||||
'network': {"label": "1", "cidr": "10.0.0.0/24",
|
|
||||||
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
|
||||||
'id': '1'}})
|
|
||||||
|
|
||||||
def delete_os_tenant_networks_1(self, **kw):
|
|
||||||
return (204, FAKE_RESPONSE_HEADERS, None)
|
|
||||||
|
|
||||||
def get_os_baremetal_nodes(self, **kw):
|
|
||||||
return (
|
|
||||||
200, FAKE_RESPONSE_HEADERS, {
|
|
||||||
'nodes': [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"instance_uuid": None,
|
|
||||||
"interfaces": [],
|
|
||||||
"cpus": 2,
|
|
||||||
"local_gb": 10,
|
|
||||||
"memory_mb": 5,
|
|
||||||
"pm_address": "2.3.4.5",
|
|
||||||
"pm_user": "pmuser",
|
|
||||||
"pm_password": "pmpass",
|
|
||||||
"prov_mac_address": "aa:bb:cc:dd:ee:ff",
|
|
||||||
"prov_vlan_id": 1,
|
|
||||||
"service_host": "somehost",
|
|
||||||
"terminal_port": 8080,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_os_baremetal_nodes_1(self, **kw):
|
|
||||||
return (
|
|
||||||
200, FAKE_RESPONSE_HEADERS, {
|
|
||||||
'node': {
|
|
||||||
"id": 1,
|
|
||||||
"instance_uuid": None,
|
|
||||||
"pm_address": "1.2.3.4",
|
|
||||||
"interfaces": [],
|
|
||||||
"cpus": 2,
|
|
||||||
"local_gb": 10,
|
|
||||||
"memory_mb": 5,
|
|
||||||
"pm_user": "pmuser",
|
|
||||||
"pm_password": "pmpass",
|
|
||||||
"prov_mac_address": "aa:bb:cc:dd:ee:ff",
|
|
||||||
"prov_vlan_id": 1,
|
|
||||||
"service_host": "somehost",
|
|
||||||
"terminal_port": 8080,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
def post_os_assisted_volume_snapshots(self, **kw):
|
|
||||||
return (202, FAKE_RESPONSE_HEADERS,
|
|
||||||
{'snapshot': {'id': 'blah', 'volumeId': '1'}})
|
|
||||||
|
|
||||||
def delete_os_assisted_volume_snapshots_x(self, **kw):
|
|
||||||
return (202, FAKE_RESPONSE_HEADERS, {})
|
|
||||||
|
|
||||||
def post_os_server_external_events(self, **kw):
|
|
||||||
return (200, FAKE_RESPONSE_HEADERS, {
|
|
||||||
'events': [
|
|
||||||
{'name': 'test-event',
|
|
||||||
'status': 'completed',
|
|
||||||
'tag': 'tag',
|
|
||||||
'server_uuid': 'fake-uuid1'},
|
|
||||||
{'name': 'test-event',
|
|
||||||
'status': 'completed',
|
|
||||||
'tag': 'tag',
|
|
||||||
'server_uuid': 'fake-uuid2'}]})
|
|
@ -18,9 +18,10 @@ import warnings
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
from novaclient import api_versions
|
||||||
from novaclient import extension
|
from novaclient import extension
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.tests.unit.v2.contrib import fakes
|
from novaclient.tests.unit.v2 import fakes
|
||||||
from novaclient.v2.contrib import baremetal
|
from novaclient.v2.contrib import baremetal
|
||||||
|
|
||||||
|
|
||||||
@ -31,7 +32,8 @@ class BaremetalExtensionTest(utils.TestCase):
|
|||||||
extensions = [
|
extensions = [
|
||||||
extension.Extension(baremetal.__name__.split(".")[-1], baremetal),
|
extension.Extension(baremetal.__name__.split(".")[-1], baremetal),
|
||||||
]
|
]
|
||||||
self.cs = fakes.FakeClient(extensions=extensions)
|
self.cs = fakes.FakeClient(api_versions.APIVersion("2.0"),
|
||||||
|
extensions=extensions)
|
||||||
|
|
||||||
def test_list_nodes(self, mock_warn):
|
def test_list_nodes(self, mock_warn):
|
||||||
nl = self.cs.baremetal.list()
|
nl = self.cs.baremetal.list()
|
||||||
|
@ -13,9 +13,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from novaclient import api_versions
|
||||||
from novaclient import extension
|
from novaclient import extension
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.tests.unit.v2.contrib import fakes
|
from novaclient.tests.unit.v2 import fakes
|
||||||
from novaclient.v2.contrib import tenant_networks
|
from novaclient.v2.contrib import tenant_networks
|
||||||
|
|
||||||
|
|
||||||
@ -27,7 +28,8 @@ class TenantNetworkExtensionTests(utils.TestCase):
|
|||||||
extension.Extension(tenant_networks.__name__.split(".")[-1],
|
extension.Extension(tenant_networks.__name__.split(".")[-1],
|
||||||
tenant_networks),
|
tenant_networks),
|
||||||
]
|
]
|
||||||
self.cs = fakes.FakeClient(extensions=extensions)
|
self.cs = fakes.FakeClient(api_versions.APIVersion("2.0"),
|
||||||
|
extensions=extensions)
|
||||||
|
|
||||||
def test_list_tenant_networks(self):
|
def test_list_tenant_networks(self):
|
||||||
nets = self.cs.tenant_networks.list()
|
nets = self.cs.tenant_networks.list()
|
||||||
|
@ -60,9 +60,8 @@ class FakeClient(fakes.FakeClient, client.Client):
|
|||||||
client.Client.__init__(self, 'username', 'password',
|
client.Client.__init__(self, 'username', 'password',
|
||||||
'project_id', 'auth_url',
|
'project_id', 'auth_url',
|
||||||
extensions=kwargs.get('extensions'),
|
extensions=kwargs.get('extensions'),
|
||||||
direct_use=False)
|
direct_use=False, api_version=api_version)
|
||||||
kwargs["api_version"] = api_version
|
self.client = FakeHTTPClient(api_version=api_version, **kwargs)
|
||||||
self.client = FakeHTTPClient(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class FakeHTTPClient(base_client.HTTPClient):
|
class FakeHTTPClient(base_client.HTTPClient):
|
||||||
@ -2142,11 +2141,6 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||||||
|
|
||||||
return (200, FAKE_RESPONSE_HEADERS, migrations)
|
return (200, FAKE_RESPONSE_HEADERS, migrations)
|
||||||
|
|
||||||
def post_os_server_external_events(self, **kw):
|
|
||||||
return (200, {}, {'events': [
|
|
||||||
{'name': 'network-changed',
|
|
||||||
'server_uuid': '1234'}]})
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Server Groups
|
# Server Groups
|
||||||
#
|
#
|
||||||
@ -2242,6 +2236,90 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||||||
def delete_servers_1234_tags(self, **kw):
|
def delete_servers_1234_tags(self, **kw):
|
||||||
return (204, {}, None)
|
return (204, {}, None)
|
||||||
|
|
||||||
|
def get_os_tenant_networks(self):
|
||||||
|
return (200, FAKE_RESPONSE_HEADERS, {
|
||||||
|
'networks': [{"label": "1", "cidr": "10.0.0.0/24",
|
||||||
|
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
||||||
|
'id': '1'}]})
|
||||||
|
|
||||||
|
def get_os_tenant_networks_1(self, **kw):
|
||||||
|
return (200, FAKE_RESPONSE_HEADERS, {
|
||||||
|
'network': {"label": "1", "cidr": "10.0.0.0/24",
|
||||||
|
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
||||||
|
'id': '1'}})
|
||||||
|
|
||||||
|
def post_os_tenant_networks(self, **kw):
|
||||||
|
return (201, FAKE_RESPONSE_HEADERS, {
|
||||||
|
'network': {"label": "1", "cidr": "10.0.0.0/24",
|
||||||
|
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
||||||
|
'id': '1'}})
|
||||||
|
|
||||||
|
def delete_os_tenant_networks_1(self, **kw):
|
||||||
|
return (204, FAKE_RESPONSE_HEADERS, None)
|
||||||
|
|
||||||
|
def get_os_baremetal_nodes(self, **kw):
|
||||||
|
return (
|
||||||
|
200, FAKE_RESPONSE_HEADERS, {
|
||||||
|
'nodes': [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"instance_uuid": None,
|
||||||
|
"interfaces": [],
|
||||||
|
"cpus": 2,
|
||||||
|
"local_gb": 10,
|
||||||
|
"memory_mb": 5,
|
||||||
|
"pm_address": "2.3.4.5",
|
||||||
|
"pm_user": "pmuser",
|
||||||
|
"pm_password": "pmpass",
|
||||||
|
"prov_mac_address": "aa:bb:cc:dd:ee:ff",
|
||||||
|
"prov_vlan_id": 1,
|
||||||
|
"service_host": "somehost",
|
||||||
|
"terminal_port": 8080,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_os_baremetal_nodes_1(self, **kw):
|
||||||
|
return (
|
||||||
|
200, FAKE_RESPONSE_HEADERS, {
|
||||||
|
'node': {
|
||||||
|
"id": 1,
|
||||||
|
"instance_uuid": None,
|
||||||
|
"pm_address": "1.2.3.4",
|
||||||
|
"interfaces": [],
|
||||||
|
"cpus": 2,
|
||||||
|
"local_gb": 10,
|
||||||
|
"memory_mb": 5,
|
||||||
|
"pm_user": "pmuser",
|
||||||
|
"pm_password": "pmpass",
|
||||||
|
"prov_mac_address": "aa:bb:cc:dd:ee:ff",
|
||||||
|
"prov_vlan_id": 1,
|
||||||
|
"service_host": "somehost",
|
||||||
|
"terminal_port": 8080,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def post_os_assisted_volume_snapshots(self, **kw):
|
||||||
|
return (202, FAKE_RESPONSE_HEADERS,
|
||||||
|
{'snapshot': {'id': 'blah', 'volumeId': '1'}})
|
||||||
|
|
||||||
|
def delete_os_assisted_volume_snapshots_x(self, **kw):
|
||||||
|
return (202, FAKE_RESPONSE_HEADERS, {})
|
||||||
|
|
||||||
|
def post_os_server_external_events(self, **kw):
|
||||||
|
return (200, FAKE_RESPONSE_HEADERS, {
|
||||||
|
'events': [
|
||||||
|
{'name': 'test-event',
|
||||||
|
'status': 'completed',
|
||||||
|
'tag': 'tag',
|
||||||
|
'server_uuid': 'fake-uuid1'},
|
||||||
|
{'name': 'test-event',
|
||||||
|
'status': 'completed',
|
||||||
|
'tag': 'tag',
|
||||||
|
'server_uuid': 'fake-uuid2'}]})
|
||||||
|
|
||||||
|
|
||||||
class FakeSessionClient(fakes.FakeClient, client.Client):
|
class FakeSessionClient(fakes.FakeClient, client.Client):
|
||||||
|
|
||||||
@ -2249,9 +2327,8 @@ class FakeSessionClient(fakes.FakeClient, client.Client):
|
|||||||
client.Client.__init__(self, 'username', 'password',
|
client.Client.__init__(self, 'username', 'password',
|
||||||
'project_id', 'auth_url',
|
'project_id', 'auth_url',
|
||||||
extensions=kwargs.get('extensions'),
|
extensions=kwargs.get('extensions'),
|
||||||
direct_use=False)
|
direct_use=False, api_version=api_version)
|
||||||
kwargs["api_version"] = api_version
|
self.client = FakeSessionMockClient(api_version=api_version, **kwargs)
|
||||||
self.client = FakeSessionMockClient(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class FakeSessionMockClient(base_client.SessionClient, FakeHTTPClient):
|
class FakeSessionMockClient(base_client.SessionClient, FakeHTTPClient):
|
||||||
|
@ -16,20 +16,15 @@
|
|||||||
Assisted volume snapshots - to be used by Cinder and not end users.
|
Assisted volume snapshots - to be used by Cinder and not end users.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from novaclient import extension
|
from novaclient import api_versions
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.tests.unit.v2.contrib import fakes
|
from novaclient.tests.unit.v2 import fakes
|
||||||
from novaclient.v2.contrib import assisted_volume_snapshots as assisted_snaps
|
|
||||||
|
|
||||||
|
|
||||||
class AssistedVolumeSnapshotsTestCase(utils.TestCase):
|
class AssistedVolumeSnapshotsTestCase(utils.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(AssistedVolumeSnapshotsTestCase, self).setUp()
|
super(AssistedVolumeSnapshotsTestCase, self).setUp()
|
||||||
extensions = [
|
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||||
extension.Extension(assisted_snaps.__name__.split(".")[-1],
|
|
||||||
assisted_snaps),
|
|
||||||
]
|
|
||||||
self.cs = fakes.FakeClient(extensions=extensions)
|
|
||||||
|
|
||||||
def test_create_snap(self):
|
def test_create_snap(self):
|
||||||
vs = self.cs.assisted_volume_snapshots.create('1', {})
|
vs = self.cs.assisted_volume_snapshots.create('1', {})
|
@ -18,9 +18,13 @@ from keystoneauth1 import fixture
|
|||||||
import mock
|
import mock
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from novaclient import client
|
||||||
from novaclient import exceptions
|
from novaclient import exceptions
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.v2 import client
|
|
||||||
|
|
||||||
|
def Client(*args, **kwargs):
|
||||||
|
return client.Client("2", *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||||
@ -35,9 +39,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||||||
return resp
|
return resp
|
||||||
|
|
||||||
def test_authenticate_success(self):
|
def test_authenticate_success(self):
|
||||||
cs = client.Client("username", "password", "project_id",
|
cs = Client("username", "password", "project_id", utils.AUTH_URL_V2,
|
||||||
utils.AUTH_URL_V2, service_type='compute',
|
service_type='compute')
|
||||||
direct_use=False)
|
|
||||||
resp = self.get_token()
|
resp = self.get_token()
|
||||||
|
|
||||||
auth_response = utils.TestResponse({
|
auth_response = utils.TestResponse({
|
||||||
@ -83,8 +86,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
def test_authenticate_failure(self):
|
def test_authenticate_failure(self):
|
||||||
cs = client.Client("username", "password", "project_id",
|
cs = Client("username", "password", "project_id", utils.AUTH_URL_V2)
|
||||||
utils.AUTH_URL_V2, direct_use=False)
|
|
||||||
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
||||||
auth_response = utils.TestResponse({
|
auth_response = utils.TestResponse({
|
||||||
"status_code": 401,
|
"status_code": 401,
|
||||||
@ -100,9 +102,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
def test_v1_auth_redirect(self):
|
def test_v1_auth_redirect(self):
|
||||||
cs = client.Client("username", "password", "project_id",
|
cs = Client("username", "password", "project_id", utils.AUTH_URL_V1,
|
||||||
utils.AUTH_URL_V1, service_type='compute',
|
service_type='compute')
|
||||||
direct_use=False)
|
|
||||||
dict_correct_response = self.get_token()
|
dict_correct_response = self.get_token()
|
||||||
correct_response = json.dumps(dict_correct_response)
|
correct_response = json.dumps(dict_correct_response)
|
||||||
dict_responses = [
|
dict_responses = [
|
||||||
@ -166,9 +167,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
def test_v2_auth_redirect(self):
|
def test_v2_auth_redirect(self):
|
||||||
cs = client.Client("username", "password", "project_id",
|
cs = Client("username", "password", "project_id", utils.AUTH_URL_V2,
|
||||||
utils.AUTH_URL_V2, service_type='compute',
|
service_type='compute')
|
||||||
direct_use=False)
|
|
||||||
dict_correct_response = self.get_token()
|
dict_correct_response = self.get_token()
|
||||||
correct_response = json.dumps(dict_correct_response)
|
correct_response = json.dumps(dict_correct_response)
|
||||||
dict_responses = [
|
dict_responses = [
|
||||||
@ -232,9 +232,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
def test_ambiguous_endpoints(self):
|
def test_ambiguous_endpoints(self):
|
||||||
cs = client.Client("username", "password", "project_id",
|
cs = Client("username", "password", "project_id", utils.AUTH_URL_V2,
|
||||||
utils.AUTH_URL_V2, service_type='compute',
|
service_type='compute')
|
||||||
direct_use=False)
|
|
||||||
resp = self.get_token()
|
resp = self.get_token()
|
||||||
|
|
||||||
# duplicate existing service
|
# duplicate existing service
|
||||||
@ -256,9 +255,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
def test_authenticate_with_token_success(self):
|
def test_authenticate_with_token_success(self):
|
||||||
cs = client.Client("username", None, "project_id",
|
cs = Client("username", None, "project_id", utils.AUTH_URL_V2,
|
||||||
utils.AUTH_URL_V2, service_type='compute',
|
service_type='compute')
|
||||||
direct_use=False)
|
|
||||||
cs.client.auth_token = "FAKE_ID"
|
cs.client.auth_token = "FAKE_ID"
|
||||||
resp = self.get_token(token_id="FAKE_ID")
|
resp = self.get_token(token_id="FAKE_ID")
|
||||||
auth_response = utils.TestResponse({
|
auth_response = utils.TestResponse({
|
||||||
@ -300,8 +298,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||||||
self.assertEqual(cs.client.auth_token, token_id)
|
self.assertEqual(cs.client.auth_token, token_id)
|
||||||
|
|
||||||
def test_authenticate_with_token_failure(self):
|
def test_authenticate_with_token_failure(self):
|
||||||
cs = client.Client("username", None, "project_id", utils.AUTH_URL_V2,
|
cs = Client("username", None, "project_id", utils.AUTH_URL_V2)
|
||||||
direct_use=False)
|
|
||||||
cs.client.auth_token = "FAKE_ID"
|
cs.client.auth_token = "FAKE_ID"
|
||||||
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
||||||
auth_response = utils.TestResponse({
|
auth_response = utils.TestResponse({
|
||||||
@ -317,8 +314,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||||||
|
|
||||||
class AuthenticationTests(utils.TestCase):
|
class AuthenticationTests(utils.TestCase):
|
||||||
def test_authenticate_success(self):
|
def test_authenticate_success(self):
|
||||||
cs = client.Client("username", "password",
|
cs = Client("username", "password", "project_id", utils.AUTH_URL)
|
||||||
"project_id", utils.AUTH_URL, direct_use=False)
|
|
||||||
management_url = 'https://localhost/v1.1/443470'
|
management_url = 'https://localhost/v1.1/443470'
|
||||||
auth_response = utils.TestResponse({
|
auth_response = utils.TestResponse({
|
||||||
'status_code': 204,
|
'status_code': 204,
|
||||||
@ -353,8 +349,7 @@ class AuthenticationTests(utils.TestCase):
|
|||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
def test_authenticate_failure(self):
|
def test_authenticate_failure(self):
|
||||||
cs = client.Client("username", "password",
|
cs = Client("username", "password", "project_id", utils.AUTH_URL)
|
||||||
"project_id", utils.AUTH_URL, direct_use=False)
|
|
||||||
auth_response = utils.TestResponse({'status_code': 401})
|
auth_response = utils.TestResponse({'status_code': 401})
|
||||||
mock_request = mock.Mock(return_value=(auth_response))
|
mock_request = mock.Mock(return_value=(auth_response))
|
||||||
|
|
||||||
@ -365,8 +360,8 @@ class AuthenticationTests(utils.TestCase):
|
|||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
def test_auth_automatic(self):
|
def test_auth_automatic(self):
|
||||||
cs = client.Client("username", "password",
|
cs = Client("username", "password", "project_id", utils.AUTH_URL,
|
||||||
"project_id", utils.AUTH_URL, direct_use=False)
|
direct_use=False)
|
||||||
http_client = cs.client
|
http_client = cs.client
|
||||||
http_client.management_url = ''
|
http_client.management_url = ''
|
||||||
http_client.get_service_url = mock.Mock(return_value='')
|
http_client.get_service_url = mock.Mock(return_value='')
|
||||||
@ -382,8 +377,7 @@ class AuthenticationTests(utils.TestCase):
|
|||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
def test_auth_manual(self):
|
def test_auth_manual(self):
|
||||||
cs = client.Client("username", "password",
|
cs = Client("username", "password", "project_id", utils.AUTH_URL)
|
||||||
"project_id", utils.AUTH_URL, direct_use=False)
|
|
||||||
|
|
||||||
@mock.patch.object(cs.client, 'authenticate')
|
@mock.patch.object(cs.client, 'authenticate')
|
||||||
def test_auth_call(m):
|
def test_auth_call(m):
|
||||||
|
@ -13,20 +13,15 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient import extension
|
from novaclient import api_versions
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.tests.unit.v2.contrib import fakes
|
from novaclient.tests.unit.v2 import fakes
|
||||||
from novaclient.v2.contrib import cells
|
|
||||||
|
|
||||||
|
|
||||||
class CellsExtensionTests(utils.TestCase):
|
class CellsExtensionTests(utils.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(CellsExtensionTests, self).setUp()
|
super(CellsExtensionTests, self).setUp()
|
||||||
extensions = [
|
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||||
extension.Extension(cells.__name__.split(".")[-1],
|
|
||||||
cells),
|
|
||||||
]
|
|
||||||
self.cs = fakes.FakeClient(extensions=extensions)
|
|
||||||
|
|
||||||
def test_get_cells(self):
|
def test_get_cells(self):
|
||||||
cell_name = 'child_cell'
|
cell_name = 'child_cell'
|
@ -14,6 +14,7 @@ import uuid
|
|||||||
|
|
||||||
from keystoneauth1 import session
|
from keystoneauth1 import session
|
||||||
|
|
||||||
|
from novaclient import api_versions
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.v2 import client
|
from novaclient.v2 import client
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ class ClientTest(utils.TestCase):
|
|||||||
|
|
||||||
s = session.Session()
|
s = session.Session()
|
||||||
c = client.Client(session=s,
|
c = client.Client(session=s,
|
||||||
|
api_version=api_versions.APIVersion("2.0"),
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
endpoint_override=endpoint_override,
|
endpoint_override=endpoint_override,
|
||||||
direct_use=False)
|
direct_use=False)
|
||||||
@ -40,6 +42,7 @@ class ClientTest(utils.TestCase):
|
|||||||
|
|
||||||
s = session.Session()
|
s = session.Session()
|
||||||
c = client.Client(session=s,
|
c = client.Client(session=s,
|
||||||
|
api_version=api_versions.APIVersion("2.0"),
|
||||||
interface=interface,
|
interface=interface,
|
||||||
endpoint_type=endpoint_type,
|
endpoint_type=endpoint_type,
|
||||||
direct_use=False)
|
direct_use=False)
|
||||||
|
@ -13,20 +13,15 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient import extension
|
from novaclient import api_versions
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.tests.unit.v2.contrib import fakes
|
from novaclient.tests.unit.v2 import fakes
|
||||||
from novaclient.v2.contrib import instance_action
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceActionExtensionTests(utils.TestCase):
|
class InstanceActionExtensionTests(utils.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(InstanceActionExtensionTests, self).setUp()
|
super(InstanceActionExtensionTests, self).setUp()
|
||||||
extensions = [
|
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||||
extension.Extension(instance_action.__name__.split(".")[-1],
|
|
||||||
instance_action),
|
|
||||||
]
|
|
||||||
self.cs = fakes.FakeClient(extensions=extensions)
|
|
||||||
|
|
||||||
def test_list_instance_actions(self):
|
def test_list_instance_actions(self):
|
||||||
server_uuid = '1234'
|
server_uuid = '1234'
|
@ -12,21 +12,14 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient import api_versions
|
from novaclient import api_versions
|
||||||
from novaclient import extension
|
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.tests.unit.v2 import fakes
|
from novaclient.tests.unit.v2 import fakes
|
||||||
from novaclient.v2.contrib import list_extensions
|
|
||||||
|
|
||||||
|
|
||||||
class ListExtensionsTests(utils.TestCase):
|
class ListExtensionsTests(utils.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(ListExtensionsTests, self).setUp()
|
super(ListExtensionsTests, self).setUp()
|
||||||
extensions = [
|
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||||
extension.Extension(list_extensions.__name__.split(".")[-1],
|
|
||||||
list_extensions),
|
|
||||||
]
|
|
||||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.0"),
|
|
||||||
extensions=extensions)
|
|
||||||
|
|
||||||
def test_list_extensions(self):
|
def test_list_extensions(self):
|
||||||
all_exts = self.cs.list_extensions.show_all()
|
all_exts = self.cs.list_extensions.show_all()
|
@ -11,21 +11,15 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient import api_versions
|
from novaclient import api_versions
|
||||||
from novaclient import extension
|
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.tests.unit.v2 import fakes
|
from novaclient.tests.unit.v2 import fakes
|
||||||
from novaclient.v2.contrib import migrations
|
from novaclient.v2 import migrations
|
||||||
|
|
||||||
|
|
||||||
class MigrationsTest(utils.TestCase):
|
class MigrationsTest(utils.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(MigrationsTest, self).setUp()
|
super(MigrationsTest, self).setUp()
|
||||||
self.extensions = [
|
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||||
extension.Extension(migrations.__name__.split(".")[-1],
|
|
||||||
migrations),
|
|
||||||
]
|
|
||||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.0"),
|
|
||||||
extensions=self.extensions)
|
|
||||||
|
|
||||||
def test_list_migrations(self):
|
def test_list_migrations(self):
|
||||||
ml = self.cs.migrations.list()
|
ml = self.cs.migrations.list()
|
||||||
@ -36,8 +30,7 @@ class MigrationsTest(utils.TestCase):
|
|||||||
self.assertRaises(AttributeError, getattr, m, "migration_type")
|
self.assertRaises(AttributeError, getattr, m, "migration_type")
|
||||||
|
|
||||||
def test_list_migrations_v223(self):
|
def test_list_migrations_v223(self):
|
||||||
cs = fakes.FakeClient(extensions=self.extensions,
|
cs = fakes.FakeClient(api_versions.APIVersion("2.23"))
|
||||||
api_version=api_versions.APIVersion("2.23"))
|
|
||||||
ml = cs.migrations.list()
|
ml = cs.migrations.list()
|
||||||
self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
cs.assert_called('GET', '/os-migrations')
|
cs.assert_called('GET', '/os-migrations')
|
@ -16,20 +16,15 @@
|
|||||||
External event triggering for servers, not to be used by users.
|
External event triggering for servers, not to be used by users.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from novaclient import extension
|
from novaclient import api_versions
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
from novaclient.tests.unit.v2.contrib import fakes
|
from novaclient.tests.unit.v2 import fakes
|
||||||
from novaclient.v2.contrib import server_external_events as ext_events
|
|
||||||
|
|
||||||
|
|
||||||
class ServerExternalEventsTestCase(utils.TestCase):
|
class ServerExternalEventsTestCase(utils.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(ServerExternalEventsTestCase, self).setUp()
|
super(ServerExternalEventsTestCase, self).setUp()
|
||||||
extensions = [
|
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||||
extension.Extension(ext_events.__name__.split(".")[-1],
|
|
||||||
ext_events),
|
|
||||||
]
|
|
||||||
self.cs = fakes.FakeClient(extensions=extensions)
|
|
||||||
|
|
||||||
def test_external_event(self):
|
def test_external_event(self):
|
||||||
events = [{'server_uuid': 'fake-uuid1',
|
events = [{'server_uuid': 'fake-uuid1',
|
@ -77,8 +77,7 @@ class ShellTest(utils.TestCase):
|
|||||||
self.shell = self.useFixture(ShellFixture()).shell
|
self.shell = self.useFixture(ShellFixture()).shell
|
||||||
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
self.useFixture(fixtures.MonkeyPatch(
|
||||||
'novaclient.client.Client',
|
'novaclient.client.Client', fakes.FakeClient))
|
||||||
lambda *args, **kwargs: fakes.FakeClient(*args, **kwargs)))
|
|
||||||
|
|
||||||
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
||||||
@mock.patch('sys.stderr', new_callable=six.StringIO)
|
@mock.patch('sys.stderr', new_callable=six.StringIO)
|
||||||
@ -3150,9 +3149,9 @@ class ShellWithSessionClientTest(ShellTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Run before each test."""
|
"""Run before each test."""
|
||||||
super(ShellWithSessionClientTest, self).setUp()
|
super(ShellWithSessionClientTest, self).setUp()
|
||||||
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
self.useFixture(fixtures.MonkeyPatch(
|
||||||
'novaclient.client.Client',
|
'novaclient.client.Client', fakes.FakeSessionClient))
|
||||||
lambda *args, **kwargs: fakes.FakeSessionClient(*args, **kwargs)))
|
|
||||||
|
|
||||||
|
|
||||||
class GetSecgroupTest(utils.TestCase):
|
class GetSecgroupTest(utils.TestCase):
|
||||||
|
54
novaclient/v2/assisted_volume_snapshots.py
Normal file
54
novaclient/v2/assisted_volume_snapshots.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Copyright (C) 2013, Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Assisted volume snapshots - to be used by Cinder and not end users.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from novaclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class Snapshot(base.Resource):
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Snapshot: %s>" % self.id
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"""
|
||||||
|
Delete this snapshot.
|
||||||
|
|
||||||
|
:returns: An instance of novaclient.base.TupleWithMeta
|
||||||
|
"""
|
||||||
|
return self.manager.delete(self)
|
||||||
|
|
||||||
|
|
||||||
|
class AssistedSnapshotManager(base.Manager):
|
||||||
|
resource_class = Snapshot
|
||||||
|
|
||||||
|
def create(self, volume_id, create_info):
|
||||||
|
body = {'snapshot': {'volume_id': volume_id,
|
||||||
|
'create_info': create_info}}
|
||||||
|
return self._create('/os-assisted-volume-snapshots', body, 'snapshot')
|
||||||
|
|
||||||
|
def delete(self, snapshot, delete_info):
|
||||||
|
"""
|
||||||
|
Delete a specified assisted volume snapshot.
|
||||||
|
|
||||||
|
:param snapshot: an assisted volume snapshot to delete
|
||||||
|
:param delete_info: Information for snapshot deletion
|
||||||
|
:returns: An instance of novaclient.base.TupleWithMeta
|
||||||
|
"""
|
||||||
|
return self._delete("/os-assisted-volume-snapshots/%s?delete_info=%s" %
|
||||||
|
(base.getid(snapshot), json.dumps(delete_info)))
|
44
novaclient/v2/cells.py
Normal file
44
novaclient/v2/cells.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Copyright 2013 Rackspace Hosting
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from novaclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class Cell(base.Resource):
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Cell: %s>" % self.name
|
||||||
|
|
||||||
|
|
||||||
|
class CellsManager(base.Manager):
|
||||||
|
resource_class = Cell
|
||||||
|
|
||||||
|
def get(self, cell_name):
|
||||||
|
"""
|
||||||
|
Get a cell.
|
||||||
|
|
||||||
|
:param cell_name: Name of the :class:`Cell` to get.
|
||||||
|
:rtype: :class:`Cell`
|
||||||
|
"""
|
||||||
|
return self._get("/os-cells/%s" % cell_name, "cell")
|
||||||
|
|
||||||
|
def capacities(self, cell_name=None):
|
||||||
|
"""
|
||||||
|
Get capacities for a cell.
|
||||||
|
|
||||||
|
:param cell_name: Name of the :class:`Cell` to get capacities for.
|
||||||
|
:rtype: :class:`Cell`
|
||||||
|
"""
|
||||||
|
path = ["%s/capacities" % cell_name, "capacities"][cell_name is None]
|
||||||
|
return self._get("/os-cells/%s" % path, "cell")
|
@ -22,9 +22,12 @@ from novaclient import exceptions
|
|||||||
from novaclient.i18n import _LE
|
from novaclient.i18n import _LE
|
||||||
from novaclient.v2 import agents
|
from novaclient.v2 import agents
|
||||||
from novaclient.v2 import aggregates
|
from novaclient.v2 import aggregates
|
||||||
|
from novaclient.v2 import assisted_volume_snapshots
|
||||||
from novaclient.v2 import availability_zones
|
from novaclient.v2 import availability_zones
|
||||||
|
from novaclient.v2 import cells
|
||||||
from novaclient.v2 import certs
|
from novaclient.v2 import certs
|
||||||
from novaclient.v2 import cloudpipe
|
from novaclient.v2 import cloudpipe
|
||||||
|
from novaclient.v2 import contrib
|
||||||
from novaclient.v2 import fixed_ips
|
from novaclient.v2 import fixed_ips
|
||||||
from novaclient.v2 import flavor_access
|
from novaclient.v2 import flavor_access
|
||||||
from novaclient.v2 import flavors
|
from novaclient.v2 import flavors
|
||||||
@ -36,14 +39,18 @@ from novaclient.v2 import fping
|
|||||||
from novaclient.v2 import hosts
|
from novaclient.v2 import hosts
|
||||||
from novaclient.v2 import hypervisors
|
from novaclient.v2 import hypervisors
|
||||||
from novaclient.v2 import images
|
from novaclient.v2 import images
|
||||||
|
from novaclient.v2 import instance_action
|
||||||
from novaclient.v2 import keypairs
|
from novaclient.v2 import keypairs
|
||||||
from novaclient.v2 import limits
|
from novaclient.v2 import limits
|
||||||
|
from novaclient.v2 import list_extensions
|
||||||
|
from novaclient.v2 import migrations
|
||||||
from novaclient.v2 import networks
|
from novaclient.v2 import networks
|
||||||
from novaclient.v2 import quota_classes
|
from novaclient.v2 import quota_classes
|
||||||
from novaclient.v2 import quotas
|
from novaclient.v2 import quotas
|
||||||
from novaclient.v2 import security_group_default_rules
|
from novaclient.v2 import security_group_default_rules
|
||||||
from novaclient.v2 import security_group_rules
|
from novaclient.v2 import security_group_rules
|
||||||
from novaclient.v2 import security_groups
|
from novaclient.v2 import security_groups
|
||||||
|
from novaclient.v2 import server_external_events
|
||||||
from novaclient.v2 import server_groups
|
from novaclient.v2 import server_groups
|
||||||
from novaclient.v2 import server_migrations
|
from novaclient.v2 import server_migrations
|
||||||
from novaclient.v2 import servers
|
from novaclient.v2 import servers
|
||||||
@ -172,16 +179,36 @@ class Client(object):
|
|||||||
self.server_migrations = \
|
self.server_migrations = \
|
||||||
server_migrations.ServerMigrationsManager(self)
|
server_migrations.ServerMigrationsManager(self)
|
||||||
|
|
||||||
|
# V2.0 extensions:
|
||||||
|
# NOTE(andreykurilin): baremetal and tenant_networks extensions are
|
||||||
|
# deprecated now, which is why they are not initialized by default.
|
||||||
|
self.assisted_volume_snapshots = \
|
||||||
|
assisted_volume_snapshots.AssistedSnapshotManager(self)
|
||||||
|
self.cells = cells.CellsManager(self)
|
||||||
|
self.instance_action = instance_action.InstanceActionManager(self)
|
||||||
|
self.list_extensions = list_extensions.ListExtManager(self)
|
||||||
|
self.migrations = migrations.MigrationManager(self)
|
||||||
|
self.server_external_events = \
|
||||||
|
server_external_events.ServerExternalEventManager(self)
|
||||||
|
|
||||||
|
self.logger = logger or logging.getLogger(__name__)
|
||||||
|
|
||||||
# Add in any extensions...
|
# Add in any extensions...
|
||||||
if extensions:
|
if extensions:
|
||||||
for extension in extensions:
|
for extension in extensions:
|
||||||
|
# do not import extensions from contrib directory twice.
|
||||||
|
if extension.name in contrib.V2_0_EXTENSIONS:
|
||||||
|
# NOTE(andreykurilin): this message looks more like
|
||||||
|
# warning or note, but it is not critical, so let's do
|
||||||
|
# not flood "warning" logging level and use just debug..
|
||||||
|
self.logger.debug("Nova 2.0 extenstion '%s' is auto-loaded"
|
||||||
|
" by default. You do not need to specify"
|
||||||
|
" it manually.", extension.name)
|
||||||
|
continue
|
||||||
if extension.manager_class:
|
if extension.manager_class:
|
||||||
setattr(self, extension.name,
|
setattr(self, extension.name,
|
||||||
extension.manager_class(self))
|
extension.manager_class(self))
|
||||||
|
|
||||||
if not logger:
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
self.client = client._construct_http_client(
|
self.client = client._construct_http_client(
|
||||||
username=username,
|
username=username,
|
||||||
password=password,
|
password=password,
|
||||||
@ -208,7 +235,7 @@ class Client(object):
|
|||||||
session=session,
|
session=session,
|
||||||
auth=auth,
|
auth=auth,
|
||||||
api_version=api_version,
|
api_version=api_version,
|
||||||
logger=logger,
|
logger=self.logger,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
from novaclient.i18n import _LW
|
||||||
|
|
||||||
|
# NOTE(andreykurilin): "baremetal" and "tenant_networks" extensions excluded
|
||||||
|
# here deliberately. They were deprecated separately from deprecation
|
||||||
|
# extension mechanism and I prefer to not auto-load them by default
|
||||||
|
# (V2_0_EXTENSIONS is designed for such behaviour).
|
||||||
|
V2_0_EXTENSIONS = {
|
||||||
|
'assisted_volume_snapshots':
|
||||||
|
'novaclient.v2.assisted_volume_snapshots',
|
||||||
|
'cells': 'novaclient.v2.cells',
|
||||||
|
'instance_action': 'novaclient.v2.instance_action',
|
||||||
|
'list_extensions': 'novaclient.v2.list_extensions',
|
||||||
|
'migrations': 'novaclient.v2.migrations',
|
||||||
|
'server_external_events': 'novaclient.v2.server_external_events',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def warn(alternative=True):
|
||||||
|
"""Prints warning msg for contrib modules."""
|
||||||
|
frm = inspect.stack()[1]
|
||||||
|
module_name = inspect.getmodule(frm[0]).__name__
|
||||||
|
if module_name.startswith("novaclient.v2.contrib."):
|
||||||
|
msg = (_LW("Module `%s` is deprecated as of OpenStack Ocata") %
|
||||||
|
module_name)
|
||||||
|
if alternative:
|
||||||
|
new_module_name = module_name.replace("contrib.", "")
|
||||||
|
msg += _LW(" in favor of `%s`") % new_module_name
|
||||||
|
|
||||||
|
msg += (_LW(" and will be removed after OpenStack Pike."))
|
||||||
|
|
||||||
|
if not alternative:
|
||||||
|
msg += _LW(" All shell commands were moved to "
|
||||||
|
"`novaclient.v2.shell` and will be automatically "
|
||||||
|
"loaded.")
|
||||||
|
|
||||||
|
warnings.warn(msg)
|
@ -16,42 +16,14 @@
|
|||||||
Assisted volume snapshots - to be used by Cinder and not end users.
|
Assisted volume snapshots - to be used by Cinder and not end users.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
from novaclient.v2 import assisted_volume_snapshots
|
||||||
|
from novaclient.v2 import contrib
|
||||||
from novaclient import base
|
|
||||||
|
|
||||||
|
|
||||||
class Snapshot(base.Resource):
|
AssistedSnapshotManager = assisted_volume_snapshots.AssistedSnapshotManager
|
||||||
def __repr__(self):
|
Snapshot = assisted_volume_snapshots.Snapshot
|
||||||
return "<Snapshot: %s>" % self.id
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
"""
|
|
||||||
Delete this snapshot.
|
|
||||||
|
|
||||||
:returns: An instance of novaclient.base.TupleWithMeta
|
|
||||||
"""
|
|
||||||
return self.manager.delete(self)
|
|
||||||
|
|
||||||
|
|
||||||
class AssistedSnapshotManager(base.Manager):
|
|
||||||
resource_class = Snapshot
|
|
||||||
|
|
||||||
def create(self, volume_id, create_info):
|
|
||||||
body = {'snapshot': {'volume_id': volume_id,
|
|
||||||
'create_info': create_info}}
|
|
||||||
return self._create('/os-assisted-volume-snapshots', body, 'snapshot')
|
|
||||||
|
|
||||||
def delete(self, snapshot, delete_info):
|
|
||||||
"""
|
|
||||||
Delete a specified assisted volume snapshot.
|
|
||||||
|
|
||||||
:param snapshot: an assisted volume snapshot to delete
|
|
||||||
:param delete_info: Information for snapshot deletion
|
|
||||||
:returns: An instance of novaclient.base.TupleWithMeta
|
|
||||||
"""
|
|
||||||
return self._delete("/os-assisted-volume-snapshots/%s?delete_info=%s" %
|
|
||||||
(base.getid(snapshot), json.dumps(delete_info)))
|
|
||||||
|
|
||||||
manager_class = AssistedSnapshotManager
|
manager_class = AssistedSnapshotManager
|
||||||
name = 'assisted_volume_snapshots'
|
name = 'assisted_volume_snapshots'
|
||||||
|
|
||||||
|
contrib.warn()
|
||||||
|
@ -13,61 +13,11 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient import base
|
from novaclient.v2 import cells
|
||||||
from novaclient.i18n import _
|
from novaclient.v2 import contrib
|
||||||
from novaclient import utils
|
|
||||||
|
|
||||||
|
|
||||||
class Cell(base.Resource):
|
Cell = cells.Cell
|
||||||
def __repr__(self):
|
CellsManager = cells.CellsManager
|
||||||
return "<Cell: %s>" % self.name
|
|
||||||
|
|
||||||
|
contrib.warn()
|
||||||
class CellsManager(base.Manager):
|
|
||||||
resource_class = Cell
|
|
||||||
|
|
||||||
def get(self, cell_name):
|
|
||||||
"""
|
|
||||||
Get a cell.
|
|
||||||
|
|
||||||
:param cell_name: Name of the :class:`Cell` to get.
|
|
||||||
:rtype: :class:`Cell`
|
|
||||||
"""
|
|
||||||
return self._get("/os-cells/%s" % cell_name, "cell")
|
|
||||||
|
|
||||||
def capacities(self, cell_name=None):
|
|
||||||
"""
|
|
||||||
Get capacities for a cell.
|
|
||||||
|
|
||||||
:param cell_name: Name of the :class:`Cell` to get capacities for.
|
|
||||||
:rtype: :class:`Cell`
|
|
||||||
"""
|
|
||||||
path = ["%s/capacities" % cell_name, "capacities"][cell_name is None]
|
|
||||||
return self._get("/os-cells/%s" % path, "cell")
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
|
||||||
'cell',
|
|
||||||
metavar='<cell-name>',
|
|
||||||
help=_('Name of the cell.'))
|
|
||||||
def do_cell_show(cs, args):
|
|
||||||
"""Show details of a given cell."""
|
|
||||||
cell = cs.cells.get(args.cell)
|
|
||||||
utils.print_dict(cell._info)
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
|
||||||
'--cell',
|
|
||||||
metavar='<cell-name>',
|
|
||||||
help=_("Name of the cell to get the capacities."),
|
|
||||||
default=None)
|
|
||||||
def do_cell_capacities(cs, args):
|
|
||||||
"""Get cell capacities for all cells or a given cell."""
|
|
||||||
cell = cs.cells.capacities(args.cell)
|
|
||||||
print(_("Ram Available: %s MB") % cell.capacities['ram_free']['total_mb'])
|
|
||||||
utils.print_dict(cell.capacities['ram_free']['units_by_mb'],
|
|
||||||
dict_property='Ram(MB)', dict_value="Units")
|
|
||||||
print(_("\nDisk Available: %s MB") %
|
|
||||||
cell.capacities['disk_free']['total_mb'])
|
|
||||||
utils.print_dict(cell.capacities['disk_free']['units_by_mb'],
|
|
||||||
dict_property='Disk(MB)', dict_value="Units")
|
|
||||||
|
@ -12,16 +12,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from novaclient import utils
|
from novaclient.v2 import contrib
|
||||||
|
|
||||||
|
contrib.warn(alternative=False)
|
||||||
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
|
|
||||||
def do_force_delete(cs, args):
|
|
||||||
"""Force delete a server."""
|
|
||||||
utils.find_resource(cs.servers, args.server).force_delete()
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
|
|
||||||
def do_restore(cs, args):
|
|
||||||
"""Restore a soft-deleted server."""
|
|
||||||
utils.find_resource(cs.servers, args.server, deleted=True).restore()
|
|
||||||
|
@ -13,73 +13,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient import api_versions
|
from novaclient.v2 import contrib
|
||||||
from novaclient import base
|
from novaclient.v2 import shell
|
||||||
from novaclient.i18n import _
|
|
||||||
from novaclient import utils
|
|
||||||
|
|
||||||
|
|
||||||
class EvacuateHostResponse(base.Resource):
|
EvacuateHostResponse = shell.EvacuateHostResponse
|
||||||
pass
|
|
||||||
|
|
||||||
|
contrib.warn(alternative=False)
|
||||||
def _server_evacuate(cs, server, args):
|
|
||||||
success = True
|
|
||||||
error_message = ""
|
|
||||||
try:
|
|
||||||
if api_versions.APIVersion("2.29") <= cs.api_version:
|
|
||||||
# if microversion >= 2.29
|
|
||||||
force = getattr(args, 'force', None)
|
|
||||||
cs.servers.evacuate(server=server['uuid'], host=args.target_host,
|
|
||||||
force=force)
|
|
||||||
elif api_versions.APIVersion("2.14") <= cs.api_version:
|
|
||||||
# if microversion 2.14 - 2.28
|
|
||||||
cs.servers.evacuate(server=server['uuid'], host=args.target_host)
|
|
||||||
else:
|
|
||||||
# else microversion 2.0 - 2.13
|
|
||||||
on_shared_storage = getattr(args, 'on_shared_storage', None)
|
|
||||||
cs.servers.evacuate(server=server['uuid'],
|
|
||||||
host=args.target_host,
|
|
||||||
on_shared_storage=on_shared_storage)
|
|
||||||
except Exception as e:
|
|
||||||
success = False
|
|
||||||
error_message = _("Error while evacuating instance: %s") % e
|
|
||||||
return EvacuateHostResponse(base.Manager,
|
|
||||||
{"server_uuid": server['uuid'],
|
|
||||||
"evacuate_accepted": success,
|
|
||||||
"error_message": error_message})
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('host', metavar='<host>', help='Name of host.')
|
|
||||||
@utils.arg(
|
|
||||||
'--target_host',
|
|
||||||
metavar='<target_host>',
|
|
||||||
default=None,
|
|
||||||
help=_('Name of target host. If no host is specified the scheduler will '
|
|
||||||
'select a target.'))
|
|
||||||
@utils.arg(
|
|
||||||
'--on-shared-storage',
|
|
||||||
dest='on_shared_storage',
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help=_('Specifies whether all instances files are on shared storage'),
|
|
||||||
start_version='2.0',
|
|
||||||
end_version='2.13')
|
|
||||||
@utils.arg(
|
|
||||||
'--force',
|
|
||||||
dest='force',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
help=_('Force to not verify the scheduler if a host is provided.'),
|
|
||||||
start_version='2.29')
|
|
||||||
def do_host_evacuate(cs, args):
|
|
||||||
"""Evacuate all instances from failed host."""
|
|
||||||
hypervisors = cs.hypervisors.search(args.host, servers=True)
|
|
||||||
response = []
|
|
||||||
for hyper in hypervisors:
|
|
||||||
if hasattr(hyper, 'servers'):
|
|
||||||
for server in hyper.servers:
|
|
||||||
response.append(_server_evacuate(cs, server, args))
|
|
||||||
|
|
||||||
utils.print_list(response,
|
|
||||||
["Server UUID", "Evacuate Accepted", "Error Message"])
|
|
||||||
|
@ -13,87 +13,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient.i18n import _
|
from novaclient.v2 import contrib
|
||||||
from novaclient import utils
|
|
||||||
|
|
||||||
|
contrib.warn(alternative=False)
|
||||||
def _server_live_migrate(cs, server, args):
|
|
||||||
class HostEvacuateLiveResponse(object):
|
|
||||||
def __init__(self, server_uuid, live_migration_accepted,
|
|
||||||
error_message):
|
|
||||||
self.server_uuid = server_uuid
|
|
||||||
self.live_migration_accepted = live_migration_accepted
|
|
||||||
self.error_message = error_message
|
|
||||||
success = True
|
|
||||||
error_message = ""
|
|
||||||
update_kwargs = {}
|
|
||||||
try:
|
|
||||||
# API >= 2.30
|
|
||||||
if 'force' in args and args.force:
|
|
||||||
update_kwargs['force'] = args.force
|
|
||||||
# API 2.0->2.24
|
|
||||||
if 'disk_over_commit' in args:
|
|
||||||
update_kwargs['disk_over_commit'] = args.disk_over_commit
|
|
||||||
cs.servers.live_migrate(server['uuid'], args.target_host,
|
|
||||||
args.block_migrate, **update_kwargs)
|
|
||||||
except Exception as e:
|
|
||||||
success = False
|
|
||||||
error_message = _("Error while live migrating instance: %s") % e
|
|
||||||
return HostEvacuateLiveResponse(server['uuid'],
|
|
||||||
success,
|
|
||||||
error_message)
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('host', metavar='<host>', help='Name of host.')
|
|
||||||
@utils.arg(
|
|
||||||
'--target-host',
|
|
||||||
metavar='<target_host>',
|
|
||||||
default=None,
|
|
||||||
help=_('Name of target host.'))
|
|
||||||
@utils.arg(
|
|
||||||
'--block-migrate',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
help=_('Enable block migration. (Default=False)'),
|
|
||||||
start_version="2.0", end_version="2.24")
|
|
||||||
@utils.arg(
|
|
||||||
'--block-migrate',
|
|
||||||
action='store_true',
|
|
||||||
default="auto",
|
|
||||||
help=_('Enable block migration. (Default=auto)'),
|
|
||||||
start_version="2.25")
|
|
||||||
@utils.arg(
|
|
||||||
'--disk-over-commit',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
help=_('Enable disk overcommit.'),
|
|
||||||
start_version="2.0", end_version="2.24")
|
|
||||||
@utils.arg(
|
|
||||||
'--max-servers',
|
|
||||||
type=int,
|
|
||||||
dest='max_servers',
|
|
||||||
metavar='<max_servers>',
|
|
||||||
help='Maximum number of servers to live migrate simultaneously')
|
|
||||||
@utils.arg(
|
|
||||||
'--force',
|
|
||||||
dest='force',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
help=_('Force to not verify the scheduler if a host is provided.'),
|
|
||||||
start_version='2.30')
|
|
||||||
def do_host_evacuate_live(cs, args):
|
|
||||||
"""Live migrate all instances of the specified host
|
|
||||||
to other available hosts.
|
|
||||||
"""
|
|
||||||
hypervisors = cs.hypervisors.search(args.host, servers=True)
|
|
||||||
response = []
|
|
||||||
migrating = 0
|
|
||||||
for hyper in hypervisors:
|
|
||||||
for server in getattr(hyper, 'servers', []):
|
|
||||||
response.append(_server_live_migrate(cs, server, args))
|
|
||||||
migrating = migrating + 1
|
|
||||||
if args.max_servers is not None and migrating >= args.max_servers:
|
|
||||||
break
|
|
||||||
|
|
||||||
utils.print_list(response, ["Server UUID", "Live Migration Accepted",
|
|
||||||
"Error Message"])
|
|
||||||
|
@ -13,40 +13,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient import base
|
from novaclient.v2 import contrib
|
||||||
from novaclient.i18n import _
|
from novaclient.v2 import shell
|
||||||
from novaclient import utils
|
|
||||||
|
|
||||||
|
|
||||||
class HostServersMigrateResponse(base.Resource):
|
HostServersMigrateResponse = shell.HostServersMigrateResponse
|
||||||
pass
|
|
||||||
|
|
||||||
|
contrib.warn(alternative=False)
|
||||||
def _server_migrate(cs, server):
|
|
||||||
success = True
|
|
||||||
error_message = ""
|
|
||||||
try:
|
|
||||||
cs.servers.migrate(server['uuid'])
|
|
||||||
except Exception as e:
|
|
||||||
success = False
|
|
||||||
error_message = _("Error while migrating instance: %s") % e
|
|
||||||
return HostServersMigrateResponse(base.Manager,
|
|
||||||
{"server_uuid": server['uuid'],
|
|
||||||
"migration_accepted": success,
|
|
||||||
"error_message": error_message})
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('host', metavar='<host>', help='Name of host.')
|
|
||||||
def do_host_servers_migrate(cs, args):
|
|
||||||
"""Cold migrate all instances off the specified host to other available
|
|
||||||
hosts.
|
|
||||||
"""
|
|
||||||
hypervisors = cs.hypervisors.search(args.host, servers=True)
|
|
||||||
response = []
|
|
||||||
for hyper in hypervisors:
|
|
||||||
if hasattr(hyper, 'servers'):
|
|
||||||
for server in hyper.servers:
|
|
||||||
response.append(_server_migrate(cs, server))
|
|
||||||
|
|
||||||
utils.print_list(response,
|
|
||||||
["Server UUID", "Migration Accepted", "Error Message"])
|
|
||||||
|
@ -13,81 +13,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import pprint
|
from novaclient.v2 import contrib
|
||||||
|
from novaclient.v2 import instance_action
|
||||||
from novaclient import api_versions
|
|
||||||
from novaclient import base
|
|
||||||
from novaclient.i18n import _
|
|
||||||
from novaclient import utils
|
|
||||||
from novaclient.v2 import shell
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceActionManager(base.ManagerWithFind):
|
InstanceActionManager = instance_action.InstanceActionManager
|
||||||
resource_class = base.Resource
|
|
||||||
|
|
||||||
def get(self, server, request_id):
|
contrib.warn()
|
||||||
"""
|
|
||||||
Get details of an action performed on an instance.
|
|
||||||
|
|
||||||
:param request_id: The request_id of the action to get.
|
|
||||||
"""
|
|
||||||
return self._get("/servers/%s/os-instance-actions/%s" %
|
|
||||||
(base.getid(server), request_id), 'instanceAction')
|
|
||||||
|
|
||||||
def list(self, server):
|
|
||||||
"""
|
|
||||||
Get a list of actions performed on a server.
|
|
||||||
"""
|
|
||||||
return self._list('/servers/%s/os-instance-actions' %
|
|
||||||
base.getid(server), 'instanceActions')
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
|
||||||
'server',
|
|
||||||
metavar='<server>',
|
|
||||||
help=_('Name or UUID of the server to show actions for.'),
|
|
||||||
start_version="2.0", end_version="2.20")
|
|
||||||
@utils.arg(
|
|
||||||
'server',
|
|
||||||
metavar='<server>',
|
|
||||||
help=_('Name or UUID of the server to show actions for. Only UUID can be '
|
|
||||||
'used to show actions for a deleted server.'),
|
|
||||||
start_version="2.21")
|
|
||||||
@utils.arg(
|
|
||||||
'request_id',
|
|
||||||
metavar='<request_id>',
|
|
||||||
help=_('Request ID of the action to get.'))
|
|
||||||
def do_instance_action(cs, args):
|
|
||||||
"""Show an action."""
|
|
||||||
if cs.api_version < api_versions.APIVersion("2.21"):
|
|
||||||
server = shell._find_server(cs, args.server)
|
|
||||||
else:
|
|
||||||
server = shell._find_server(cs, args.server, raise_if_notfound=False)
|
|
||||||
action_resource = cs.instance_action.get(server, args.request_id)
|
|
||||||
action = action_resource._info
|
|
||||||
if 'events' in action:
|
|
||||||
action['events'] = pprint.pformat(action['events'])
|
|
||||||
utils.print_dict(action)
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
|
||||||
'server',
|
|
||||||
metavar='<server>',
|
|
||||||
help=_('Name or UUID of the server to list actions for.'),
|
|
||||||
start_version="2.0", end_version="2.20")
|
|
||||||
@utils.arg(
|
|
||||||
'server',
|
|
||||||
metavar='<server>',
|
|
||||||
help=_('Name or UUID of the server to list actions for. Only UUID can be '
|
|
||||||
'used to list actions on a deleted server.'),
|
|
||||||
start_version="2.21")
|
|
||||||
def do_instance_action_list(cs, args):
|
|
||||||
"""List actions on a server."""
|
|
||||||
if cs.api_version < api_versions.APIVersion("2.21"):
|
|
||||||
server = shell._find_server(cs, args.server)
|
|
||||||
else:
|
|
||||||
server = shell._find_server(cs, args.server, raise_if_notfound=False)
|
|
||||||
actions = cs.instance_action.list(server)
|
|
||||||
utils.print_list(actions,
|
|
||||||
['Action', 'Request_ID', 'Message', 'Start_Time'],
|
|
||||||
sortby_index=3)
|
|
||||||
|
@ -13,34 +13,11 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient import base
|
from novaclient.v2 import contrib
|
||||||
from novaclient import utils
|
from novaclient.v2 import list_extensions
|
||||||
|
|
||||||
|
|
||||||
class ListExtResource(base.Resource):
|
ListExtResource = list_extensions.ListExtResource
|
||||||
@property
|
ListExtManager = list_extensions.ListExtResource
|
||||||
def summary(self):
|
|
||||||
descr = self.description.strip()
|
|
||||||
if not descr:
|
|
||||||
return '??'
|
|
||||||
lines = descr.split("\n")
|
|
||||||
if len(lines) == 1:
|
|
||||||
return lines[0]
|
|
||||||
else:
|
|
||||||
return lines[0] + "..."
|
|
||||||
|
|
||||||
|
contrib.warn()
|
||||||
class ListExtManager(base.Manager):
|
|
||||||
resource_class = ListExtResource
|
|
||||||
|
|
||||||
def show_all(self):
|
|
||||||
return self._list("/extensions", 'extensions')
|
|
||||||
|
|
||||||
|
|
||||||
def do_list_extensions(client, _args):
|
|
||||||
"""
|
|
||||||
List all the os-api extensions that are available.
|
|
||||||
"""
|
|
||||||
extensions = client.list_extensions.show_all()
|
|
||||||
fields = ["Name", "Summary", "Alias", "Updated"]
|
|
||||||
utils.print_list(extensions, fields)
|
|
||||||
|
@ -13,35 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from novaclient.i18n import _
|
from novaclient.v2 import contrib
|
||||||
from novaclient import utils
|
|
||||||
from novaclient.v2 import shell
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
contrib.warn(alternative=False)
|
||||||
'host',
|
|
||||||
metavar='<host>',
|
|
||||||
help=_('Name of host.'))
|
|
||||||
@utils.arg(
|
|
||||||
'action',
|
|
||||||
metavar='<action>',
|
|
||||||
choices=['set', 'delete'],
|
|
||||||
help=_("Actions: 'set' or 'delete'"))
|
|
||||||
@utils.arg(
|
|
||||||
'metadata',
|
|
||||||
metavar='<key=value>',
|
|
||||||
nargs='+',
|
|
||||||
action='append',
|
|
||||||
default=[],
|
|
||||||
help=_('Metadata to set or delete (only key is necessary on delete)'))
|
|
||||||
def do_host_meta(cs, args):
|
|
||||||
"""Set or Delete metadata on all instances of a host."""
|
|
||||||
hypervisors = cs.hypervisors.search(args.host, servers=True)
|
|
||||||
for hyper in hypervisors:
|
|
||||||
metadata = shell._extract_metadata(args)
|
|
||||||
if hasattr(hyper, 'servers'):
|
|
||||||
for server in hyper.servers:
|
|
||||||
if args.action == 'set':
|
|
||||||
cs.servers.set_meta(server['uuid'], metadata)
|
|
||||||
elif args.action == 'delete':
|
|
||||||
cs.servers.delete_meta(server['uuid'], metadata.keys())
|
|
||||||
|
@ -14,86 +14,11 @@
|
|||||||
migration interface
|
migration interface
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from six.moves.urllib import parse
|
from novaclient.v2 import contrib
|
||||||
|
from novaclient.v2 import migrations
|
||||||
from novaclient import api_versions
|
|
||||||
from novaclient import base
|
|
||||||
from novaclient.i18n import _
|
|
||||||
from novaclient import utils
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(base.Resource):
|
Migration = migrations.Migration
|
||||||
def __repr__(self):
|
MigrationManager = migrations.MigrationManager
|
||||||
return "<Migration: %s>" % self.id
|
|
||||||
|
|
||||||
|
contrib.warn()
|
||||||
class MigrationManager(base.ManagerWithFind):
|
|
||||||
resource_class = Migration
|
|
||||||
|
|
||||||
def list(self, host=None, status=None, cell_name=None):
|
|
||||||
"""
|
|
||||||
Get a list of migrations.
|
|
||||||
:param host: (optional) filter migrations by host name.
|
|
||||||
:param status: (optional) filter migrations by status.
|
|
||||||
:param cell_name: (optional) filter migrations for a cell.
|
|
||||||
"""
|
|
||||||
opts = {}
|
|
||||||
if host:
|
|
||||||
opts['host'] = host
|
|
||||||
if status:
|
|
||||||
opts['status'] = status
|
|
||||||
if cell_name:
|
|
||||||
opts['cell_name'] = cell_name
|
|
||||||
|
|
||||||
# Transform the dict to a sequence of two-element tuples in fixed
|
|
||||||
# order, then the encoded string will be consistent in Python 2&3.
|
|
||||||
new_opts = sorted(opts.items(), key=lambda x: x[0])
|
|
||||||
|
|
||||||
query_string = "?%s" % parse.urlencode(new_opts) if new_opts else ""
|
|
||||||
|
|
||||||
return self._list("/os-migrations%s" % query_string, "migrations")
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
|
||||||
'--host',
|
|
||||||
dest='host',
|
|
||||||
metavar='<host>',
|
|
||||||
help=_('Fetch migrations for the given host.'))
|
|
||||||
@utils.arg(
|
|
||||||
'--status',
|
|
||||||
dest='status',
|
|
||||||
metavar='<status>',
|
|
||||||
help=_('Fetch migrations for the given status.'))
|
|
||||||
@utils.arg(
|
|
||||||
'--cell_name',
|
|
||||||
dest='cell_name',
|
|
||||||
metavar='<cell_name>',
|
|
||||||
help=_('Fetch migrations for the given cell_name.'))
|
|
||||||
def do_migration_list(cs, args):
|
|
||||||
"""Print a list of migrations."""
|
|
||||||
migrations = cs.migrations.list(args.host, args.status, args.cell_name)
|
|
||||||
_print_migrations(cs, migrations)
|
|
||||||
|
|
||||||
|
|
||||||
def _print_migrations(cs, migrations):
|
|
||||||
fields = ['Source Node', 'Dest Node', 'Source Compute', 'Dest Compute',
|
|
||||||
'Dest Host', 'Status', 'Instance UUID', 'Old Flavor',
|
|
||||||
'New Flavor', 'Created At', 'Updated At']
|
|
||||||
|
|
||||||
def old_flavor(migration):
|
|
||||||
return migration.old_instance_type_id
|
|
||||||
|
|
||||||
def new_flavor(migration):
|
|
||||||
return migration.new_instance_type_id
|
|
||||||
|
|
||||||
def migration_type(migration):
|
|
||||||
return migration.migration_type
|
|
||||||
|
|
||||||
formatters = {'Old Flavor': old_flavor, 'New Flavor': new_flavor}
|
|
||||||
|
|
||||||
if cs.api_version >= api_versions.APIVersion("2.23"):
|
|
||||||
fields.insert(0, "Id")
|
|
||||||
fields.append("Type")
|
|
||||||
formatters.update({"Type": migration_type})
|
|
||||||
|
|
||||||
utils.print_list(migrations, fields, formatters)
|
|
||||||
|
@ -16,28 +16,14 @@
|
|||||||
External event triggering for servers, not to be used by users.
|
External event triggering for servers, not to be used by users.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from novaclient import base
|
from novaclient.v2 import contrib
|
||||||
|
from novaclient.v2 import server_external_events
|
||||||
|
|
||||||
|
|
||||||
class Event(base.Resource):
|
Event = server_external_events.Event
|
||||||
def __repr__(self):
|
ServerExternalEventManager = server_external_events.ServerExternalEventManager
|
||||||
return "<Event: %s>" % self.name
|
|
||||||
|
|
||||||
|
|
||||||
class ServerExternalEventManager(base.Manager):
|
|
||||||
resource_class = Event
|
|
||||||
|
|
||||||
def create(self, events):
|
|
||||||
"""Create one or more server events.
|
|
||||||
|
|
||||||
:param:events: A list of dictionaries containing 'server_uuid', 'name',
|
|
||||||
'status', and 'tag' (which may be absent)
|
|
||||||
"""
|
|
||||||
|
|
||||||
body = {'events': events}
|
|
||||||
return self._create('/os-server-external-events', body, 'events',
|
|
||||||
return_raw=True)
|
|
||||||
|
|
||||||
|
|
||||||
manager_class = ServerExternalEventManager
|
manager_class = ServerExternalEventManager
|
||||||
name = 'server_external_events'
|
name = 'server_external_events'
|
||||||
|
|
||||||
|
contrib.warn()
|
||||||
|
40
novaclient/v2/instance_action.py
Normal file
40
novaclient/v2/instance_action.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Copyright 2013 Rackspace Hosting
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from novaclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceAction(base.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceActionManager(base.ManagerWithFind):
|
||||||
|
resource_class = InstanceAction
|
||||||
|
|
||||||
|
def get(self, server, request_id):
|
||||||
|
"""
|
||||||
|
Get details of an action performed on an instance.
|
||||||
|
|
||||||
|
:param request_id: The request_id of the action to get.
|
||||||
|
"""
|
||||||
|
return self._get("/servers/%s/os-instance-actions/%s" %
|
||||||
|
(base.getid(server), request_id), 'instanceAction')
|
||||||
|
|
||||||
|
def list(self, server):
|
||||||
|
"""
|
||||||
|
Get a list of actions performed on a server.
|
||||||
|
"""
|
||||||
|
return self._list('/servers/%s/os-instance-actions' %
|
||||||
|
base.getid(server), 'instanceActions')
|
36
novaclient/v2/list_extensions.py
Normal file
36
novaclient/v2/list_extensions.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from novaclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class ListExtResource(base.Resource):
|
||||||
|
@property
|
||||||
|
def summary(self):
|
||||||
|
descr = self.description.strip()
|
||||||
|
if not descr:
|
||||||
|
return '??'
|
||||||
|
lines = descr.split("\n")
|
||||||
|
if len(lines) == 1:
|
||||||
|
return lines[0]
|
||||||
|
else:
|
||||||
|
return lines[0] + "..."
|
||||||
|
|
||||||
|
|
||||||
|
class ListExtManager(base.Manager):
|
||||||
|
resource_class = ListExtResource
|
||||||
|
|
||||||
|
def show_all(self):
|
||||||
|
return self._list("/extensions", 'extensions')
|
51
novaclient/v2/migrations.py
Normal file
51
novaclient/v2/migrations.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
migration interface
|
||||||
|
"""
|
||||||
|
|
||||||
|
from six.moves.urllib import parse
|
||||||
|
|
||||||
|
from novaclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(base.Resource):
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Migration: %s>" % self.id
|
||||||
|
|
||||||
|
|
||||||
|
class MigrationManager(base.ManagerWithFind):
|
||||||
|
resource_class = Migration
|
||||||
|
|
||||||
|
def list(self, host=None, status=None, cell_name=None):
|
||||||
|
"""
|
||||||
|
Get a list of migrations.
|
||||||
|
:param host: (optional) filter migrations by host name.
|
||||||
|
:param status: (optional) filter migrations by status.
|
||||||
|
:param cell_name: (optional) filter migrations for a cell.
|
||||||
|
"""
|
||||||
|
opts = {}
|
||||||
|
if host:
|
||||||
|
opts['host'] = host
|
||||||
|
if status:
|
||||||
|
opts['status'] = status
|
||||||
|
if cell_name:
|
||||||
|
opts['cell_name'] = cell_name
|
||||||
|
|
||||||
|
# Transform the dict to a sequence of two-element tuples in fixed
|
||||||
|
# order, then the encoded string will be consistent in Python 2&3.
|
||||||
|
new_opts = sorted(opts.items(), key=lambda x: x[0])
|
||||||
|
|
||||||
|
query_string = "?%s" % parse.urlencode(new_opts) if new_opts else ""
|
||||||
|
|
||||||
|
return self._list("/os-migrations%s" % query_string, "migrations")
|
39
novaclient/v2/server_external_events.py
Normal file
39
novaclient/v2/server_external_events.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Copyright (C) 2014, Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
External event triggering for servers, not to be used by users.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from novaclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class Event(base.Resource):
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Event: %s>" % self.name
|
||||||
|
|
||||||
|
|
||||||
|
class ServerExternalEventManager(base.Manager):
|
||||||
|
resource_class = Event
|
||||||
|
|
||||||
|
def create(self, events):
|
||||||
|
"""Create one or more server events.
|
||||||
|
|
||||||
|
:param:events: A list of dictionaries containing 'server_uuid', 'name',
|
||||||
|
'status', and 'tag' (which may be absent)
|
||||||
|
"""
|
||||||
|
|
||||||
|
body = {'events': events}
|
||||||
|
return self._create('/os-server-external-events', body, 'events',
|
||||||
|
return_raw=True)
|
@ -26,6 +26,7 @@ import getpass
|
|||||||
import locale
|
import locale
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import pprint
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -5185,3 +5186,359 @@ def do_server_tag_delete_all(cs, args):
|
|||||||
"""Delete all tags from a server."""
|
"""Delete all tags from a server."""
|
||||||
server = _find_server(cs, args.server)
|
server = _find_server(cs, args.server)
|
||||||
server.delete_all_tags()
|
server.delete_all_tags()
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'cell',
|
||||||
|
metavar='<cell-name>',
|
||||||
|
help=_('Name of the cell.'))
|
||||||
|
def do_cell_show(cs, args):
|
||||||
|
"""Show details of a given cell."""
|
||||||
|
cell = cs.cells.get(args.cell)
|
||||||
|
utils.print_dict(cell._info)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'--cell',
|
||||||
|
metavar='<cell-name>',
|
||||||
|
help=_("Name of the cell to get the capacities."),
|
||||||
|
default=None)
|
||||||
|
def do_cell_capacities(cs, args):
|
||||||
|
"""Get cell capacities for all cells or a given cell."""
|
||||||
|
cell = cs.cells.capacities(args.cell)
|
||||||
|
print(_("Ram Available: %s MB") % cell.capacities['ram_free']['total_mb'])
|
||||||
|
utils.print_dict(cell.capacities['ram_free']['units_by_mb'],
|
||||||
|
dict_property='Ram(MB)', dict_value="Units")
|
||||||
|
print(_("\nDisk Available: %s MB") %
|
||||||
|
cell.capacities['disk_free']['total_mb'])
|
||||||
|
utils.print_dict(cell.capacities['disk_free']['units_by_mb'],
|
||||||
|
dict_property='Disk(MB)', dict_value="Units")
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
|
||||||
|
def do_force_delete(cs, args):
|
||||||
|
"""Force delete a server."""
|
||||||
|
utils.find_resource(cs.servers, args.server).force_delete()
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
|
||||||
|
def do_restore(cs, args):
|
||||||
|
"""Restore a soft-deleted server."""
|
||||||
|
utils.find_resource(cs.servers, args.server, deleted=True).restore()
|
||||||
|
|
||||||
|
|
||||||
|
class EvacuateHostResponse(base.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _server_evacuate(cs, server, args):
|
||||||
|
success = True
|
||||||
|
error_message = ""
|
||||||
|
try:
|
||||||
|
if api_versions.APIVersion("2.29") <= cs.api_version:
|
||||||
|
# if microversion >= 2.29
|
||||||
|
force = getattr(args, 'force', None)
|
||||||
|
cs.servers.evacuate(server=server['uuid'], host=args.target_host,
|
||||||
|
force=force)
|
||||||
|
elif api_versions.APIVersion("2.14") <= cs.api_version:
|
||||||
|
# if microversion 2.14 - 2.28
|
||||||
|
cs.servers.evacuate(server=server['uuid'], host=args.target_host)
|
||||||
|
else:
|
||||||
|
# else microversion 2.0 - 2.13
|
||||||
|
on_shared_storage = getattr(args, 'on_shared_storage', None)
|
||||||
|
cs.servers.evacuate(server=server['uuid'],
|
||||||
|
host=args.target_host,
|
||||||
|
on_shared_storage=on_shared_storage)
|
||||||
|
except Exception as e:
|
||||||
|
success = False
|
||||||
|
error_message = _("Error while evacuating instance: %s") % e
|
||||||
|
return EvacuateHostResponse(base.Manager, {"server_uuid": server['uuid'],
|
||||||
|
"evacuate_accepted": success,
|
||||||
|
"error_message": error_message})
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('host', metavar='<host>', help='Name of host.')
|
||||||
|
@utils.arg(
|
||||||
|
'--target_host',
|
||||||
|
metavar='<target_host>',
|
||||||
|
default=None,
|
||||||
|
help=_('Name of target host. If no host is specified the scheduler will '
|
||||||
|
'select a target.'))
|
||||||
|
@utils.arg(
|
||||||
|
'--on-shared-storage',
|
||||||
|
dest='on_shared_storage',
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help=_('Specifies whether all instances files are on shared storage'),
|
||||||
|
start_version='2.0',
|
||||||
|
end_version='2.13')
|
||||||
|
@utils.arg(
|
||||||
|
'--force',
|
||||||
|
dest='force',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_('Force to not verify the scheduler if a host is provided.'),
|
||||||
|
start_version='2.29')
|
||||||
|
def do_host_evacuate(cs, args):
|
||||||
|
"""Evacuate all instances from failed host."""
|
||||||
|
|
||||||
|
hypervisors = cs.hypervisors.search(args.host, servers=True)
|
||||||
|
response = []
|
||||||
|
for hyper in hypervisors:
|
||||||
|
if hasattr(hyper, 'servers'):
|
||||||
|
for server in hyper.servers:
|
||||||
|
response.append(_server_evacuate(cs, server, args))
|
||||||
|
|
||||||
|
utils.print_list(response,
|
||||||
|
["Server UUID", "Evacuate Accepted", "Error Message"])
|
||||||
|
|
||||||
|
|
||||||
|
def _server_live_migrate(cs, server, args):
|
||||||
|
class HostEvacuateLiveResponse(object):
|
||||||
|
def __init__(self, server_uuid, live_migration_accepted,
|
||||||
|
error_message):
|
||||||
|
self.server_uuid = server_uuid
|
||||||
|
self.live_migration_accepted = live_migration_accepted
|
||||||
|
self.error_message = error_message
|
||||||
|
success = True
|
||||||
|
error_message = ""
|
||||||
|
update_kwargs = {}
|
||||||
|
try:
|
||||||
|
# API >= 2.30
|
||||||
|
if 'force' in args and args.force:
|
||||||
|
update_kwargs['force'] = args.force
|
||||||
|
# API 2.0->2.24
|
||||||
|
if 'disk_over_commit' in args:
|
||||||
|
update_kwargs['disk_over_commit'] = args.disk_over_commit
|
||||||
|
cs.servers.live_migrate(server['uuid'], args.target_host,
|
||||||
|
args.block_migrate, **update_kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
success = False
|
||||||
|
error_message = _("Error while live migrating instance: %s") % e
|
||||||
|
return HostEvacuateLiveResponse(server['uuid'],
|
||||||
|
success,
|
||||||
|
error_message)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('host', metavar='<host>', help='Name of host.')
|
||||||
|
@utils.arg(
|
||||||
|
'--target-host',
|
||||||
|
metavar='<target_host>',
|
||||||
|
default=None,
|
||||||
|
help=_('Name of target host.'))
|
||||||
|
@utils.arg(
|
||||||
|
'--block-migrate',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_('Enable block migration. (Default=False)'),
|
||||||
|
start_version="2.0", end_version="2.24")
|
||||||
|
@utils.arg(
|
||||||
|
'--block-migrate',
|
||||||
|
action='store_true',
|
||||||
|
default="auto",
|
||||||
|
help=_('Enable block migration. (Default=auto)'),
|
||||||
|
start_version="2.25")
|
||||||
|
@utils.arg(
|
||||||
|
'--disk-over-commit',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_('Enable disk overcommit.'),
|
||||||
|
start_version="2.0", end_version="2.24")
|
||||||
|
@utils.arg(
|
||||||
|
'--max-servers',
|
||||||
|
type=int,
|
||||||
|
dest='max_servers',
|
||||||
|
metavar='<max_servers>',
|
||||||
|
help='Maximum number of servers to live migrate simultaneously')
|
||||||
|
@utils.arg(
|
||||||
|
'--force',
|
||||||
|
dest='force',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_('Force to not verify the scheduler if a host is provided.'),
|
||||||
|
start_version='2.30')
|
||||||
|
def do_host_evacuate_live(cs, args):
|
||||||
|
"""Live migrate all instances of the specified host
|
||||||
|
to other available hosts.
|
||||||
|
"""
|
||||||
|
hypervisors = cs.hypervisors.search(args.host, servers=True)
|
||||||
|
response = []
|
||||||
|
migrating = 0
|
||||||
|
for hyper in hypervisors:
|
||||||
|
for server in getattr(hyper, 'servers', []):
|
||||||
|
response.append(_server_live_migrate(cs, server, args))
|
||||||
|
migrating += 1
|
||||||
|
if args.max_servers is not None and migrating >= args.max_servers:
|
||||||
|
break
|
||||||
|
|
||||||
|
utils.print_list(response, ["Server UUID", "Live Migration Accepted",
|
||||||
|
"Error Message"])
|
||||||
|
|
||||||
|
|
||||||
|
class HostServersMigrateResponse(base.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _server_migrate(cs, server):
|
||||||
|
success = True
|
||||||
|
error_message = ""
|
||||||
|
try:
|
||||||
|
cs.servers.migrate(server['uuid'])
|
||||||
|
except Exception as e:
|
||||||
|
success = False
|
||||||
|
error_message = _("Error while migrating instance: %s") % e
|
||||||
|
return HostServersMigrateResponse(base.Manager,
|
||||||
|
{"server_uuid": server['uuid'],
|
||||||
|
"migration_accepted": success,
|
||||||
|
"error_message": error_message})
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('host', metavar='<host>', help='Name of host.')
|
||||||
|
def do_host_servers_migrate(cs, args):
|
||||||
|
"""Cold migrate all instances off the specified host to other available
|
||||||
|
hosts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hypervisors = cs.hypervisors.search(args.host, servers=True)
|
||||||
|
response = []
|
||||||
|
for hyper in hypervisors:
|
||||||
|
if hasattr(hyper, 'servers'):
|
||||||
|
for server in hyper.servers:
|
||||||
|
response.append(_server_migrate(cs, server))
|
||||||
|
|
||||||
|
utils.print_list(response,
|
||||||
|
["Server UUID", "Migration Accepted", "Error Message"])
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'server',
|
||||||
|
metavar='<server>',
|
||||||
|
help=_('Name or UUID of the server to show actions for.'),
|
||||||
|
start_version="2.0", end_version="2.20")
|
||||||
|
@utils.arg(
|
||||||
|
'server',
|
||||||
|
metavar='<server>',
|
||||||
|
help=_('Name or UUID of the server to show actions for. Only UUID can be '
|
||||||
|
'used to show actions for a deleted server.'),
|
||||||
|
start_version="2.21")
|
||||||
|
@utils.arg(
|
||||||
|
'request_id',
|
||||||
|
metavar='<request_id>',
|
||||||
|
help=_('Request ID of the action to get.'))
|
||||||
|
def do_instance_action(cs, args):
|
||||||
|
"""Show an action."""
|
||||||
|
if cs.api_version < api_versions.APIVersion("2.21"):
|
||||||
|
server = _find_server(cs, args.server)
|
||||||
|
else:
|
||||||
|
server = _find_server(cs, args.server, raise_if_notfound=False)
|
||||||
|
action_resource = cs.instance_action.get(server, args.request_id)
|
||||||
|
action = action_resource._info
|
||||||
|
if 'events' in action:
|
||||||
|
action['events'] = pprint.pformat(action['events'])
|
||||||
|
utils.print_dict(action)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'server',
|
||||||
|
metavar='<server>',
|
||||||
|
help=_('Name or UUID of the server to list actions for.'),
|
||||||
|
start_version="2.0", end_version="2.20")
|
||||||
|
@utils.arg(
|
||||||
|
'server',
|
||||||
|
metavar='<server>',
|
||||||
|
help=_('Name or UUID of the server to list actions for. Only UUID can be '
|
||||||
|
'used to list actions on a deleted server.'),
|
||||||
|
start_version="2.21")
|
||||||
|
def do_instance_action_list(cs, args):
|
||||||
|
"""List actions on a server."""
|
||||||
|
if cs.api_version < api_versions.APIVersion("2.21"):
|
||||||
|
server = _find_server(cs, args.server)
|
||||||
|
else:
|
||||||
|
server = _find_server(cs, args.server, raise_if_notfound=False)
|
||||||
|
actions = cs.instance_action.list(server)
|
||||||
|
utils.print_list(actions,
|
||||||
|
['Action', 'Request_ID', 'Message', 'Start_Time'],
|
||||||
|
sortby_index=3)
|
||||||
|
|
||||||
|
|
||||||
|
def do_list_extensions(cs, _args):
|
||||||
|
"""
|
||||||
|
List all the os-api extensions that are available.
|
||||||
|
"""
|
||||||
|
extensions = cs.list_extensions.show_all()
|
||||||
|
fields = ["Name", "Summary", "Alias", "Updated"]
|
||||||
|
utils.print_list(extensions, fields)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'host',
|
||||||
|
metavar='<host>',
|
||||||
|
help=_('Name of host.'))
|
||||||
|
@utils.arg(
|
||||||
|
'action',
|
||||||
|
metavar='<action>',
|
||||||
|
choices=['set', 'delete'],
|
||||||
|
help=_("Actions: 'set' or 'delete'"))
|
||||||
|
@utils.arg(
|
||||||
|
'metadata',
|
||||||
|
metavar='<key=value>',
|
||||||
|
nargs='+',
|
||||||
|
action='append',
|
||||||
|
default=[],
|
||||||
|
help=_('Metadata to set or delete (only key is necessary on delete)'))
|
||||||
|
def do_host_meta(cs, args):
|
||||||
|
"""Set or Delete metadata on all instances of a host."""
|
||||||
|
hypervisors = cs.hypervisors.search(args.host, servers=True)
|
||||||
|
for hyper in hypervisors:
|
||||||
|
metadata = _extract_metadata(args)
|
||||||
|
if hasattr(hyper, 'servers'):
|
||||||
|
for server in hyper.servers:
|
||||||
|
if args.action == 'set':
|
||||||
|
cs.servers.set_meta(server['uuid'], metadata)
|
||||||
|
elif args.action == 'delete':
|
||||||
|
cs.servers.delete_meta(server['uuid'], metadata.keys())
|
||||||
|
|
||||||
|
|
||||||
|
def _print_migrations(cs, migrations):
|
||||||
|
fields = ['Source Node', 'Dest Node', 'Source Compute', 'Dest Compute',
|
||||||
|
'Dest Host', 'Status', 'Instance UUID', 'Old Flavor',
|
||||||
|
'New Flavor', 'Created At', 'Updated At']
|
||||||
|
|
||||||
|
def old_flavor(migration):
|
||||||
|
return migration.old_instance_type_id
|
||||||
|
|
||||||
|
def new_flavor(migration):
|
||||||
|
return migration.new_instance_type_id
|
||||||
|
|
||||||
|
def migration_type(migration):
|
||||||
|
return migration.migration_type
|
||||||
|
|
||||||
|
formatters = {'Old Flavor': old_flavor, 'New Flavor': new_flavor}
|
||||||
|
|
||||||
|
if cs.api_version >= api_versions.APIVersion("2.23"):
|
||||||
|
fields.insert(0, "Id")
|
||||||
|
fields.append("Type")
|
||||||
|
formatters.update({"Type": migration_type})
|
||||||
|
|
||||||
|
utils.print_list(migrations, fields, formatters)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'--host',
|
||||||
|
dest='host',
|
||||||
|
metavar='<host>',
|
||||||
|
help=_('Fetch migrations for the given host.'))
|
||||||
|
@utils.arg(
|
||||||
|
'--status',
|
||||||
|
dest='status',
|
||||||
|
metavar='<status>',
|
||||||
|
help=_('Fetch migrations for the given status.'))
|
||||||
|
@utils.arg(
|
||||||
|
'--cell_name',
|
||||||
|
dest='cell_name',
|
||||||
|
metavar='<cell_name>',
|
||||||
|
help=_('Fetch migrations for the given cell_name.'))
|
||||||
|
def do_migration_list(cs, args):
|
||||||
|
"""Print a list of migrations."""
|
||||||
|
migrations = cs.migrations.list(args.host, args.status, args.cell_name)
|
||||||
|
_print_migrations(cs, migrations)
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
prelude: >
|
||||||
|
All extensions of API V2.0 were merged to 2.1, but NovaClient continued
|
||||||
|
to store them as a separate entities.
|
||||||
|
upgrade:
|
||||||
|
- All managers and resources from novaclient.v2.contrib submodules are moved
|
||||||
|
to appropriate submodules of novaclient.v2 (except barametal and
|
||||||
|
tenant_networks, which were deprecated previously)
|
||||||
|
- All shell commands from novaclient.v2.contrib submodules are moved to
|
||||||
|
novaclient.v2.shell module.
|
||||||
|
- novaclient.v2.client.Client imports all modules (which were located in
|
||||||
|
submodules of novaclient.v2.contrib) by-default for api version v2
|
||||||
|
- Method novaclient.client.discover_extensions returns only barametal and
|
||||||
|
tenant_networks extensions, since they are not included by default.
|
||||||
|
- There are no modules and extensions for "deferred_delete", "host_evacuate",
|
||||||
|
"host_evacuate_live" and "metadata_extensions" anymore. Previously, they
|
||||||
|
contained only shell commands and shell module auto loads them (there is
|
||||||
|
no ability to not do it).
|
||||||
|
deprecations:
|
||||||
|
- All modules of novaclient.v2.contrib are deprecated now and will be
|
||||||
|
removed after OpenStack Pike.
|
Loading…
Reference in New Issue
Block a user