Статьи

ОТДЫХАТЬ или НЕ ОТДЫХАТЬ

Примечание куратора. Содержание этой статьи изначально было написано Себастьеном Гоасгуеном в   блоге Build a Cloud  .

Я хотел бы написать, как Шекспир, но так как я не оставляю вас в этом блоге о репрезентативной передаче состояния (REST) ​​и, в частности, дискуссии о том, является ли API-интерфейс CloudStack REST API или нет. Краткий ответ: API-интерфейс CloudStack похож на REST, но не на RESTfull, поскольку он основан только на методе GET. API на основе http, который может возвращать JSON, не делает его API RESTfull. Это следует рассматривать не как негативную критику, а как разъяснение.

Сначала несколько слов об API CloudStack и несколько указателей. Она очень обширна : от создания пользователей, управления виртуальными машинами до настройки брандмауэра и более сложных сетевых функций. У Github много клиентов, написанных сообществом, и я уверен, что там вы найдете свой любимый язык. Руководство разработчика подробно объясняет, как делать запросы. С CloudMonkey взаимодействие с API стало еще проще. Это потрясающий способ узнать API и проверить необходимые параметры каждого вызова. В сочетании с devcloud вы можете иметь полностью функциональный локальный испытательный стенд CloudStack.

Чтобы изучить API, я также часто смотрю на GUI и вижу, как выполняются вызовы API. Для этого я получаю доступ к пользовательскому интерфейсу в Firefox и запускаю консоль Firebug. Затем я могу увидеть вызовы, которые меня интересуют, и, в частности, проверить параметры / заголовки и т. Д. Или сделать HTTP-вызов.

REST известен как архитектурный стиль, и, говоря по-французски, это по сути способ спроектировать ваш API для создания веб-службы, которая использует только методы HTTP для управления состоянием веб-ресурсов. По моему мнению, это был действительно ответ на сложность веб-сервисов, основанных на SOAP, и появившихся многочисленных стандартов. REST действительно развился с Web 2.0 и рассматривался как способ создания простых в использовании веб-сервисов. Недавно я нашел несколько статей Луиса Рей, в которых четко объясняется, как создавать веб-службы REST, а также реализовывать с помощью Flask облегченную веб-среду на основе Python .

Внимательно изучая API CloudStack, мы видим, что он использует http, может возвращать объекты JSON, но использует только HTTP-метод GET. Таким образом, это действительно API запросов, который мы можем вызывать RESTLike, но не RESTfull, аналогично API-интерфейсу Amazon AWS EC2 Query.

Чтобы проиллюстрировать это, я решил написать оболочку REST поверх API CloudStack. Это было действительно причиной моего CloudStack Silly Tuesday Hack . Фактическая полезность этого все еще сомнительна ? было бы лучше переписать API CloudStack в собственный REST API. При этом «облачные оболочки», такие как jclouds / deltacloud, заняли позицию, в которой они представляют API поверх существующих API IaaS. Так что, кто знает, это может быть полезно.

Сначала я хотел иметь возможность совершать http-вызовы через curl и использовать API-интерфейс CloudStack на основе GET по умолчанию, затем я хотел сделать его более RESTFull. Я решил использовать Flask, потому что он имеет чистый способ определения веб-маршрутов и указания используемого метода HTTP. Чтобы сделать все это, я использовал DevCloud, как упоминалось выше, и проверял все свои звонки с помощью CloudMonkey. Для того, чтобы простые вызовы GET через завиток и избегать построения подписи звонков, я взял requester.py файл из CloudMonkey и импортировать его в моем приложении Колбы, проверка в сути для всего кода. Ниже приведен пример «маршрута». В этом примере мы рассматриваем только управление пользователями.

@app.route('/list')
def list():
    print request.query_string
    res={}
    for key in request.args.iterkeys():
        res[key]=request.args.get(key)
    print res
    response, error = requester.make_request('listUsers',res,None,host,port,apikey,secretkey,protocol,path)
    return response

Http GET для http: // localhost: 5000 / list будет перенаправлен в функцию list (). Параметры запроса будут проанализированы и сохранены в словаре, который в свою очередь будет передан listUsers из CloudStack. Вот где лежит глупость. ПОЛУЧИТЬ, что делает ПОЛУЧИТЬ. Используя curl, мы можем легко вызвать этот метод:

curl -X GET -G 'http://localhost:5000/list'
{ "listusersresponse" : { "count":4 ,"user" : [  {"id":"7ed6d5da-93b2-4545-a502-23d20b48ef2a","username":"admin","firstname":"admin","lastname":"cloud","created":"2012-07-05T12:18:27-0700","state":"enabled","account":"admin","accounttype":1,"domainid":"8a111e58-e155-4482-93ce-84efff3c7c77","domain":"ROOT","apikey":"plgWJfZK4gyS3mOMTVmjUVg-X-jlWlnfaUJ9GAbBbf9EdM-kAYMmAiLqzzq1ElZLYq_u38zCm0bewzGUdP66mg","secretkey":"VDaACYb0LV9eNjTetIOElcVQkvJck_J_QljX_FcHRj87ZKiy0z0ty0ZsYBkoXkY9b7eq1EhwJaw7FF3akA3KBQ","accountid":"7548ac03-af1d-4c1c-9064-2f3e2c0eda0d"}, {"id":"1fea6418-5576-4989-a21e-4790787bbee3","username":"runseb","firstname":"sebgoa","lastname":"goa","email":"
 [email protected]","created":"2013-04-10T16:52:06-0700","state":"enabled","account":"admin","accounttype":1,"domainid":"8a111e58-e155-4482-93ce-84efff3c7c77","domain":"ROOT","apikey":"Xhsb3MewjJQaXXMszRcLvQI9_NPy_UcbDj1QXikkVbDC9MDSPwWdtZ1bUY1H7JBEYTtDDLY3yuchCeW778GkBA","secretkey":"gIsgmi8C5YwxMHjX5o51pSe0kqs6JnKriw0jJBLceY5bgnfzKjL4aM6ctJX-i1ddQIHJLbLJDK9MRzsKk6xZ_w","accountid":"7548ac03-af1d-4c1c-9064-2f3e2c0eda0d"}, {"id":"b3b60a8d-df6f-4ce6-a6f9-6194907457a5","username":"john","firstname":"sebgoa","lastname":"goa","email":"
 [email protected]","created":"2013-04-12T05:09:10-0700","state":"enabled","account":"admin","accounttype":1,"domainid":"8a111e58-e155-4482-93ce-84efff3c7c77","domain":"ROOT","accountid":"7548ac03-af1d-4c1c-9064-2f3e2c0eda0d"}, {"id":"62d866e4-da97-46c2-a3e1-10faf6197c73","username":"titi","firstname":"www","lastname":"rr","email":"
 [email protected]","created":"2013-04-15T06:21:59-0700","state":"enabled","account":"admin","accounttype":1,"domainid":"8a111e58-e155-4482-93ce-84efff3c7c77","domain":"ROOT","accountid":"7548ac03-af1d-4c1c-9064-2f3e2c0eda0d"} ] } }

В ответе мы видим список всех пользователей системы. Тот же маршрут списка также будет работать, если мы передадим идентификатор одного пользователя:

curl -X GET -G 'http://localhost:5000/list' -d id=7ed6d5da-93b2-4545-a502-23d20b48ef2a
{ "listusersresponse" : { "count":1 ,"user" : [  {"id":"7ed6d5da-93b2-4545-a502-23d20b48ef2a","username":"admin","firstname":"admin","lastname":"cloud","created":"2012-07-05T12:18:27-0700","state":"enabled","account":"admin","accounttype":1,"domainid":"8a111e58-e155-4482-93ce-84efff3c7c77","domain":"ROOT","apikey":"plgWJfZK4gyS3mOMTVmjUVg-X-jlWlnfaUJ9GAbBbf9EdM-kAYMmAiLqzzq1ElZLYq_u38zCm0bewzGUdP66mg","secretkey":"VDaACYb0LV9eNjTetIOElcVQkvJck_J_QljX_FcHRj87ZKiy0z0ty0ZsYBkoXkY9b7eq1EhwJaw7FF3akA3KBQ","accountid":"7548ac03-af1d-4c1c-9064-2f3e2c0eda0d"} ] } }

Интересно, что при удалении пользователя будет использоваться тот же код, но другой вызов API:

@app.route('/delete')
def delete():
    print request.query_string
    res={}
    for key in request.args.iterkeys():
        res[key]=request.args.get(key)
    print res
    response, error = requester.make_request('deleteUser',res,None,host,port,apikey,secretkey,protocol,path)
    return response

Вы бы назвали это, чтобы удалить пользователя, передавая идентификатор пользователя следующим образом:

curl -X GET -G 'http://localhost:5000/delete' -d id=62d866e4-da97-46c2-a3e1-10faf6197c73
{ "deleteuserresponse" : { "success" : "true"}  }

Вы удаляете ресурс, и все же это все еще вызов GET. Почему бы не использовать вызов DELETE для удаления ресурса? Это было бы более интуитивным и хорошим использованием грамматики HTTP. Та же самая ситуация произойдет для обновления пользователя или создания пользователя, все еще GET, в то время как мы могли бы использовать POST и PATCH. В CloudStack-Flask я добавил несколько маршрутов для создания правильного сервиса REST. Например, маршрут становится:

@app.route('/user/', methods=['GET','DELETE','PATCH'])
def user(uuid):
    if request.method =='GET':
        response, error = requester.make_request('listUsers',{'id':uuid},None,host,port,apikey,secretkey,protocol,path)
        return response
    elif request.method =='PATCH':
        data = request.json
        data['id']=uuid
        response, error = requester.make_request('updateUser',data,None,host,port,apikey,secretkey,protocol,path)
        return response
    else:
        response, error = requester.make_request('deleteUser',{'id':uuid},None,host,port,apikey,secretkey,protocol,path)
        return response

Идентификатор пользователя указывается в URL. Пользователь — это реальный веб-ресурс, состояние которого мы меняем. И метод HTTP используется для определения типа действия. В нашем случае GET перечисляет информацию о пользователе, PATCH обновляет параметры пользователя, а DELETE удаляет пользователя. Идентификатор теперь является частью URI, а не параметрами запроса.

curl -X GET http://localhost:5000/user/b3b60a8d-df6f-4ce6-a6f9-6194907457a5
{ "listusersresponse" : { "count":1 ,"user" : [  {"id":"b3b60a8d-df6f-4ce6-a6f9-6194907457a5","username":"john","firstname":"sebgoa","lastname":"goa","email":"
 [email protected]","created":"2013-04-12T05:09:10-0700","state":"enabled","account":"admin","accounttype":1,"domainid":"8a111e58-e155-4482-93ce-84efff3c7c77","domain":"ROOT","accountid":"7548ac03-af1d-4c1c-9064-2f3e2c0eda0d"} ] } }

curl -X  DELETE http://localhost:5000/user/b3b60a8d-df6f-4ce6-a6f9-6194907457a5
{ "deleteuserresponse" : { "success" : "true"}  }

curl -X  PATCH -H "Content-Type: application/json" http://localhost:5000/user/1fea6418-5576-4989-a21e-4790787bbee3 -d '{"firstname":"foobar"}'
{ "updateuserresponse" :  { "user" : {"id":"1fea6418-5576-4989-a21e-4790787bbee3","username":"runseb","firstname":"foobar","lastname":"goa","email":"
 [email protected]","created":"2013-04-10T16:52:06-0700","state":"enabled","account":"admin","accounttype":1,"domainid":"8a111e58-e155-4482-93ce-84efff3c7c77","domain":"ROOT","apikey":"Xhsb3MewjJQaXXMszRcLvQI9_NPy_UcbDj1QXikkVbDC9MDSPwWdtZ1bUY1H7JBEYTtDDLY3yuchCeW778GkBA","secretkey":"gIsgmi8C5YwxMHjX5o51pSe0kqs6JnKriw0jJBLceY5bgnfzKjL4aM6ctJX-i1ddQIHJLbLJDK9MRzsKk6xZ_w","accountid":"7548ac03-af1d-4c1c-9064-2f3e2c0eda0d"} }  }

Определите свои ресурсы / сущности и используйте грамматику HTTP, чтобы изменить их состояние. Не забудьте проверить мое приложение cloudtack-flask , оно очень простое прямо сейчас, но может быть хорошей оболочкой REST, удвоенной с интерфейсом на основе загрузчика, мы поговорим о веб-представлениях в следующем посте.