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 functools
|
||||
import glob
|
||||
import hashlib
|
||||
import imp
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import pkgutil
|
||||
import re
|
||||
import warnings
|
||||
@ -772,18 +769,14 @@ def _discover_via_python_path():
|
||||
|
||||
|
||||
def _discover_via_contrib_path(version):
|
||||
module_path = os.path.dirname(os.path.abspath(__file__))
|
||||
ext_path = os.path.join(module_path, "v%s" % version.ver_major, 'contrib')
|
||||
ext_glob = os.path.join(ext_path, "*.py")
|
||||
if version.ver_major == 2:
|
||||
modules = {"baremetal": "novaclient.v2.contrib.baremetal",
|
||||
"tenant_networks": "novaclient.v2.contrib.tenant_networks"}
|
||||
|
||||
for ext_path in glob.iglob(ext_glob):
|
||||
name = os.path.basename(ext_path)[:-3]
|
||||
|
||||
if name in extensions_ignored_name:
|
||||
continue
|
||||
|
||||
module = imp.load_source(name, ext_path)
|
||||
yield name, module
|
||||
for name, module_name in modules.items():
|
||||
module_loader = pkgutil.get_loader(module_name)
|
||||
module = module_loader.load_module(module_name)
|
||||
yield name, module
|
||||
|
||||
|
||||
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
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import extension
|
||||
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
|
||||
|
||||
|
||||
@ -31,7 +32,8 @@ class BaremetalExtensionTest(utils.TestCase):
|
||||
extensions = [
|
||||
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):
|
||||
nl = self.cs.baremetal.list()
|
||||
|
@ -13,9 +13,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import extension
|
||||
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
|
||||
|
||||
|
||||
@ -27,7 +28,8 @@ class TenantNetworkExtensionTests(utils.TestCase):
|
||||
extension.Extension(tenant_networks.__name__.split(".")[-1],
|
||||
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):
|
||||
nets = self.cs.tenant_networks.list()
|
||||
|
@ -60,9 +60,8 @@ class FakeClient(fakes.FakeClient, client.Client):
|
||||
client.Client.__init__(self, 'username', 'password',
|
||||
'project_id', 'auth_url',
|
||||
extensions=kwargs.get('extensions'),
|
||||
direct_use=False)
|
||||
kwargs["api_version"] = api_version
|
||||
self.client = FakeHTTPClient(**kwargs)
|
||||
direct_use=False, api_version=api_version)
|
||||
self.client = FakeHTTPClient(api_version=api_version, **kwargs)
|
||||
|
||||
|
||||
class FakeHTTPClient(base_client.HTTPClient):
|
||||
@ -2142,11 +2141,6 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
|
||||
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
|
||||
#
|
||||
@ -2242,6 +2236,90 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
def delete_servers_1234_tags(self, **kw):
|
||||
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):
|
||||
|
||||
@ -2249,9 +2327,8 @@ class FakeSessionClient(fakes.FakeClient, client.Client):
|
||||
client.Client.__init__(self, 'username', 'password',
|
||||
'project_id', 'auth_url',
|
||||
extensions=kwargs.get('extensions'),
|
||||
direct_use=False)
|
||||
kwargs["api_version"] = api_version
|
||||
self.client = FakeSessionMockClient(**kwargs)
|
||||
direct_use=False, api_version=api_version)
|
||||
self.client = FakeSessionMockClient(api_version=api_version, **kwargs)
|
||||
|
||||
|
||||
class FakeSessionMockClient(base_client.SessionClient, FakeHTTPClient):
|
||||
|
@ -16,20 +16,15 @@
|
||||
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.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import assisted_volume_snapshots as assisted_snaps
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
|
||||
|
||||
class AssistedVolumeSnapshotsTestCase(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(AssistedVolumeSnapshotsTestCase, self).setUp()
|
||||
extensions = [
|
||||
extension.Extension(assisted_snaps.__name__.split(".")[-1],
|
||||
assisted_snaps),
|
||||
]
|
||||
self.cs = fakes.FakeClient(extensions=extensions)
|
||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||
|
||||
def test_create_snap(self):
|
||||
vs = self.cs.assisted_volume_snapshots.create('1', {})
|
@ -18,9 +18,13 @@ from keystoneauth1 import fixture
|
||||
import mock
|
||||
import requests
|
||||
|
||||
from novaclient import client
|
||||
from novaclient import exceptions
|
||||
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):
|
||||
@ -35,9 +39,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
return resp
|
||||
|
||||
def test_authenticate_success(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, service_type='compute',
|
||||
direct_use=False)
|
||||
cs = Client("username", "password", "project_id", utils.AUTH_URL_V2,
|
||||
service_type='compute')
|
||||
resp = self.get_token()
|
||||
|
||||
auth_response = utils.TestResponse({
|
||||
@ -83,8 +86,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_authenticate_failure(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, direct_use=False)
|
||||
cs = Client("username", "password", "project_id", utils.AUTH_URL_V2)
|
||||
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
||||
auth_response = utils.TestResponse({
|
||||
"status_code": 401,
|
||||
@ -100,9 +102,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_v1_auth_redirect(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V1, service_type='compute',
|
||||
direct_use=False)
|
||||
cs = Client("username", "password", "project_id", utils.AUTH_URL_V1,
|
||||
service_type='compute')
|
||||
dict_correct_response = self.get_token()
|
||||
correct_response = json.dumps(dict_correct_response)
|
||||
dict_responses = [
|
||||
@ -166,9 +167,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_v2_auth_redirect(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, service_type='compute',
|
||||
direct_use=False)
|
||||
cs = Client("username", "password", "project_id", utils.AUTH_URL_V2,
|
||||
service_type='compute')
|
||||
dict_correct_response = self.get_token()
|
||||
correct_response = json.dumps(dict_correct_response)
|
||||
dict_responses = [
|
||||
@ -232,9 +232,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_ambiguous_endpoints(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, service_type='compute',
|
||||
direct_use=False)
|
||||
cs = Client("username", "password", "project_id", utils.AUTH_URL_V2,
|
||||
service_type='compute')
|
||||
resp = self.get_token()
|
||||
|
||||
# duplicate existing service
|
||||
@ -256,9 +255,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_authenticate_with_token_success(self):
|
||||
cs = client.Client("username", None, "project_id",
|
||||
utils.AUTH_URL_V2, service_type='compute',
|
||||
direct_use=False)
|
||||
cs = Client("username", None, "project_id", utils.AUTH_URL_V2,
|
||||
service_type='compute')
|
||||
cs.client.auth_token = "FAKE_ID"
|
||||
resp = self.get_token(token_id="FAKE_ID")
|
||||
auth_response = utils.TestResponse({
|
||||
@ -300,8 +298,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
self.assertEqual(cs.client.auth_token, token_id)
|
||||
|
||||
def test_authenticate_with_token_failure(self):
|
||||
cs = client.Client("username", None, "project_id", utils.AUTH_URL_V2,
|
||||
direct_use=False)
|
||||
cs = Client("username", None, "project_id", utils.AUTH_URL_V2)
|
||||
cs.client.auth_token = "FAKE_ID"
|
||||
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
||||
auth_response = utils.TestResponse({
|
||||
@ -317,8 +314,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
class AuthenticationTests(utils.TestCase):
|
||||
def test_authenticate_success(self):
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL, direct_use=False)
|
||||
cs = Client("username", "password", "project_id", utils.AUTH_URL)
|
||||
management_url = 'https://localhost/v1.1/443470'
|
||||
auth_response = utils.TestResponse({
|
||||
'status_code': 204,
|
||||
@ -353,8 +349,7 @@ class AuthenticationTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_authenticate_failure(self):
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL, direct_use=False)
|
||||
cs = Client("username", "password", "project_id", utils.AUTH_URL)
|
||||
auth_response = utils.TestResponse({'status_code': 401})
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
@ -365,8 +360,8 @@ class AuthenticationTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_automatic(self):
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL, direct_use=False)
|
||||
cs = Client("username", "password", "project_id", utils.AUTH_URL,
|
||||
direct_use=False)
|
||||
http_client = cs.client
|
||||
http_client.management_url = ''
|
||||
http_client.get_service_url = mock.Mock(return_value='')
|
||||
@ -382,8 +377,7 @@ class AuthenticationTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_manual(self):
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL, direct_use=False)
|
||||
cs = Client("username", "password", "project_id", utils.AUTH_URL)
|
||||
|
||||
@mock.patch.object(cs.client, 'authenticate')
|
||||
def test_auth_call(m):
|
||||
|
@ -13,20 +13,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import extension
|
||||
from novaclient import api_versions
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import cells
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
|
||||
|
||||
class CellsExtensionTests(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(CellsExtensionTests, self).setUp()
|
||||
extensions = [
|
||||
extension.Extension(cells.__name__.split(".")[-1],
|
||||
cells),
|
||||
]
|
||||
self.cs = fakes.FakeClient(extensions=extensions)
|
||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||
|
||||
def test_get_cells(self):
|
||||
cell_name = 'child_cell'
|
@ -14,6 +14,7 @@ import uuid
|
||||
|
||||
from keystoneauth1 import session
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import client
|
||||
|
||||
@ -27,6 +28,7 @@ class ClientTest(utils.TestCase):
|
||||
|
||||
s = session.Session()
|
||||
c = client.Client(session=s,
|
||||
api_version=api_versions.APIVersion("2.0"),
|
||||
user_agent=user_agent,
|
||||
endpoint_override=endpoint_override,
|
||||
direct_use=False)
|
||||
@ -40,6 +42,7 @@ class ClientTest(utils.TestCase):
|
||||
|
||||
s = session.Session()
|
||||
c = client.Client(session=s,
|
||||
api_version=api_versions.APIVersion("2.0"),
|
||||
interface=interface,
|
||||
endpoint_type=endpoint_type,
|
||||
direct_use=False)
|
||||
|
@ -13,20 +13,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import extension
|
||||
from novaclient import api_versions
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import instance_action
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
|
||||
|
||||
class InstanceActionExtensionTests(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(InstanceActionExtensionTests, self).setUp()
|
||||
extensions = [
|
||||
extension.Extension(instance_action.__name__.split(".")[-1],
|
||||
instance_action),
|
||||
]
|
||||
self.cs = fakes.FakeClient(extensions=extensions)
|
||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||
|
||||
def test_list_instance_actions(self):
|
||||
server_uuid = '1234'
|
@ -12,21 +12,14 @@
|
||||
# under the License.
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import extension
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2.contrib import list_extensions
|
||||
|
||||
|
||||
class ListExtensionsTests(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(ListExtensionsTests, self).setUp()
|
||||
extensions = [
|
||||
extension.Extension(list_extensions.__name__.split(".")[-1],
|
||||
list_extensions),
|
||||
]
|
||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.0"),
|
||||
extensions=extensions)
|
||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||
|
||||
def test_list_extensions(self):
|
||||
all_exts = self.cs.list_extensions.show_all()
|
@ -11,21 +11,15 @@
|
||||
# under the License.
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import extension
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2.contrib import migrations
|
||||
from novaclient.v2 import migrations
|
||||
|
||||
|
||||
class MigrationsTest(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(MigrationsTest, self).setUp()
|
||||
self.extensions = [
|
||||
extension.Extension(migrations.__name__.split(".")[-1],
|
||||
migrations),
|
||||
]
|
||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.0"),
|
||||
extensions=self.extensions)
|
||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||
|
||||
def test_list_migrations(self):
|
||||
ml = self.cs.migrations.list()
|
||||
@ -36,8 +30,7 @@ class MigrationsTest(utils.TestCase):
|
||||
self.assertRaises(AttributeError, getattr, m, "migration_type")
|
||||
|
||||
def test_list_migrations_v223(self):
|
||||
cs = fakes.FakeClient(extensions=self.extensions,
|
||||
api_version=api_versions.APIVersion("2.23"))
|
||||
cs = fakes.FakeClient(api_versions.APIVersion("2.23"))
|
||||
ml = cs.migrations.list()
|
||||
self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST)
|
||||
cs.assert_called('GET', '/os-migrations')
|
@ -16,20 +16,15 @@
|
||||
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.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import server_external_events as ext_events
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
|
||||
|
||||
class ServerExternalEventsTestCase(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(ServerExternalEventsTestCase, self).setUp()
|
||||
extensions = [
|
||||
extension.Extension(ext_events.__name__.split(".")[-1],
|
||||
ext_events),
|
||||
]
|
||||
self.cs = fakes.FakeClient(extensions=extensions)
|
||||
self.cs = fakes.FakeClient(api_versions.APIVersion("2.1"))
|
||||
|
||||
def test_external_event(self):
|
||||
events = [{'server_uuid': 'fake-uuid1',
|
@ -77,8 +77,7 @@ class ShellTest(utils.TestCase):
|
||||
self.shell = self.useFixture(ShellFixture()).shell
|
||||
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'novaclient.client.Client',
|
||||
lambda *args, **kwargs: fakes.FakeClient(*args, **kwargs)))
|
||||
'novaclient.client.Client', fakes.FakeClient))
|
||||
|
||||
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
||||
@mock.patch('sys.stderr', new_callable=six.StringIO)
|
||||
@ -3150,9 +3149,9 @@ class ShellWithSessionClientTest(ShellTest):
|
||||
def setUp(self):
|
||||
"""Run before each test."""
|
||||
super(ShellWithSessionClientTest, self).setUp()
|
||||
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'novaclient.client.Client',
|
||||
lambda *args, **kwargs: fakes.FakeSessionClient(*args, **kwargs)))
|
||||
'novaclient.client.Client', fakes.FakeSessionClient))
|
||||
|
||||
|
||||
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.v2 import agents
|
||||
from novaclient.v2 import aggregates
|
||||
from novaclient.v2 import assisted_volume_snapshots
|
||||
from novaclient.v2 import availability_zones
|
||||
from novaclient.v2 import cells
|
||||
from novaclient.v2 import certs
|
||||
from novaclient.v2 import cloudpipe
|
||||
from novaclient.v2 import contrib
|
||||
from novaclient.v2 import fixed_ips
|
||||
from novaclient.v2 import flavor_access
|
||||
from novaclient.v2 import flavors
|
||||
@ -36,14 +39,18 @@ from novaclient.v2 import fping
|
||||
from novaclient.v2 import hosts
|
||||
from novaclient.v2 import hypervisors
|
||||
from novaclient.v2 import images
|
||||
from novaclient.v2 import instance_action
|
||||
from novaclient.v2 import keypairs
|
||||
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 quota_classes
|
||||
from novaclient.v2 import quotas
|
||||
from novaclient.v2 import security_group_default_rules
|
||||
from novaclient.v2 import security_group_rules
|
||||
from novaclient.v2 import security_groups
|
||||
from novaclient.v2 import server_external_events
|
||||
from novaclient.v2 import server_groups
|
||||
from novaclient.v2 import server_migrations
|
||||
from novaclient.v2 import servers
|
||||
@ -172,16 +179,36 @@ class Client(object):
|
||||
self.server_migrations = \
|
||||
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...
|
||||
if 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:
|
||||
setattr(self, extension.name,
|
||||
extension.manager_class(self))
|
||||
|
||||
if not logger:
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
self.client = client._construct_http_client(
|
||||
username=username,
|
||||
password=password,
|
||||
@ -208,7 +235,7 @@ class Client(object):
|
||||
session=session,
|
||||
auth=auth,
|
||||
api_version=api_version,
|
||||
logger=logger,
|
||||
logger=self.logger,
|
||||
**kwargs)
|
||||
|
||||
@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.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from novaclient import base
|
||||
from novaclient.v2 import assisted_volume_snapshots
|
||||
from novaclient.v2 import contrib
|
||||
|
||||
|
||||
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)))
|
||||
AssistedSnapshotManager = assisted_volume_snapshots.AssistedSnapshotManager
|
||||
Snapshot = assisted_volume_snapshots.Snapshot
|
||||
|
||||
manager_class = AssistedSnapshotManager
|
||||
name = 'assisted_volume_snapshots'
|
||||
|
||||
contrib.warn()
|
||||
|
@ -13,61 +13,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import base
|
||||
from novaclient.i18n import _
|
||||
from novaclient import utils
|
||||
from novaclient.v2 import cells
|
||||
from novaclient.v2 import contrib
|
||||
|
||||
|
||||
class Cell(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<Cell: %s>" % self.name
|
||||
Cell = cells.Cell
|
||||
CellsManager = cells.CellsManager
|
||||
|
||||
|
||||
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")
|
||||
contrib.warn()
|
||||
|
@ -12,16 +12,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from novaclient import utils
|
||||
from novaclient.v2 import contrib
|
||||
|
||||
|
||||
@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()
|
||||
contrib.warn(alternative=False)
|
||||
|
@ -13,73 +13,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import base
|
||||
from novaclient.i18n import _
|
||||
from novaclient import utils
|
||||
from novaclient.v2 import contrib
|
||||
from novaclient.v2 import shell
|
||||
|
||||
|
||||
class EvacuateHostResponse(base.Resource):
|
||||
pass
|
||||
EvacuateHostResponse = shell.EvacuateHostResponse
|
||||
|
||||
|
||||
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"])
|
||||
contrib.warn(alternative=False)
|
||||
|
@ -13,87 +13,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.i18n import _
|
||||
from novaclient import utils
|
||||
from novaclient.v2 import contrib
|
||||
|
||||
|
||||
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"])
|
||||
contrib.warn(alternative=False)
|
||||
|
@ -13,40 +13,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import base
|
||||
from novaclient.i18n import _
|
||||
from novaclient import utils
|
||||
from novaclient.v2 import contrib
|
||||
from novaclient.v2 import shell
|
||||
|
||||
|
||||
class HostServersMigrateResponse(base.Resource):
|
||||
pass
|
||||
HostServersMigrateResponse = shell.HostServersMigrateResponse
|
||||
|
||||
|
||||
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"])
|
||||
contrib.warn(alternative=False)
|
||||
|
@ -13,81 +13,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import pprint
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import base
|
||||
from novaclient.i18n import _
|
||||
from novaclient import utils
|
||||
from novaclient.v2 import shell
|
||||
from novaclient.v2 import contrib
|
||||
from novaclient.v2 import instance_action
|
||||
|
||||
|
||||
class InstanceActionManager(base.ManagerWithFind):
|
||||
resource_class = base.Resource
|
||||
InstanceActionManager = instance_action.InstanceActionManager
|
||||
|
||||
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')
|
||||
|
||||
|
||||
@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)
|
||||
contrib.warn()
|
||||
|
@ -13,34 +13,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import base
|
||||
from novaclient import utils
|
||||
from novaclient.v2 import contrib
|
||||
from novaclient.v2 import list_extensions
|
||||
|
||||
|
||||
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] + "..."
|
||||
ListExtResource = list_extensions.ListExtResource
|
||||
ListExtManager = list_extensions.ListExtResource
|
||||
|
||||
|
||||
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)
|
||||
contrib.warn()
|
||||
|
@ -13,35 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.i18n import _
|
||||
from novaclient import utils
|
||||
from novaclient.v2 import shell
|
||||
from novaclient.v2 import contrib
|
||||
|
||||
|
||||
@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 = 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())
|
||||
contrib.warn(alternative=False)
|
||||
|
@ -14,86 +14,11 @@
|
||||
migration interface
|
||||
"""
|
||||
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import base
|
||||
from novaclient.i18n import _
|
||||
from novaclient import utils
|
||||
from novaclient.v2 import contrib
|
||||
from novaclient.v2 import migrations
|
||||
|
||||
|
||||
class Migration(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<Migration: %s>" % self.id
|
||||
Migration = migrations.Migration
|
||||
MigrationManager = migrations.MigrationManager
|
||||
|
||||
|
||||
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)
|
||||
contrib.warn()
|
||||
|
@ -16,28 +16,14 @@
|
||||
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):
|
||||
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)
|
||||
|
||||
Event = server_external_events.Event
|
||||
ServerExternalEventManager = server_external_events.ServerExternalEventManager
|
||||
|
||||
manager_class = ServerExternalEventManager
|
||||
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 logging
|
||||
import os
|
||||
import pprint
|
||||
import sys
|
||||
import time
|
||||
|
||||
@ -5185,3 +5186,359 @@ def do_server_tag_delete_all(cs, args):
|
||||
"""Delete all tags from a server."""
|
||||
server = _find_server(cs, args.server)
|
||||
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